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