ip6tables-restore.c revision b171b546cb529e2996df05fe91cba058fae9fd99
1/* Code to restore the iptables state, from file by ip6tables-save.
2 * Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
3 *
4 * based on iptables-restore
5 * Authors:
6 *      Harald Welte <laforge@gnumonks.org>
7 *      Rusty Russell <rusty@linuxcare.com.au>
8 * This code is distributed under the terms of GNU GPL v2
9 */
10
11#include <getopt.h>
12#include <sys/errno.h>
13#include <stdbool.h>
14#include <string.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include "ip6tables.h"
18#include "xtables.h"
19#include "libiptc/libip6tc.h"
20#include "ip6tables-multi.h"
21
22#ifdef DEBUG
23#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
24#else
25#define DEBUGP(x, args...)
26#endif
27
28static int binary = 0, counters = 0, verbose = 0, noflush = 0;
29
30/* Keeping track of external matches and targets.  */
31static const struct option options[] = {
32	{.name = "binary",   .has_arg = false, .val = 'b'},
33	{.name = "counters", .has_arg = false, .val = 'c'},
34	{.name = "verbose",  .has_arg = false, .val = 'v'},
35	{.name = "test",     .has_arg = false, .val = 't'},
36	{.name = "help",     .has_arg = false, .val = 'h'},
37	{.name = "noflush",  .has_arg = false, .val = 'n'},
38	{.name = "modprobe", .has_arg = true,  .val = 'M'},
39	{.name = "table",    .has_arg = true,  .val = 'T'},
40	{NULL},
41};
42
43static void print_usage(const char *name, const char *version) __attribute__((noreturn));
44
45static void print_usage(const char *name, const char *version)
46{
47	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
48			"	   [ --binary ]\n"
49			"	   [ --counters ]\n"
50			"	   [ --verbose ]\n"
51			"	   [ --test ]\n"
52			"	   [ --help ]\n"
53			"	   [ --noflush ]\n"
54			"          [ --modprobe=<command>]\n", name);
55
56	exit(1);
57}
58
59static struct ip6tc_handle *create_handle(const char *tablename)
60{
61	struct ip6tc_handle *handle;
62
63	handle = ip6tc_init(tablename);
64
65	if (!handle) {
66		/* try to insmod the module if iptc_init failed */
67		xtables_load_ko(xtables_modprobe_program, false);
68		handle = ip6tc_init(tablename);
69	}
70
71	if (!handle) {
72		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
73			"table '%s'\n", ip6tables_globals.program_name,
74			tablename);
75		exit(1);
76	}
77	return handle;
78}
79
80static int parse_counters(char *string, struct ip6t_counters *ctr)
81{
82	unsigned long long pcnt, bcnt;
83	int ret;
84
85	ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
86	ctr->pcnt = pcnt;
87	ctr->bcnt = bcnt;
88	return ret == 2;
89}
90
91/* global new argv and argc */
92static char *newargv[255];
93static int newargc;
94
95/* function adding one argument to newargv, updating newargc
96 * returns true if argument added, false otherwise */
97static int add_argv(char *what) {
98	DEBUGP("add_argv: %s\n", what);
99	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
100		newargv[newargc] = strdup(what);
101		newargc++;
102		return 1;
103	} else {
104		xtables_error(PARAMETER_PROBLEM,
105			"Parser cannot handle more arguments\n");
106		return 0;
107	}
108}
109
110static void free_argv(void) {
111	int i;
112
113	for (i = 0; i < newargc; i++)
114		free(newargv[i]);
115}
116
117int ip6tables_restore_main(int argc, char *argv[])
118{
119	struct ip6tc_handle *handle = NULL;
120	char buffer[10240];
121	int c;
122	char curtable[IP6T_TABLE_MAXNAMELEN + 1];
123	FILE *in;
124	int in_table = 0, testing = 0;
125	const char *tablename = NULL;
126
127	line = 0;
128
129	ip6tables_globals.program_name = "ip6tables-restore";
130	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
131	if (c < 0) {
132		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
133				ip6tables_globals.program_name,
134				ip6tables_globals.program_version);
135		exit(1);
136	}
137#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
138	init_extensions();
139	init_extensions6();
140#endif
141
142	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
143		switch (c) {
144			case 'b':
145				binary = 1;
146				break;
147			case 'c':
148				counters = 1;
149				break;
150			case 'v':
151				verbose = 1;
152				break;
153			case 't':
154				testing = 1;
155				break;
156			case 'h':
157				print_usage("ip6tables-restore",
158					    IPTABLES_VERSION);
159				break;
160			case 'n':
161				noflush = 1;
162				break;
163			case 'M':
164				xtables_modprobe_program = optarg;
165				break;
166			case 'T':
167				tablename = optarg;
168				break;
169		}
170	}
171
172	if (optind == argc - 1) {
173		in = fopen(argv[optind], "re");
174		if (!in) {
175			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
176				strerror(errno));
177			exit(1);
178		}
179	}
180	else if (optind < argc) {
181		fprintf(stderr, "Unknown arguments found on commandline\n");
182		exit(1);
183	}
184	else in = stdin;
185
186	/* Grab standard input. */
187	while (fgets(buffer, sizeof(buffer), in)) {
188		int ret = 0;
189
190		line++;
191		if (buffer[0] == '\n')
192			continue;
193		else if (buffer[0] == '#') {
194			if (verbose)
195				fputs(buffer, stdout);
196			continue;
197		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
198			if (!testing) {
199				DEBUGP("Calling commit\n");
200				ret = ip6tc_commit(handle);
201				ip6tc_free(handle);
202				handle = NULL;
203			} else {
204				DEBUGP("Not calling commit, testing\n");
205				ret = 1;
206			}
207			in_table = 0;
208		} else if ((buffer[0] == '*') && (!in_table)) {
209			/* New table */
210			char *table;
211
212			table = strtok(buffer+1, " \t\n");
213			DEBUGP("line %u, table '%s'\n", line, table);
214			if (!table) {
215				xtables_error(PARAMETER_PROBLEM,
216					"%s: line %u table name invalid\n",
217					ip6tables_globals.program_name,
218					line);
219				exit(1);
220			}
221			strncpy(curtable, table, IP6T_TABLE_MAXNAMELEN);
222			curtable[IP6T_TABLE_MAXNAMELEN] = '\0';
223
224			if (tablename != NULL && strcmp(tablename, table) != 0)
225				continue;
226			if (handle)
227				ip6tc_free(handle);
228
229			handle = create_handle(table);
230			if (noflush == 0) {
231				DEBUGP("Cleaning all chains of table '%s'\n",
232					table);
233				for_each_chain6(flush_entries6, verbose, 1,
234						handle);
235
236				DEBUGP("Deleting all user-defined chains "
237				       "of table '%s'\n", table);
238				for_each_chain6(delete_chain6, verbose, 0,
239						handle);
240			}
241
242			ret = 1;
243			in_table = 1;
244
245		} else if ((buffer[0] == ':') && (in_table)) {
246			/* New chain. */
247			char *policy, *chain;
248
249			chain = strtok(buffer+1, " \t\n");
250			DEBUGP("line %u, chain '%s'\n", line, chain);
251			if (!chain) {
252				xtables_error(PARAMETER_PROBLEM,
253					   "%s: line %u chain name invalid\n",
254					   ip6tables_globals.program_name,
255					   line);
256				exit(1);
257			}
258
259			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
260				xtables_error(PARAMETER_PROBLEM,
261					   "Invalid chain name `%s' "
262					   "(%u chars max)",
263					   chain, XT_EXTENSION_MAXNAMELEN - 1);
264
265			if (ip6tc_builtin(chain, handle) <= 0) {
266				if (noflush && ip6tc_is_chain(chain, handle)) {
267					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
268					if (!ip6tc_flush_entries(chain, handle))
269						xtables_error(PARAMETER_PROBLEM,
270							   "error flushing chain "
271							   "'%s':%s\n", chain,
272							   strerror(errno));
273				} else {
274					DEBUGP("Creating new chain '%s'\n", chain);
275					if (!ip6tc_create_chain(chain, handle))
276						xtables_error(PARAMETER_PROBLEM,
277							   "error creating chain "
278							   "'%s':%s\n", chain,
279							   strerror(errno));
280				}
281			}
282
283			policy = strtok(NULL, " \t\n");
284			DEBUGP("line %u, policy '%s'\n", line, policy);
285			if (!policy) {
286				xtables_error(PARAMETER_PROBLEM,
287					   "%s: line %u policy invalid\n",
288					   ip6tables_globals.program_name,
289					   line);
290				exit(1);
291			}
292
293			if (strcmp(policy, "-") != 0) {
294				struct ip6t_counters count;
295
296				if (counters) {
297					char *ctrs;
298					ctrs = strtok(NULL, " \t\n");
299
300					if (!ctrs || !parse_counters(ctrs, &count))
301						xtables_error(PARAMETER_PROBLEM,
302							  "invalid policy counters "
303							  "for chain '%s'\n", chain);
304
305				} else {
306					memset(&count, 0,
307					       sizeof(struct ip6t_counters));
308				}
309
310				DEBUGP("Setting policy of chain %s to %s\n",
311					chain, policy);
312
313				if (!ip6tc_set_policy(chain, policy, &count,
314						     handle))
315					xtables_error(OTHER_PROBLEM,
316						"Can't set policy `%s'"
317						" on `%s' line %u: %s\n",
318						policy, chain, line,
319						ip6tc_strerror(errno));
320			}
321
322			ret = 1;
323
324		} else if (in_table) {
325			int a;
326			char *ptr = buffer;
327			char *pcnt = NULL;
328			char *bcnt = NULL;
329			char *parsestart;
330
331			/* the parser */
332			char *curchar;
333			int quote_open, escaped;
334			size_t param_len;
335
336			/* reset the newargv */
337			newargc = 0;
338
339			if (buffer[0] == '[') {
340				/* we have counters in our input */
341				ptr = strchr(buffer, ']');
342				if (!ptr)
343					xtables_error(PARAMETER_PROBLEM,
344						   "Bad line %u: need ]\n",
345						   line);
346
347				pcnt = strtok(buffer+1, ":");
348				if (!pcnt)
349					xtables_error(PARAMETER_PROBLEM,
350						   "Bad line %u: need :\n",
351						   line);
352
353				bcnt = strtok(NULL, "]");
354				if (!bcnt)
355					xtables_error(PARAMETER_PROBLEM,
356						   "Bad line %u: need ]\n",
357						   line);
358
359				/* start command parsing after counter */
360				parsestart = ptr + 1;
361			} else {
362				/* start command parsing at start of line */
363				parsestart = buffer;
364			}
365
366			add_argv(argv[0]);
367			add_argv("-t");
368			add_argv(curtable);
369
370			if (counters && pcnt && bcnt) {
371				add_argv("--set-counters");
372				add_argv((char *) pcnt);
373				add_argv((char *) bcnt);
374			}
375
376			/* After fighting with strtok enough, here's now
377			 * a 'real' parser. According to Rusty I'm now no
378			 * longer a real hacker, but I can live with that */
379
380			quote_open = 0;
381			escaped = 0;
382			param_len = 0;
383
384			for (curchar = parsestart; *curchar; curchar++) {
385				char param_buffer[1024];
386
387				if (quote_open) {
388					if (escaped) {
389						param_buffer[param_len++] = *curchar;
390						escaped = 0;
391						continue;
392					} else if (*curchar == '\\') {
393						escaped = 1;
394						continue;
395					} else if (*curchar == '"') {
396						quote_open = 0;
397						*curchar = ' ';
398					} else {
399						param_buffer[param_len++] = *curchar;
400						continue;
401					}
402				} else {
403					if (*curchar == '"') {
404						quote_open = 1;
405						continue;
406					}
407				}
408
409				if (*curchar == ' '
410				    || *curchar == '\t'
411				    || * curchar == '\n') {
412					if (!param_len) {
413						/* two spaces? */
414						continue;
415					}
416
417					param_buffer[param_len] = '\0';
418
419					/* check if table name specified */
420					if (!strncmp(param_buffer, "-t", 2)
421                                            || !strncmp(param_buffer, "--table", 8)) {
422						xtables_error(PARAMETER_PROBLEM,
423						   "Line %u seems to have a "
424						   "-t table option.\n", line);
425						exit(1);
426					}
427
428					add_argv(param_buffer);
429					param_len = 0;
430				} else {
431					/* regular character, copy to buffer */
432					param_buffer[param_len++] = *curchar;
433
434					if (param_len >= sizeof(param_buffer))
435						xtables_error(PARAMETER_PROBLEM,
436						   "Parameter too long!");
437				}
438			}
439
440			DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
441				newargc, curtable);
442
443			for (a = 0; a < newargc; a++)
444				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
445
446			ret = do_command6(newargc, newargv,
447					 &newargv[2], &handle);
448
449			free_argv();
450			fflush(stdout);
451		}
452		if (tablename != NULL && strcmp(tablename, curtable) != 0)
453			continue;
454		if (!ret) {
455			fprintf(stderr, "%s: line %u failed\n",
456					ip6tables_globals.program_name,
457					line);
458			exit(1);
459		}
460	}
461	if (in_table) {
462		fprintf(stderr, "%s: COMMIT expected at line %u\n",
463				ip6tables_globals.program_name,
464				line + 1);
465		exit(1);
466	}
467
468	fclose(in);
469	return 0;
470}
471