blob: 8795cb1771c01eb4212bc3c1b44f880d174f09a9 [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Code to save the iptables state, in human readable-form. */
Harald Welteae1ff9f2000-12-01 14:26:20 +00002/* Authors: Paul 'Rusty' Russel <rusty@linuxcare.com.au> and
3 * Harald Welte <laforge@gnumonks.org>
4 */
Marc Bouchere6869a82000-03-20 06:03:29 +00005#include <getopt.h>
6#include <sys/errno.h>
7#include <stdio.h>
Harald Welteae1ff9f2000-12-01 14:26:20 +00008#include <fcntl.h>
9#include <stdlib.h>
10#include <string.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000011#include <dlfcn.h>
12#include <time.h>
Rusty Russellb1f69be2000-08-27 07:42:54 +000013#include "libiptc/libiptc.h"
14#include "iptables.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000015
Rusty Russella8f033e2000-07-30 01:43:01 +000016static int binary = 0, counters = 0;
17
Marc Bouchere6869a82000-03-20 06:03:29 +000018static struct option options[] = {
Rusty Russella8f033e2000-07-30 01:43:01 +000019 { "binary", 0, 0, 'b' },
20 { "counters", 0, 0, 'c' },
21 { "dump", 0, 0, 'd' },
22 { "table", 1, 0, 't' },
Marc Bouchere6869a82000-03-20 06:03:29 +000023 { 0 }
24};
25
26#define IP_PARTS_NATIVE(n) \
27(unsigned int)((n)>>24)&0xFF, \
28(unsigned int)((n)>>16)&0xFF, \
29(unsigned int)((n)>>8)&0xFF, \
30(unsigned int)((n)&0xFF)
31
32#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
33
34/* This assumes that mask is contiguous, and byte-bounded. */
35static void
36print_iface(char letter, const char *iface, const unsigned char *mask,
37 int invert)
38{
39 unsigned int i;
40
41 if (mask[0] == 0)
42 return;
Rusty Russell7e53bf92000-03-20 07:03:28 +000043
Marc Bouchere6869a82000-03-20 06:03:29 +000044 printf("-%c %s", letter, invert ? "! " : "");
45
46 for (i = 0; i < IFNAMSIZ; i++) {
47 if (mask[i] != 0) {
48 if (iface[i] != '\0')
49 printf("%c", iface[i]);
50 } else {
51 if (iface[i] != '\0')
52 printf("+");
53 break;
54 }
55 }
Harald Welteae1ff9f2000-12-01 14:26:20 +000056
57 printf(" ");
Marc Bouchere6869a82000-03-20 06:03:29 +000058}
59
60/* These are hardcoded backups in iptables.c, so they are safe */
61struct pprot {
62 char *name;
63 u_int8_t num;
64};
65
Harald Welteae1ff9f2000-12-01 14:26:20 +000066/* FIXME: why don't we use /etc/services ? */
Marc Bouchere6869a82000-03-20 06:03:29 +000067static const struct pprot chain_protos[] = {
68 { "tcp", IPPROTO_TCP },
69 { "udp", IPPROTO_UDP },
70 { "icmp", IPPROTO_ICMP },
71};
72
73static void print_proto(u_int16_t proto, int invert)
74{
75 if (proto) {
76 unsigned int i;
77 const char *invertstr = invert ? "! " : "";
78
79 for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
80 if (chain_protos[i].num == proto) {
81 printf("-p %s%s ",
82 invertstr, chain_protos[i].name);
83 return;
84 }
85
86 printf("-p %s%u ", invertstr, proto);
87 }
88}
89
Harald Welteae1ff9f2000-12-01 14:26:20 +000090#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +000091static int non_zero(const void *ptr, size_t size)
92{
93 unsigned int i;
94
95 for (i = 0; i < size; i++)
96 if (((char *)ptr)[i])
97 return 0;
98
99 return 1;
100}
Harald Welteae1ff9f2000-12-01 14:26:20 +0000101#endif
102
Harald Welte32db5242001-01-23 22:46:22 +0000103static int print_match(const struct ipt_entry_match *e,
104 const struct ipt_ip *ip)
Harald Welteae1ff9f2000-12-01 14:26:20 +0000105{
106 struct iptables_match *match
107 = find_match(e->u.user.name, TRY_LOAD);
108
109 if (match) {
110 printf("-m %s ", e->u.user.name);
Harald Welte32db5242001-01-23 22:46:22 +0000111 match->save(ip, e);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000112 } else {
113 if (e->u.match_size) {
114 fprintf(stderr,
115 "Can't find library for match `%s'\n",
116 e->u.user.name);
117 exit(1);
118 }
119 }
120 return 0;
121}
122
123/* print a given ip including mask if neccessary */
124static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
125{
126 if (!mask && !ip)
127 return;
128
129 printf("%s %s%u.%u.%u.%u",
130 prefix,
131 invert ? "! " : "",
132 IP_PARTS(ip));
133
134 if (mask != 0xffffffff)
135 printf("/%u.%u.%u.%u ", IP_PARTS(mask));
136 else
137 printf(" ");
138}
Marc Bouchere6869a82000-03-20 06:03:29 +0000139
140/* We want this to be readable, so only print out neccessary fields.
141 * Because that's the kind of world I want to live in. */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000142static void print_rule(const struct ipt_entry *e,
143 iptc_handle_t *h, int counters)
Marc Bouchere6869a82000-03-20 06:03:29 +0000144{
Harald Welteae1ff9f2000-12-01 14:26:20 +0000145 struct ipt_entry_target *t;
146
147 /* print counters */
Marc Bouchere6869a82000-03-20 06:03:29 +0000148 if (counters)
Harald Welted8e65632001-01-05 15:20:07 +0000149 printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
Marc Bouchere6869a82000-03-20 06:03:29 +0000150
151 /* Print IP part. */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000152 print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
153 e->ip.invflags & IPT_INV_SRCIP);
154
155 print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
156 e->ip.invflags & IPT_INV_SRCIP);
Marc Bouchere6869a82000-03-20 06:03:29 +0000157
158 print_iface('i', e->ip.iniface, e->ip.iniface_mask,
159 e->ip.invflags & IPT_INV_VIA_IN);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000160
Marc Bouchere6869a82000-03-20 06:03:29 +0000161 print_iface('o', e->ip.outiface, e->ip.outiface_mask,
162 e->ip.invflags & IPT_INV_VIA_OUT);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000163
Marc Bouchere6869a82000-03-20 06:03:29 +0000164 print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
165
166 if (e->ip.flags & IPT_F_FRAG)
167 printf("%s-f ",
168 e->ip.invflags & IPT_INV_FRAG ? "! " : "");
169
Marc Bouchere6869a82000-03-20 06:03:29 +0000170 /* Print matchinfo part */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000171 if (e->target_offset) {
Harald Welte32db5242001-01-23 22:46:22 +0000172 IPT_MATCH_ITERATE(e, print_match, &e->ip);
Marc Bouchere6869a82000-03-20 06:03:29 +0000173 }
174
Harald Welteae1ff9f2000-12-01 14:26:20 +0000175 /* Print target name */
176 printf("-j %s ", iptc_get_target(e, h));
177
Marc Bouchere6869a82000-03-20 06:03:29 +0000178 /* Print targinfo part */
Rusty Russell082ba022001-01-07 06:54:51 +0000179 t = ipt_get_target((struct ipt_entry *)e);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000180 if (t->u.user.name[0]) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000181 struct iptables_target *target
Harald Welteae1ff9f2000-12-01 14:26:20 +0000182 = find_target(t->u.user.name, TRY_LOAD);
Marc Bouchere6869a82000-03-20 06:03:29 +0000183
184 if (target)
Harald Welte32db5242001-01-23 22:46:22 +0000185 target->save(&e->ip, t);
Marc Bouchere6869a82000-03-20 06:03:29 +0000186 else {
187 /* If some bits are non-zero, it implies we *need*
188 to understand it */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000189 if (t->u.target_size) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000190 fprintf(stderr,
191 "Can't find library for target `%s'\n",
Harald Welteae1ff9f2000-12-01 14:26:20 +0000192 t->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000193 exit(1);
194 }
195 }
196 }
197 printf("\n");
198}
199
200/* Debugging prototype. */
Rusty Russella8f033e2000-07-30 01:43:01 +0000201static int for_each_table(int (*func)(const char *tablename))
202{
203 int ret = 1;
Harald Welteae1ff9f2000-12-01 14:26:20 +0000204 FILE *procfile = NULL;
Rusty Russella8f033e2000-07-30 01:43:01 +0000205 char tablename[IPT_TABLE_MAXNAMELEN+1];
206
Harald Welteae1ff9f2000-12-01 14:26:20 +0000207 procfile = fopen("/proc/net/ip_tables_names", "r");
Rusty Russella8f033e2000-07-30 01:43:01 +0000208 if (!procfile)
209 return 0;
210
211 while (fgets(tablename, sizeof(tablename), procfile)) {
212 if (tablename[strlen(tablename) - 1] != '\n')
213 exit_error(OTHER_PROBLEM,
214 "Badly formed tablename `%s'\n",
215 tablename);
216 tablename[strlen(tablename) - 1] = '\0';
217 ret &= func(tablename);
218 }
219
220 return ret;
221}
222
223
Rusty Russella8f033e2000-07-30 01:43:01 +0000224static int do_output(const char *tablename)
225{
226 iptc_handle_t h;
227 const char *chain = NULL;
228
229 if (!tablename)
230 return for_each_table(&do_output);
231
232 h = iptc_init(tablename);
233 if (!h)
234 exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
235 iptc_strerror(errno));
236
237 if (!binary) {
238 time_t now = time(NULL);
239
240 printf("# Generated by iptables-save v%s on %s",
241 NETFILTER_VERSION, ctime(&now));
Harald Welteae1ff9f2000-12-01 14:26:20 +0000242 printf("*%s\n", tablename);
Rusty Russella8f033e2000-07-30 01:43:01 +0000243
244 /* Dump out chain names */
245 for (chain = iptc_first_chain(&h);
246 chain;
247 chain = iptc_next_chain(&h)) {
Harald Welteae1ff9f2000-12-01 14:26:20 +0000248 const struct ipt_entry *e;
249
Rusty Russella8f033e2000-07-30 01:43:01 +0000250 printf(":%s ", chain);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000251 if (iptc_builtin(chain, h)) {
Rusty Russella8f033e2000-07-30 01:43:01 +0000252 struct ipt_counters count;
253 printf("%s ",
254 iptc_get_policy(chain, &count, &h));
Harald Welted8e65632001-01-05 15:20:07 +0000255 printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
Rusty Russella8f033e2000-07-30 01:43:01 +0000256 } else {
Harald Welted8e65632001-01-05 15:20:07 +0000257 printf("- [0:0]\n");
Rusty Russella8f033e2000-07-30 01:43:01 +0000258 }
Rusty Russella8f033e2000-07-30 01:43:01 +0000259
Harald Welteae1ff9f2000-12-01 14:26:20 +0000260 /* Dump out rules */
261 e = iptc_first_rule(chain, &h);
262 while(e) {
263 print_rule(e, &h, counters);
264 e = iptc_next_rule(e, &h);
Rusty Russella8f033e2000-07-30 01:43:01 +0000265 }
266 }
267
268 now = time(NULL);
269 printf("COMMIT\n");
270 printf("# Completed on %s", ctime(&now));
271 } else {
272 /* Binary, huh? OK. */
273 exit_error(OTHER_PROBLEM, "Binary NYI\n");
274 }
275
276 return 1;
277}
278
Marc Bouchere6869a82000-03-20 06:03:29 +0000279/* Format:
280 * :Chain name POLICY packets bytes
281 * rule
282 */
283int main(int argc, char *argv[])
284{
Rusty Russella8f033e2000-07-30 01:43:01 +0000285 const char *tablename = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000286 int c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000287
288 program_name = "iptables-save";
289 program_version = NETFILTER_VERSION;
290
291 while ((c = getopt_long(argc, argv, "bc", options, NULL)) != -1) {
292 switch (c) {
293 case 'b':
294 binary = 1;
295 break;
296
297 case 'c':
298 counters = 1;
299 break;
300
Rusty Russella8f033e2000-07-30 01:43:01 +0000301 case 't':
302 /* Select specific table. */
303 tablename = optarg;
304 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000305 case 'd':
Harald Welteae1ff9f2000-12-01 14:26:20 +0000306 do_output(tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000307 exit(0);
308 }
309 }
310
311 if (optind < argc) {
312 fprintf(stderr, "Unknown arguments found on commandline");
313 exit(1);
314 }
315
Rusty Russella8f033e2000-07-30 01:43:01 +0000316 return !do_output(tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000317}