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