• 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 <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 
28 static int binary = 0, counters = 0, verbose = 0, noflush = 0;
29 
30 /* Keeping track of external matches and targets.  */
31 static 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 
43 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
44 
print_usage(const char * name,const char * version)45 static 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 
create_handle(const char * tablename)59 static 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 
parse_counters(char * string,struct xt_counters * ctr)80 static 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 */
92 static char *newargv[255];
93 static int newargc;
94 
95 /* function adding one argument to newargv, updating newargc
96  * returns true if argument added, false otherwise */
add_argv(char * what)97 static 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 
free_argv(void)110 static void free_argv(void) {
111 	int i;
112 
113 	for (i = 0; i < newargc; i++)
114 		free(newargv[i]);
115 }
116 
add_param_to_argv(char * parsestart)117 static void add_param_to_argv(char *parsestart)
118 {
119 	int quote_open = 0, escaped = 0, param_len = 0;
120 	char param_buffer[1024], *curchar;
121 
122 	/* After fighting with strtok enough, here's now
123 	 * a 'real' parser. According to Rusty I'm now no
124 	 * longer a real hacker, but I can live with that */
125 
126 	for (curchar = parsestart; *curchar; curchar++) {
127 		if (quote_open) {
128 			if (escaped) {
129 				param_buffer[param_len++] = *curchar;
130 				escaped = 0;
131 				continue;
132 			} else if (*curchar == '\\') {
133 				escaped = 1;
134 				continue;
135 			} else if (*curchar == '"') {
136 				quote_open = 0;
137 				*curchar = ' ';
138 			} else {
139 				param_buffer[param_len++] = *curchar;
140 				continue;
141 			}
142 		} else {
143 			if (*curchar == '"') {
144 				quote_open = 1;
145 				continue;
146 			}
147 		}
148 
149 		if (*curchar == ' '
150 		    || *curchar == '\t'
151 		    || * curchar == '\n') {
152 			if (!param_len) {
153 				/* two spaces? */
154 				continue;
155 			}
156 
157 			param_buffer[param_len] = '\0';
158 
159 			/* check if table name specified */
160 			if (!strncmp(param_buffer, "-t", 2)
161                             || !strncmp(param_buffer, "--table", 8)) {
162 				xtables_error(PARAMETER_PROBLEM,
163 				"The -t option (seen in line %u) cannot be "
164 				"used in ip6tables-restore.\n", line);
165 				exit(1);
166 			}
167 
168 			add_argv(param_buffer);
169 			param_len = 0;
170 		} else {
171 			/* regular character, copy to buffer */
172 			param_buffer[param_len++] = *curchar;
173 
174 			if (param_len >= sizeof(param_buffer))
175 				xtables_error(PARAMETER_PROBLEM,
176 				   "Parameter too long!");
177 		}
178 	}
179 }
180 
ip6tables_restore_main(int argc,char * argv[])181 int ip6tables_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 = &ip6tc_ops;
191 
192 	line = 0;
193 
194 	ip6tables_globals.program_name = "ip6tables-restore";
195 	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
196 	if (c < 0) {
197 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
198 				ip6tables_globals.program_name,
199 				ip6tables_globals.program_version);
200 		exit(1);
201 	}
202 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
203 	init_extensions();
204 	init_extensions6();
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("ip6tables-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 != NULL && 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_chain6(flush_entries6, verbose, 1,
298 						handle);
299 
300 				DEBUGP("Deleting all user-defined chains "
301 				       "of table '%s'\n", table);
302 				for_each_chain6(delete_chain6, 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_command6(%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_command6(newargc, newargv,
441 					 &newargv[2], &handle, true);
442 
443 			free_argv();
444 			fflush(stdout);
445 		}
446 		if (tablename != NULL && 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