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 
25 static int binary = 0, counters = 0, verbose = 0, noflush = 0;
26 
27 /* Keeping track of external matches and targets.  */
28 static 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 
40 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
41 
42 #define prog_name iptables_globals.program_name
43 
print_usage(const char * name,const char * version)44 static 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 
create_handle(const char * tablename)59 static 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 
parse_counters(char * string,struct xt_counters * ctr)79 static 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 */
91 static char *newargv[255];
92 static int newargc;
93 
94 /* function adding one argument to newargv, updating newargc
95  * returns true if argument added, false otherwise */
add_argv(char * what)96 static 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 
free_argv(void)109 static void free_argv(void) {
110 	int i;
111 
112 	for (i = 0; i < newargc; i++)
113 		free(newargv[i]);
114 }
115 
add_param_to_argv(char * parsestart)116 static 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 				"The -t option (seen in line %u) cannot be "
163 				"used in iptables-restore.\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 
180 int
iptables_restore_main(int argc,char * argv[])181 iptables_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, true);
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