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
21static int binary = 0, counters = 0;
22
23static 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. */
33static void
34print_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 */
61struct pprot {
62	char *name;
63	u_int8_t num;
64};
65
66static 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. */
75static 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
99static 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 */
123static 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 (l == 0 && !invert)
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.  */
144static 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. */
226static 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
249static 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 */
317int 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}
360