• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "config.h"
8 #include <getopt.h>
9 #include <errno.h>
10 #include <libgen.h>
11 #include <stdbool.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include "iptables.h"
16 #include "xtables.h"
17 #include "libiptc/libiptc.h"
18 #include "xtables-multi.h"
19 #include "nft.h"
20 #include "nft-bridge.h"
21 #include "nft-cache.h"
22 #include <libnftnl/chain.h>
23 
24 static int counters, verbose;
25 
26 /* Keeping track of external matches and targets.  */
27 static const struct option options[] = {
28 	{.name = "counters", .has_arg = false, .val = 'c'},
29 	{.name = "verbose",  .has_arg = false, .val = 'v'},
30 	{.name = "version",       .has_arg = 0, .val = 'V'},
31 	{.name = "test",     .has_arg = false, .val = 't'},
32 	{.name = "help",     .has_arg = false, .val = 'h'},
33 	{.name = "noflush",  .has_arg = false, .val = 'n'},
34 	{.name = "modprobe", .has_arg = true,  .val = 'M'},
35 	{.name = "table",    .has_arg = true,  .val = 'T'},
36 	{.name = "ipv4",     .has_arg = false, .val = '4'},
37 	{.name = "ipv6",     .has_arg = false, .val = '6'},
38 	{.name = "wait",          .has_arg = 2, .val = 'w'},
39 	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
40 	{NULL},
41 };
42 
43 #define prog_name xtables_globals.program_name
44 #define prog_vers xtables_globals.program_version
45 
print_usage(const char * name,const char * version)46 static void print_usage(const char *name, const char *version)
47 {
48 	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6] [file]\n"
49 			"	   [ --counters ]\n"
50 			"	   [ --verbose ]\n"
51 			"	   [ --version]\n"
52 			"	   [ --test ]\n"
53 			"	   [ --help ]\n"
54 			"	   [ --noflush ]\n"
55 			"	   [ --table=<TABLE> ]\n"
56 			"	   [ --modprobe=<command> ]\n"
57 			"	   [ --ipv4 ]\n"
58 			"	   [ --ipv6 ]\n", name);
59 }
60 
61 static const struct nft_xt_restore_cb restore_cb = {
62 	.commit		= nft_commit,
63 	.abort		= nft_abort,
64 	.table_new	= nft_table_new,
65 	.table_flush	= nft_table_flush,
66 	.do_command	= do_commandx,
67 	.chain_set	= nft_chain_set,
68 	.chain_restore  = nft_chain_restore,
69 };
70 
71 struct nft_xt_restore_state {
72 	const struct builtin_table *curtable;
73 	struct argv_store av_store;
74 	bool in_table;
75 };
76 
xtables_restore_parse_line(struct nft_handle * h,const struct nft_xt_restore_parse * p,struct nft_xt_restore_state * state,char * buffer)77 static void xtables_restore_parse_line(struct nft_handle *h,
78 				       const struct nft_xt_restore_parse *p,
79 				       struct nft_xt_restore_state *state,
80 				       char *buffer)
81 {
82 	const struct nft_xt_restore_cb *cb = p->cb;
83 	int ret = 0;
84 
85 	if (buffer[0] == '\n')
86 		return;
87 	else if (buffer[0] == '#') {
88 		if (verbose) {
89 			fputs(buffer, stdout);
90 			fflush(stdout);
91 		}
92 		return;
93 	} else if (state->in_table &&
94 		   (strncmp(buffer, "COMMIT", 6) == 0) &&
95 		   (buffer[6] == '\0' || buffer[6] == '\n')) {
96 		if (!p->testing) {
97 			/* Commit per table, although we support
98 			 * global commit at once, stick by now to
99 			 * the existing behaviour.
100 			 */
101 			DEBUGP("Calling commit\n");
102 			if (cb->commit)
103 				ret = cb->commit(h);
104 		} else {
105 			DEBUGP("Not calling commit, testing\n");
106 			if (cb->abort)
107 				ret = cb->abort(h);
108 		}
109 		state->in_table = false;
110 
111 	} else if ((buffer[0] == '*') && (!state->in_table || !p->commit)) {
112 		/* New table */
113 		char *table;
114 
115 		table = strtok(buffer+1, " \t\n");
116 		DEBUGP("line %u, table '%s'\n", line, table);
117 		if (!table)
118 			xtables_error(PARAMETER_PROBLEM,
119 				"%s: line %u table name invalid\n",
120 				xt_params->program_name, line);
121 
122 		state->curtable = nft_table_builtin_find(h, table);
123 		if (!state->curtable)
124 			xtables_error(PARAMETER_PROBLEM,
125 				"%s: line %u table name '%s' invalid\n",
126 				xt_params->program_name, line, table);
127 
128 		if (p->tablename && (strcmp(p->tablename, table) != 0))
129 			return;
130 
131 		if (h->noflush == 0) {
132 			DEBUGP("Cleaning all chains of table '%s'\n", table);
133 			if (cb->table_flush)
134 				cb->table_flush(h, table);
135 		}
136 
137 		ret = 1;
138 		state->in_table = true;
139 
140 		if (cb->table_new)
141 			cb->table_new(h, table);
142 
143 	} else if ((buffer[0] == ':') && state->in_table) {
144 		/* New chain. */
145 		char *policy, *chain = NULL;
146 		struct xt_counters count = {};
147 
148 		chain = strtok(buffer+1, " \t\n");
149 		DEBUGP("line %u, chain '%s'\n", line, chain);
150 		if (!chain)
151 			xtables_error(PARAMETER_PROBLEM,
152 				   "%s: line %u chain name invalid\n",
153 				   xt_params->program_name, line);
154 
155 		if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
156 			xtables_error(PARAMETER_PROBLEM,
157 				   "Invalid chain name `%s' (%u chars max)",
158 				   chain, XT_EXTENSION_MAXNAMELEN - 1);
159 
160 		policy = strtok(NULL, " \t\n");
161 		DEBUGP("line %u, policy '%s'\n", line, policy);
162 		if (!policy)
163 			xtables_error(PARAMETER_PROBLEM,
164 				   "%s: line %u policy invalid\n",
165 				   xt_params->program_name, line);
166 
167 		if (nft_chain_builtin_find(state->curtable, chain)) {
168 			if (counters) {
169 				char *ctrs;
170 				ctrs = strtok(NULL, " \t\n");
171 
172 				if (!ctrs || !parse_counters(ctrs, &count))
173 					xtables_error(PARAMETER_PROBLEM,
174 						   "invalid policy counters for chain '%s'\n",
175 						   chain);
176 
177 			}
178 			if (cb->chain_set &&
179 			    cb->chain_set(h, state->curtable->name,
180 					  chain, policy, &count) < 0) {
181 				xtables_error(OTHER_PROBLEM,
182 					      "Can't set policy `%s' on `%s' line %u: %s\n",
183 					      policy, chain, line,
184 					      strerror(errno));
185 			}
186 			DEBUGP("Setting policy of chain %s to %s\n",
187 			       chain, policy);
188 		} else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
189 			   errno != EEXIST) {
190 			xtables_error(PARAMETER_PROBLEM,
191 				      "cannot create chain '%s' (%s)\n",
192 				      chain, strerror(errno));
193 		} else if (h->family == NFPROTO_BRIDGE &&
194 			   !ebt_set_user_chain_policy(h, state->curtable->name,
195 						      chain, policy)) {
196 			xtables_error(OTHER_PROBLEM,
197 				      "Can't set policy `%s' on `%s' line %u: %s\n",
198 				      policy, chain, line,
199 				      strerror(errno));
200 		}
201 		ret = 1;
202 	} else if (state->in_table) {
203 		char *pcnt = NULL;
204 		char *bcnt = NULL;
205 		char *parsestart = buffer;
206 
207 		add_argv(&state->av_store, xt_params->program_name, 0);
208 		add_argv(&state->av_store, "-t", 0);
209 		add_argv(&state->av_store, state->curtable->name, 0);
210 
211 		tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
212 		if (counters && pcnt && bcnt) {
213 			add_argv(&state->av_store, "--set-counters", 0);
214 			add_argv(&state->av_store, pcnt, 0);
215 			add_argv(&state->av_store, bcnt, 0);
216 		}
217 
218 		add_param_to_argv(&state->av_store, parsestart, line);
219 
220 		DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
221 		       state->av_store.argc, state->curtable->name);
222 		debug_print_argv(&state->av_store);
223 
224 		ret = cb->do_command(h, state->av_store.argc,
225 				     state->av_store.argv,
226 				     &state->av_store.argv[2], true);
227 		if (ret < 0) {
228 			if (cb->abort)
229 				ret = cb->abort(h);
230 			else
231 				ret = 0;
232 
233 			if (ret < 0) {
234 				fprintf(stderr,
235 					"failed to abort commit operation\n");
236 			}
237 			exit(1);
238 		}
239 
240 		free_argv(&state->av_store);
241 		fflush(stdout);
242 	}
243 	if (p->tablename && state->curtable &&
244 	    (strcmp(p->tablename, state->curtable->name) != 0))
245 		return;
246 	if (!ret) {
247 		fprintf(stderr, "%s: line %u failed\n",
248 				xt_params->program_name, line);
249 		exit(1);
250 	}
251 }
252 
253 /* Return true if given iptables-restore line will require a full cache.
254  * Typically these are commands referring to an existing rule
255  * (either by number or content) or commands listing the ruleset. */
cmd_needs_full_cache(char * cmd)256 static bool cmd_needs_full_cache(char *cmd)
257 {
258 	char c, chain[32];
259 	int rulenum, mcount;
260 
261 	mcount = sscanf(cmd, "-%c %31s %d", &c, chain, &rulenum);
262 
263 	if (mcount == 3)
264 		return true;
265 	if (mcount < 1)
266 		return false;
267 
268 	switch (c) {
269 	case 'D':
270 	case 'C':
271 	case 'S':
272 	case 'L':
273 	case 'Z':
274 		return true;
275 	}
276 
277 	return false;
278 }
279 
280 #define PREBUFSIZ	65536
281 
xtables_restore_parse(struct nft_handle * h,const struct nft_xt_restore_parse * p)282 void xtables_restore_parse(struct nft_handle *h,
283 			   const struct nft_xt_restore_parse *p)
284 {
285 	struct nft_xt_restore_state state = {};
286 	char preload_buffer[PREBUFSIZ] = {}, buffer[10240], *ptr;
287 
288 	if (!h->noflush) {
289 		nft_fake_cache(h);
290 	} else {
291 		ssize_t pblen = sizeof(preload_buffer);
292 		bool do_cache = false;
293 
294 		ptr = preload_buffer;
295 		while (fgets(buffer, sizeof(buffer), p->in)) {
296 			size_t blen = strlen(buffer);
297 
298 			/* drop trailing newline; xtables_restore_parse_line()
299 			 * uses strtok() which replaces them by nul-characters,
300 			 * causing unpredictable string delimiting in
301 			 * preload_buffer */
302 			if (buffer[blen - 1] == '\n')
303 				buffer[blen - 1] = '\0';
304 			else
305 				blen++;
306 
307 			pblen -= blen;
308 			if (pblen <= 0) {
309 				/* buffer exhausted */
310 				do_cache = true;
311 				break;
312 			}
313 
314 			if (cmd_needs_full_cache(buffer)) {
315 				do_cache = true;
316 				break;
317 			}
318 
319 			/* copy string including terminating nul-char */
320 			memcpy(ptr, buffer, blen);
321 			ptr += blen;
322 			buffer[0] = '\0';
323 		}
324 
325 		if (do_cache)
326 			nft_build_cache(h, NULL);
327 	}
328 
329 	line = 0;
330 	ptr = preload_buffer;
331 	while (*ptr) {
332 		h->error.lineno = ++line;
333 		DEBUGP("%s: buffered line %d: '%s'\n", __func__, line, ptr);
334 		xtables_restore_parse_line(h, p, &state, ptr);
335 		ptr += strlen(ptr) + 1;
336 	}
337 	if (*buffer) {
338 		h->error.lineno = ++line;
339 		DEBUGP("%s: overrun line %d: '%s'\n", __func__, line, buffer);
340 		xtables_restore_parse_line(h, p, &state, buffer);
341 	}
342 	while (fgets(buffer, sizeof(buffer), p->in)) {
343 		h->error.lineno = ++line;
344 		DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
345 		xtables_restore_parse_line(h, p, &state, buffer);
346 	}
347 	if (state.in_table && p->commit) {
348 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
349 				xt_params->program_name, line + 1);
350 		exit(1);
351 	} else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
352 		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
353 			      xt_params->program_name);
354 	}
355 }
356 
357 static int
xtables_restore_main(int family,const char * progname,int argc,char * argv[])358 xtables_restore_main(int family, const char *progname, int argc, char *argv[])
359 {
360 	const struct builtin_table *tables;
361 	struct nft_handle h = {
362 		.family = family,
363 		.restore = true,
364 	};
365 	int c;
366 	struct nft_xt_restore_parse p = {
367 		.commit = true,
368 		.cb = &restore_cb,
369 	};
370 
371 	line = 0;
372 
373 	xtables_globals.program_name = progname;
374 	c = xtables_init_all(&xtables_globals, family);
375 	if (c < 0) {
376 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
377 				xtables_globals.program_name,
378 				xtables_globals.program_version);
379 		exit(1);
380 	}
381 
382 	while ((c = getopt_long(argc, argv, "bcvVthnM:T:46wW", options, NULL)) != -1) {
383 		switch (c) {
384 			case 'b':
385 				fprintf(stderr, "-b/--binary option is not implemented\n");
386 				break;
387 			case 'c':
388 				counters = 1;
389 				break;
390 			case 'v':
391 				verbose = 1;
392 				break;
393 			case 'V':
394 				printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
395 				exit(0);
396 			case 't':
397 				p.testing = 1;
398 				break;
399 			case 'h':
400 				print_usage(prog_name, PACKAGE_VERSION);
401 				exit(0);
402 			case 'n':
403 				h.noflush = 1;
404 				break;
405 			case 'M':
406 				xtables_modprobe_program = optarg;
407 				break;
408 			case 'T':
409 				p.tablename = optarg;
410 				break;
411 			case '4':
412 				h.family = AF_INET;
413 				break;
414 			case '6':
415 				h.family = AF_INET6;
416 				xtables_set_nfproto(AF_INET6);
417 				break;
418 			case 'w': /* fallthrough.  Ignored by xt-restore */
419 			case 'W':
420 				if (!optarg && xs_has_arg(argc, argv))
421 					optind++;
422 				break;
423 			default:
424 				fprintf(stderr,
425 					"Try `%s -h' for more information.\n",
426 					prog_name);
427 				exit(1);
428 		}
429 	}
430 
431 	if (optind == argc - 1) {
432 		p.in = fopen(argv[optind], "re");
433 		if (!p.in) {
434 			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
435 				strerror(errno));
436 			exit(1);
437 		}
438 	} else if (optind < argc) {
439 		fprintf(stderr, "Unknown arguments found on commandline\n");
440 		exit(1);
441 	} else {
442 		p.in = stdin;
443 	}
444 
445 	switch (family) {
446 	case NFPROTO_IPV4:
447 	case NFPROTO_IPV6: /* fallthough, same table */
448 		tables = xtables_ipv4;
449 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
450 		init_extensions();
451 		init_extensions4();
452 #endif
453 		break;
454 	case NFPROTO_ARP:
455 		tables = xtables_arp;
456 		break;
457 	case NFPROTO_BRIDGE:
458 		tables = xtables_bridge;
459 		break;
460 	default:
461 		fprintf(stderr, "Unknown family %d\n", family);
462 		return 1;
463 	}
464 
465 	if (nft_init(&h, tables) < 0) {
466 		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
467 				xtables_globals.program_name,
468 				xtables_globals.program_version,
469 				strerror(errno));
470 		exit(EXIT_FAILURE);
471 	}
472 
473 	xtables_restore_parse(&h, &p);
474 
475 	nft_fini(&h);
476 	fclose(p.in);
477 	return 0;
478 }
479 
xtables_ip4_restore_main(int argc,char * argv[])480 int xtables_ip4_restore_main(int argc, char *argv[])
481 {
482 	return xtables_restore_main(NFPROTO_IPV4, basename(*argv),
483 				    argc, argv);
484 }
485 
xtables_ip6_restore_main(int argc,char * argv[])486 int xtables_ip6_restore_main(int argc, char *argv[])
487 {
488 	return xtables_restore_main(NFPROTO_IPV6, basename(*argv),
489 				    argc, argv);
490 }
491 
ebt_table_flush(struct nft_handle * h,const char * table)492 static int ebt_table_flush(struct nft_handle *h, const char *table)
493 {
494 	/* drop any pending policy rule add/removal jobs */
495 	nft_abort_policy_rule(h, table);
496 	return nft_table_flush(h, table);
497 }
498 
499 static const struct nft_xt_restore_cb ebt_restore_cb = {
500 	.commit		= nft_bridge_commit,
501 	.table_new	= nft_table_new,
502 	.table_flush	= ebt_table_flush,
503 	.do_command	= do_commandeb,
504 	.chain_set	= nft_chain_set,
505 	.chain_restore  = nft_chain_restore,
506 };
507 
508 static const struct option ebt_restore_options[] = {
509 	{.name = "noflush", .has_arg = 0, .val = 'n'},
510 	{ 0 }
511 };
512 
xtables_eb_restore_main(int argc,char * argv[])513 int xtables_eb_restore_main(int argc, char *argv[])
514 {
515 	struct nft_xt_restore_parse p = {
516 		.in = stdin,
517 		.cb = &ebt_restore_cb,
518 	};
519 	bool noflush = false;
520 	struct nft_handle h;
521 	int c;
522 
523 	while ((c = getopt_long(argc, argv, "n",
524 				ebt_restore_options, NULL)) != -1) {
525 		switch(c) {
526 		case 'n':
527 			noflush = 1;
528 			break;
529 		default:
530 			fprintf(stderr,
531 				"Usage: ebtables-restore [ --noflush ]\n");
532 			exit(1);
533 			break;
534 		}
535 	}
536 
537 	nft_init_eb(&h, "ebtables-restore");
538 	h.noflush = noflush;
539 	xtables_restore_parse(&h, &p);
540 	nft_fini(&h);
541 
542 	return 0;
543 }
544 
545 static const struct nft_xt_restore_cb arp_restore_cb = {
546 	.commit		= nft_commit,
547 	.table_new	= nft_table_new,
548 	.table_flush	= nft_table_flush,
549 	.do_command	= do_commandarp,
550 	.chain_set	= nft_chain_set,
551 	.chain_restore  = nft_chain_restore,
552 };
553 
xtables_arp_restore_main(int argc,char * argv[])554 int xtables_arp_restore_main(int argc, char *argv[])
555 {
556 	struct nft_xt_restore_parse p = {
557 		.in = stdin,
558 		.cb = &arp_restore_cb,
559 	};
560 	struct nft_handle h;
561 
562 	nft_init_arp(&h, "arptables-restore");
563 	xtables_restore_parse(&h, &p);
564 	nft_fini(&h);
565 
566 	return 0;
567 }
568