iptables-restore.c revision 44191bdbd71e685fba9eab864b9df25e63905220
1/* Code to restore the iptables state, from file by iptables-save.
2 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
3 * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
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 <stdbool.h>
11#include <string.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include "iptables.h"
15#include "xtables.h"
16#include "libiptc/libiptc.h"
17#include "iptables-multi.h"
18
19#ifdef DEBUG
20#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
21#else
22#define DEBUGP(x, args...)
23#endif
24
25static int binary = 0, counters = 0, verbose = 0, noflush = 0;
26
27/* Keeping track of external matches and targets.  */
28static const struct option options[] = {
29	{.name = "binary",   .has_arg = false, .val = 'b'},
30	{.name = "counters", .has_arg = false, .val = 'c'},
31	{.name = "verbose",  .has_arg = false, .val = 'v'},
32	{.name = "test",     .has_arg = false, .val = 't'},
33	{.name = "help",     .has_arg = false, .val = 'h'},
34	{.name = "noflush",  .has_arg = false, .val = 'n'},
35	{.name = "modprobe", .has_arg = true,  .val = 'M'},
36	{.name = "table",    .has_arg = true,  .val = 'T'},
37	{NULL},
38};
39
40static void print_usage(const char *name, const char *version) __attribute__((noreturn));
41
42#define prog_name iptables_globals.program_name
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			"	   [ --table=<TABLE> ]\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 = iptc_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 = iptc_init(tablename);
69	}
70
71	if (!handle) {
72		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
73			"table '%s'\n", prog_name, tablename);
74		exit(1);
75	}
76	return handle;
77}
78
79static int parse_counters(char *string, struct xt_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		newargv[++newargc] = NULL;
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
116static void add_param_to_argv(char *parsestart)
117{
118	int quote_open = 0, escaped = 0, param_len = 0;
119	char param_buffer[1024], *curchar;
120
121	/* After fighting with strtok enough, here's now
122	 * a 'real' parser. According to Rusty I'm now no
123	 * longer a real hacker, but I can live with that */
124
125	for (curchar = parsestart; *curchar; curchar++) {
126		if (quote_open) {
127			if (escaped) {
128				param_buffer[param_len++] = *curchar;
129				escaped = 0;
130				continue;
131			} else if (*curchar == '\\') {
132				escaped = 1;
133				continue;
134			} else if (*curchar == '"') {
135				quote_open = 0;
136				*curchar = ' ';
137			} else {
138				param_buffer[param_len++] = *curchar;
139				continue;
140			}
141		} else {
142			if (*curchar == '"') {
143				quote_open = 1;
144				continue;
145			}
146		}
147
148		if (*curchar == ' '
149		    || *curchar == '\t'
150		    || * curchar == '\n') {
151			if (!param_len) {
152				/* two spaces? */
153				continue;
154			}
155
156			param_buffer[param_len] = '\0';
157
158			/* check if table name specified */
159			if (!strncmp(param_buffer, "-t", 2)
160			    || !strncmp(param_buffer, "--table", 8)) {
161				xtables_error(PARAMETER_PROBLEM,
162				   "Line %u seems to have a "
163				   "-t table option.\n", line);
164				exit(1);
165			}
166
167			add_argv(param_buffer);
168			param_len = 0;
169		} else {
170			/* regular character, copy to buffer */
171			param_buffer[param_len++] = *curchar;
172
173			if (param_len >= sizeof(param_buffer))
174				xtables_error(PARAMETER_PROBLEM,
175				   "Parameter too long!");
176		}
177	}
178}
179
180int
181iptables_restore_main(int argc, char *argv[])
182{
183	struct xtc_handle *handle = NULL;
184	char buffer[10240];
185	int c;
186	char curtable[XT_TABLE_MAXNAMELEN + 1];
187	FILE *in;
188	int in_table = 0, testing = 0;
189	const char *tablename = NULL;
190	const struct xtc_ops *ops = &iptc_ops;
191
192	line = 0;
193
194	iptables_globals.program_name = "iptables-restore";
195	c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
196	if (c < 0) {
197		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
198				iptables_globals.program_name,
199				iptables_globals.program_version);
200		exit(1);
201	}
202#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
203	init_extensions();
204	init_extensions4();
205#endif
206
207	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
208		switch (c) {
209			case 'b':
210				binary = 1;
211				break;
212			case 'c':
213				counters = 1;
214				break;
215			case 'v':
216				verbose = 1;
217				break;
218			case 't':
219				testing = 1;
220				break;
221			case 'h':
222				print_usage("iptables-restore",
223					    IPTABLES_VERSION);
224				break;
225			case 'n':
226				noflush = 1;
227				break;
228			case 'M':
229				xtables_modprobe_program = optarg;
230				break;
231			case 'T':
232				tablename = optarg;
233				break;
234		}
235	}
236
237	if (optind == argc - 1) {
238		in = fopen(argv[optind], "re");
239		if (!in) {
240			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
241				strerror(errno));
242			exit(1);
243		}
244	}
245	else if (optind < argc) {
246		fprintf(stderr, "Unknown arguments found on commandline\n");
247		exit(1);
248	}
249	else in = stdin;
250
251	/* Grab standard input. */
252	while (fgets(buffer, sizeof(buffer), in)) {
253		int ret = 0;
254
255		line++;
256		if (buffer[0] == '\n')
257			continue;
258		else if (buffer[0] == '#') {
259			if (verbose)
260				fputs(buffer, stdout);
261			continue;
262		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
263			if (!testing) {
264				DEBUGP("Calling commit\n");
265				ret = ops->commit(handle);
266				ops->free(handle);
267				handle = NULL;
268			} else {
269				DEBUGP("Not calling commit, testing\n");
270				ret = 1;
271			}
272			in_table = 0;
273		} else if ((buffer[0] == '*') && (!in_table)) {
274			/* New table */
275			char *table;
276
277			table = strtok(buffer+1, " \t\n");
278			DEBUGP("line %u, table '%s'\n", line, table);
279			if (!table) {
280				xtables_error(PARAMETER_PROBLEM,
281					"%s: line %u table name invalid\n",
282					xt_params->program_name, line);
283				exit(1);
284			}
285			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
286			curtable[XT_TABLE_MAXNAMELEN] = '\0';
287
288			if (tablename && (strcmp(tablename, table) != 0))
289				continue;
290			if (handle)
291				ops->free(handle);
292
293			handle = create_handle(table);
294			if (noflush == 0) {
295				DEBUGP("Cleaning all chains of table '%s'\n",
296					table);
297				for_each_chain4(flush_entries4, verbose, 1,
298						handle);
299
300				DEBUGP("Deleting all user-defined chains "
301				       "of table '%s'\n", table);
302				for_each_chain4(delete_chain4, verbose, 0,
303						handle);
304			}
305
306			ret = 1;
307			in_table = 1;
308
309		} else if ((buffer[0] == ':') && (in_table)) {
310			/* New chain. */
311			char *policy, *chain;
312
313			chain = strtok(buffer+1, " \t\n");
314			DEBUGP("line %u, chain '%s'\n", line, chain);
315			if (!chain) {
316				xtables_error(PARAMETER_PROBLEM,
317					   "%s: line %u chain name invalid\n",
318					   xt_params->program_name, line);
319				exit(1);
320			}
321
322			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
323				xtables_error(PARAMETER_PROBLEM,
324					   "Invalid chain name `%s' "
325					   "(%u chars max)",
326					   chain, XT_EXTENSION_MAXNAMELEN - 1);
327
328			if (ops->builtin(chain, handle) <= 0) {
329				if (noflush && ops->is_chain(chain, handle)) {
330					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
331					if (!ops->flush_entries(chain, handle))
332						xtables_error(PARAMETER_PROBLEM,
333							   "error flushing chain "
334							   "'%s':%s\n", chain,
335							   strerror(errno));
336				} else {
337					DEBUGP("Creating new chain '%s'\n", chain);
338					if (!ops->create_chain(chain, handle))
339						xtables_error(PARAMETER_PROBLEM,
340							   "error creating chain "
341							   "'%s':%s\n", chain,
342							   strerror(errno));
343				}
344			}
345
346			policy = strtok(NULL, " \t\n");
347			DEBUGP("line %u, policy '%s'\n", line, policy);
348			if (!policy) {
349				xtables_error(PARAMETER_PROBLEM,
350					   "%s: line %u policy invalid\n",
351					   xt_params->program_name, line);
352				exit(1);
353			}
354
355			if (strcmp(policy, "-") != 0) {
356				struct xt_counters count;
357
358				if (counters) {
359					char *ctrs;
360					ctrs = strtok(NULL, " \t\n");
361
362					if (!ctrs || !parse_counters(ctrs, &count))
363						xtables_error(PARAMETER_PROBLEM,
364							   "invalid policy counters "
365							   "for chain '%s'\n", chain);
366
367				} else {
368					memset(&count, 0, sizeof(count));
369				}
370
371				DEBUGP("Setting policy of chain %s to %s\n",
372					chain, policy);
373
374				if (!ops->set_policy(chain, policy, &count,
375						     handle))
376					xtables_error(OTHER_PROBLEM,
377						"Can't set policy `%s'"
378						" on `%s' line %u: %s\n",
379						policy, chain, line,
380						ops->strerror(errno));
381			}
382
383			ret = 1;
384
385		} else if (in_table) {
386			int a;
387			char *ptr = buffer;
388			char *pcnt = NULL;
389			char *bcnt = NULL;
390			char *parsestart;
391
392			/* reset the newargv */
393			newargc = 0;
394
395			if (buffer[0] == '[') {
396				/* we have counters in our input */
397				ptr = strchr(buffer, ']');
398				if (!ptr)
399					xtables_error(PARAMETER_PROBLEM,
400						   "Bad line %u: need ]\n",
401						   line);
402
403				pcnt = strtok(buffer+1, ":");
404				if (!pcnt)
405					xtables_error(PARAMETER_PROBLEM,
406						   "Bad line %u: need :\n",
407						   line);
408
409				bcnt = strtok(NULL, "]");
410				if (!bcnt)
411					xtables_error(PARAMETER_PROBLEM,
412						   "Bad line %u: need ]\n",
413						   line);
414
415				/* start command parsing after counter */
416				parsestart = ptr + 1;
417			} else {
418				/* start command parsing at start of line */
419				parsestart = buffer;
420			}
421
422			add_argv(argv[0]);
423			add_argv("-t");
424			add_argv(curtable);
425
426			if (counters && pcnt && bcnt) {
427				add_argv("--set-counters");
428				add_argv((char *) pcnt);
429				add_argv((char *) bcnt);
430			}
431
432			add_param_to_argv(parsestart);
433
434			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
435				newargc, curtable);
436
437			for (a = 0; a < newargc; a++)
438				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
439
440			ret = do_command4(newargc, newargv,
441					 &newargv[2], &handle);
442
443			free_argv();
444			fflush(stdout);
445		}
446		if (tablename && (strcmp(tablename, curtable) != 0))
447			continue;
448		if (!ret) {
449			fprintf(stderr, "%s: line %u failed\n",
450					xt_params->program_name, line);
451			exit(1);
452		}
453	}
454	if (in_table) {
455		fprintf(stderr, "%s: COMMIT expected at line %u\n",
456				xt_params->program_name, line + 1);
457		exit(1);
458	}
459
460	fclose(in);
461	return 0;
462}
463