90163b501aa1c0e67e5015200879591dcd7757cf
[iptables.git] / iptables-save.c
1 /* Code to save the iptables state, in human readable-form. */
2 /* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
3  * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
4  *
5  * This code is distributed under the terms of GNU GPL v2
6  *
7  */
8 #include <getopt.h>
9 #include <sys/errno.h>
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <dlfcn.h>
15 #include <time.h>
16 #include "libiptc/libiptc.h"
17 #include "iptables.h"
18
19 static int binary = 0, counters = 0;
20
21 static struct option options[] = {
22         { "binary", 0, 0, 'b' },
23         { "counters", 0, 0, 'c' },
24         { "dump", 0, 0, 'd' },
25         { "table", 1, 0, 't' },
26         { 0 }
27 };
28
29 #define IP_PARTS_NATIVE(n)                      \
30 (unsigned int)((n)>>24)&0xFF,                   \
31 (unsigned int)((n)>>16)&0xFF,                   \
32 (unsigned int)((n)>>8)&0xFF,                    \
33 (unsigned int)((n)&0xFF)
34
35 #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
36
37 /* This assumes that mask is contiguous, and byte-bounded. */
38 static void
39 print_iface(char letter, const char *iface, const unsigned char *mask,
40             int invert)
41 {
42         unsigned int i;
43
44         if (mask[0] == 0)
45                 return;
46
47         printf("-%c %s", letter, invert ? "! " : "");
48
49         for (i = 0; i < IFNAMSIZ; i++) {
50                 if (mask[i] != 0) {
51                         if (iface[i] != '\0')
52                                 printf("%c", iface[i]);
53                 } else {
54                         /* we can access iface[i-1] here, because 
55                          * a few lines above we make sure that mask[0] != 0 */
56                         if (iface[i-1] != '\0')
57                                 printf("+");
58                         break;
59                 }
60         }
61
62         printf(" ");
63 }
64
65 /* These are hardcoded backups in iptables.c, so they are safe */
66 struct pprot {
67         char *name;
68         u_int8_t num;
69 };
70
71 /* FIXME: why don't we use /etc/protocols ? */
72 static const struct pprot chain_protos[] = {
73         { "tcp", IPPROTO_TCP },
74         { "udp", IPPROTO_UDP },
75         { "icmp", IPPROTO_ICMP },
76         { "esp", IPPROTO_ESP },
77         { "ah", IPPROTO_AH },
78 };
79
80 static void print_proto(u_int16_t proto, int invert)
81 {
82         if (proto) {
83                 unsigned int i;
84                 const char *invertstr = invert ? "! " : "";
85
86                 for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
87                         if (chain_protos[i].num == proto) {
88                                 printf("-p %s%s ",
89                                        invertstr, chain_protos[i].name);
90                                 return;
91                         }
92
93                 printf("-p %s%u ", invertstr, proto);
94         }
95 }
96
97 #if 0
98 static int non_zero(const void *ptr, size_t size)
99 {
100         unsigned int i;
101
102         for (i = 0; i < size; i++)
103                 if (((char *)ptr)[i])
104                         return 0;
105
106         return 1;
107 }
108 #endif
109
110 static int print_match(const struct ipt_entry_match *e,
111                         const struct ipt_ip *ip)
112 {
113         struct iptables_match *match
114                 = find_match(e->u.user.name, TRY_LOAD);
115
116         if (match) {
117                 printf("-m %s ", e->u.user.name);
118
119                 /* some matches don't provide a save function */
120                 if (match->save)
121                         match->save(ip, e);
122         } else {
123                 if (e->u.match_size) {
124                         fprintf(stderr,
125                                 "Can't find library for match `%s'\n",
126                                 e->u.user.name);
127                         exit(1);
128                 }
129         }
130         return 0;
131 }
132
133 /* print a given ip including mask if neccessary */
134 static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
135 {
136         if (!mask && !ip)
137                 return;
138
139         printf("%s %s%u.%u.%u.%u",
140                 prefix,
141                 invert ? "! " : "",
142                 IP_PARTS(ip));
143
144         if (mask != 0xffffffff) 
145                 printf("/%u.%u.%u.%u ", IP_PARTS(mask));
146         else
147                 printf(" ");
148 }
149
150 /* We want this to be readable, so only print out neccessary fields.
151  * Because that's the kind of world I want to live in.  */
152 static void print_rule(const struct ipt_entry *e, 
153                 iptc_handle_t *h, const char *chain, int counters)
154 {
155         struct ipt_entry_target *t;
156         const char *target_name;
157
158         /* print counters */
159         if (counters)
160                 printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
161
162         /* print chain name */
163         printf("-A %s ", chain);
164
165         /* Print IP part. */
166         print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
167                         e->ip.invflags & IPT_INV_SRCIP);        
168
169         print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
170                         e->ip.invflags & IPT_INV_DSTIP);
171
172         print_iface('i', e->ip.iniface, e->ip.iniface_mask,
173                     e->ip.invflags & IPT_INV_VIA_IN);
174
175         print_iface('o', e->ip.outiface, e->ip.outiface_mask,
176                     e->ip.invflags & IPT_INV_VIA_OUT);
177
178         print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
179
180         if (e->ip.flags & IPT_F_FRAG)
181                 printf("%s-f ",
182                        e->ip.invflags & IPT_INV_FRAG ? "! " : "");
183
184         /* Print matchinfo part */
185         if (e->target_offset) {
186                 IPT_MATCH_ITERATE(e, print_match, &e->ip);
187         }
188
189         /* Print target name */ 
190         target_name = iptc_get_target(e, h);
191         if (target_name && (*target_name != '\0'))
192                 printf("-j %s ", target_name);
193
194         /* Print targinfo part */
195         t = ipt_get_target((struct ipt_entry *)e);
196         if (t->u.user.name[0]) {
197                 struct iptables_target *target
198                         = find_target(t->u.user.name, TRY_LOAD);
199
200                 if (!target) {
201                         fprintf(stderr, "Can't find library for target `%s'\n",
202                                 t->u.user.name);
203                         exit(1);
204                 }
205
206                 if (target->save)
207                         target->save(&e->ip, t);
208                 else {
209                         /* If the target size is greater than ipt_entry_target
210                          * there is something to be saved, we just don't know
211                          * how to print it */
212                         if (t->u.target_size != 
213                             sizeof(struct ipt_entry_target)) {
214                                 fprintf(stderr, "Target `%s' is missing "
215                                                 "save function\n",
216                                         t->u.user.name);
217                                 exit(1);
218                         }
219                 }
220         }
221         printf("\n");
222 }
223
224 /* Debugging prototype. */
225 static int for_each_table(int (*func)(const char *tablename))
226 {
227         int ret = 1;
228         FILE *procfile = NULL;
229         char tablename[IPT_TABLE_MAXNAMELEN+1];
230
231         procfile = fopen("/proc/net/ip_tables_names", "r");
232         if (!procfile)
233                 return 0;
234
235         while (fgets(tablename, sizeof(tablename), procfile)) {
236                 if (tablename[strlen(tablename) - 1] != '\n')
237                         exit_error(OTHER_PROBLEM, 
238                                    "Badly formed tablename `%s'\n",
239                                    tablename);
240                 tablename[strlen(tablename) - 1] = '\0';
241                 ret &= func(tablename);
242         }
243
244         return ret;
245 }
246         
247
248 static int do_output(const char *tablename)
249 {
250         iptc_handle_t h;
251         const char *chain = NULL;
252
253         if (!tablename)
254                 return for_each_table(&do_output);
255
256         h = iptc_init(tablename);
257         if (!h)
258                 exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
259                            iptc_strerror(errno));
260
261         if (!binary) {
262                 time_t now = time(NULL);
263
264                 printf("# Generated by iptables-save v%s on %s",
265                        IPTABLES_VERSION, ctime(&now));
266                 printf("*%s\n", tablename);
267
268                 /* Dump out chain names first, 
269                  * thereby preventing dependency conflicts */
270                 for (chain = iptc_first_chain(&h);
271                      chain;
272                      chain = iptc_next_chain(&h)) {
273                         
274                         printf(":%s ", chain);
275                         if (iptc_builtin(chain, h)) {
276                                 struct ipt_counters count;
277                                 printf("%s ",
278                                        iptc_get_policy(chain, &count, &h));
279                                 printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
280                         } else {
281                                 printf("- [0:0]\n");
282                         }
283                 }
284                                 
285
286                 for (chain = iptc_first_chain(&h);
287                      chain;
288                      chain = iptc_next_chain(&h)) {
289                         const struct ipt_entry *e;
290
291                         /* Dump out rules */
292                         e = iptc_first_rule(chain, &h);
293                         while(e) {
294                                 print_rule(e, &h, chain, counters);
295                                 e = iptc_next_rule(e, &h);
296                         }
297                 }
298
299                 now = time(NULL);
300                 printf("COMMIT\n");
301                 printf("# Completed on %s", ctime(&now));
302         } else {
303                 /* Binary, huh?  OK. */
304                 exit_error(OTHER_PROBLEM, "Binary NYI\n");
305         }
306
307         iptc_free(&h);
308
309         return 1;
310 }
311
312 /* Format:
313  * :Chain name POLICY packets bytes
314  * rule
315  */
316 int main(int argc, char *argv[])
317 {
318         const char *tablename = NULL;
319         int c;
320
321         program_name = "iptables-save";
322         program_version = IPTABLES_VERSION;
323
324 #ifdef NO_SHARED_LIBS
325         init_extensions();
326 #endif
327
328         while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
329                 switch (c) {
330                 case 'b':
331                         binary = 1;
332                         break;
333
334                 case 'c':
335                         counters = 1;
336                         break;
337
338                 case 't':
339                         /* Select specific table. */
340                         tablename = optarg;
341                         break;
342                 case 'd':
343                         do_output(tablename);
344                         exit(0);
345                 }
346         }
347
348         if (optind < argc) {
349                 fprintf(stderr, "Unknown arguments found on commandline");
350                 exit(1);
351         }
352
353         return !do_output(tablename);
354 }