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