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