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