ip6tables-restore.c revision 2165f38d2582e88e8a9dd9416f34eca7a7672e5a
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 xtc_handle *create_handle(const char *tablename)
60{
61	struct xtc_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 xt_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		newargv[++newargc] = NULL;
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 xtc_handle *handle = NULL;
120	char buffer[10240];
121	int c;
122	char curtable[XT_TABLE_MAXNAMELEN + 1];
123	FILE *in;
124	int in_table = 0, testing = 0;
125	const char *tablename = NULL;
126	const struct xtc_ops *ops = &ip6tc_ops;
127
128	line = 0;
129
130	ip6tables_globals.program_name = "ip6tables-restore";
131	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
132	if (c < 0) {
133		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
134				ip6tables_globals.program_name,
135				ip6tables_globals.program_version);
136		exit(1);
137	}
138#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
139	init_extensions();
140	init_extensions6();
141#endif
142
143	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
144		switch (c) {
145			case 'b':
146				binary = 1;
147				break;
148			case 'c':
149				counters = 1;
150				break;
151			case 'v':
152				verbose = 1;
153				break;
154			case 't':
155				testing = 1;
156				break;
157			case 'h':
158				print_usage("ip6tables-restore",
159					    IPTABLES_VERSION);
160				break;
161			case 'n':
162				noflush = 1;
163				break;
164			case 'M':
165				xtables_modprobe_program = optarg;
166				break;
167			case 'T':
168				tablename = optarg;
169				break;
170		}
171	}
172
173	if (optind == argc - 1) {
174		in = fopen(argv[optind], "re");
175		if (!in) {
176			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
177				strerror(errno));
178			exit(1);
179		}
180	}
181	else if (optind < argc) {
182		fprintf(stderr, "Unknown arguments found on commandline\n");
183		exit(1);
184	}
185	else in = stdin;
186
187	/* Grab standard input. */
188	while (fgets(buffer, sizeof(buffer), in)) {
189		int ret = 0;
190
191		line++;
192		if (buffer[0] == '\n')
193			continue;
194		else if (buffer[0] == '#') {
195			if (verbose)
196				fputs(buffer, stdout);
197			continue;
198		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
199			if (!testing) {
200				DEBUGP("Calling commit\n");
201				ret = ops->commit(handle);
202				ops->free(handle);
203				handle = NULL;
204			} else {
205				DEBUGP("Not calling commit, testing\n");
206				ret = 1;
207			}
208			in_table = 0;
209		} else if ((buffer[0] == '*') && (!in_table)) {
210			/* New table */
211			char *table;
212
213			table = strtok(buffer+1, " \t\n");
214			DEBUGP("line %u, table '%s'\n", line, table);
215			if (!table) {
216				xtables_error(PARAMETER_PROBLEM,
217					"%s: line %u table name invalid\n",
218					xt_params->program_name, line);
219				exit(1);
220			}
221			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
222			curtable[XT_TABLE_MAXNAMELEN] = '\0';
223
224			if (tablename != NULL && strcmp(tablename, table) != 0)
225				continue;
226			if (handle)
227				ops->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					   xt_params->program_name, line);
255				exit(1);
256			}
257
258			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
259				xtables_error(PARAMETER_PROBLEM,
260					   "Invalid chain name `%s' "
261					   "(%u chars max)",
262					   chain, XT_EXTENSION_MAXNAMELEN - 1);
263
264			if (ops->builtin(chain, handle) <= 0) {
265				if (noflush && ops->is_chain(chain, handle)) {
266					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
267					if (!ops->flush_entries(chain, handle))
268						xtables_error(PARAMETER_PROBLEM,
269							   "error flushing chain "
270							   "'%s':%s\n", chain,
271							   strerror(errno));
272				} else {
273					DEBUGP("Creating new chain '%s'\n", chain);
274					if (!ops->create_chain(chain, handle))
275						xtables_error(PARAMETER_PROBLEM,
276							   "error creating chain "
277							   "'%s':%s\n", chain,
278							   strerror(errno));
279				}
280			}
281
282			policy = strtok(NULL, " \t\n");
283			DEBUGP("line %u, policy '%s'\n", line, policy);
284			if (!policy) {
285				xtables_error(PARAMETER_PROBLEM,
286					   "%s: line %u policy invalid\n",
287					   xt_params->program_name, line);
288				exit(1);
289			}
290
291			if (strcmp(policy, "-") != 0) {
292				struct xt_counters count;
293
294				if (counters) {
295					char *ctrs;
296					ctrs = strtok(NULL, " \t\n");
297
298					if (!ctrs || !parse_counters(ctrs, &count))
299						xtables_error(PARAMETER_PROBLEM,
300							  "invalid policy counters "
301							  "for chain '%s'\n", chain);
302
303				} else {
304					memset(&count, 0, sizeof(count));
305				}
306
307				DEBUGP("Setting policy of chain %s to %s\n",
308					chain, policy);
309
310				if (!ops->set_policy(chain, policy, &count,
311						     handle))
312					xtables_error(OTHER_PROBLEM,
313						"Can't set policy `%s'"
314						" on `%s' line %u: %s\n",
315						policy, chain, line,
316						ops->strerror(errno));
317			}
318
319			ret = 1;
320
321		} else if (in_table) {
322			int a;
323			char *ptr = buffer;
324			char *pcnt = NULL;
325			char *bcnt = NULL;
326			char *parsestart;
327
328			/* the parser */
329			char *curchar;
330			int quote_open, escaped;
331			size_t param_len;
332			char param_buffer[1024];
333
334			/* reset the newargv */
335			newargc = 0;
336
337			if (buffer[0] == '[') {
338				/* we have counters in our input */
339				ptr = strchr(buffer, ']');
340				if (!ptr)
341					xtables_error(PARAMETER_PROBLEM,
342						   "Bad line %u: need ]\n",
343						   line);
344
345				pcnt = strtok(buffer+1, ":");
346				if (!pcnt)
347					xtables_error(PARAMETER_PROBLEM,
348						   "Bad line %u: need :\n",
349						   line);
350
351				bcnt = strtok(NULL, "]");
352				if (!bcnt)
353					xtables_error(PARAMETER_PROBLEM,
354						   "Bad line %u: need ]\n",
355						   line);
356
357				/* start command parsing after counter */
358				parsestart = ptr + 1;
359			} else {
360				/* start command parsing at start of line */
361				parsestart = buffer;
362			}
363
364			add_argv(argv[0]);
365			add_argv("-t");
366			add_argv(curtable);
367
368			if (counters && pcnt && bcnt) {
369				add_argv("--set-counters");
370				add_argv((char *) pcnt);
371				add_argv((char *) bcnt);
372			}
373
374			/* After fighting with strtok enough, here's now
375			 * a 'real' parser. According to Rusty I'm now no
376			 * longer a real hacker, but I can live with that */
377
378			quote_open = 0;
379			escaped = 0;
380			param_len = 0;
381
382			for (curchar = parsestart; *curchar; curchar++) {
383				if (quote_open) {
384					if (escaped) {
385						param_buffer[param_len++] = *curchar;
386						escaped = 0;
387						continue;
388					} else if (*curchar == '\\') {
389						escaped = 1;
390						continue;
391					} else if (*curchar == '"') {
392						quote_open = 0;
393						*curchar = ' ';
394					} else {
395						param_buffer[param_len++] = *curchar;
396						continue;
397					}
398				} else {
399					if (*curchar == '"') {
400						quote_open = 1;
401						continue;
402					}
403				}
404
405				if (*curchar == ' '
406				    || *curchar == '\t'
407				    || * curchar == '\n') {
408					if (!param_len) {
409						/* two spaces? */
410						continue;
411					}
412
413					param_buffer[param_len] = '\0';
414
415					/* check if table name specified */
416					if (!strncmp(param_buffer, "-t", 2)
417                                            || !strncmp(param_buffer, "--table", 8)) {
418						xtables_error(PARAMETER_PROBLEM,
419						   "Line %u seems to have a "
420						   "-t table option.\n", line);
421						exit(1);
422					}
423
424					add_argv(param_buffer);
425					param_len = 0;
426				} else {
427					/* regular character, copy to buffer */
428					param_buffer[param_len++] = *curchar;
429
430					if (param_len >= sizeof(param_buffer))
431						xtables_error(PARAMETER_PROBLEM,
432						   "Parameter too long!");
433				}
434			}
435
436			DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
437				newargc, curtable);
438
439			for (a = 0; a < newargc; a++)
440				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
441
442			ret = do_command6(newargc, newargv,
443					 &newargv[2], &handle);
444
445			free_argv();
446			fflush(stdout);
447		}
448		if (tablename != NULL && strcmp(tablename, curtable) != 0)
449			continue;
450		if (!ret) {
451			fprintf(stderr, "%s: line %u failed\n",
452					xt_params->program_name, line);
453			exit(1);
454		}
455	}
456	if (in_table) {
457		fprintf(stderr, "%s: COMMIT expected at line %u\n",
458				xt_params->program_name, line + 1);
459		exit(1);
460	}
461
462	fclose(in);
463	return 0;
464}
465