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