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