• 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_flush	= nft_cmd_table_flush,
65 	.do_command	= do_commandx,
66 	.chain_set	= nft_cmd_chain_set,
67 	.chain_restore  = nft_cmd_chain_restore,
68 };
69 
70 struct nft_xt_restore_state {
71 	const struct builtin_table *curtable;
72 	struct argv_store av_store;
73 	bool in_table;
74 };
75 
xtables_restore_parse_line(struct nft_handle * h,const struct nft_xt_restore_parse * p,struct nft_xt_restore_state * state,char * buffer)76 static void xtables_restore_parse_line(struct nft_handle *h,
77 				       const struct nft_xt_restore_parse *p,
78 				       struct nft_xt_restore_state *state,
79 				       char *buffer)
80 {
81 	const struct nft_xt_restore_cb *cb = p->cb;
82 	int ret = 0;
83 
84 	if (buffer[0] == '\n')
85 		return;
86 	else if (buffer[0] == '#') {
87 		if (verbose) {
88 			fputs(buffer, stdout);
89 			fflush(stdout);
90 		}
91 		return;
92 	} else if (state->in_table &&
93 		   (strncmp(buffer, "COMMIT", 6) == 0) &&
94 		   (buffer[6] == '\0' || buffer[6] == '\n')) {
95 		if (!p->testing) {
96 			/* Commit per table, although we support
97 			 * global commit at once, stick by now to
98 			 * the existing behaviour.
99 			 */
100 			DEBUGP("Calling commit\n");
101 			if (cb->commit)
102 				ret = cb->commit(h);
103 		} else {
104 			DEBUGP("Not calling commit, testing\n");
105 			if (cb->abort)
106 				ret = cb->abort(h);
107 		}
108 		state->in_table = false;
109 
110 	} else if ((buffer[0] == '*') && (!state->in_table || !p->commit)) {
111 		/* New table */
112 		char *table;
113 
114 		table = strtok(buffer+1, " \t\n");
115 		DEBUGP("line %u, table '%s'\n", line, table);
116 		if (!table)
117 			xtables_error(PARAMETER_PROBLEM,
118 				"%s: line %u table name invalid\n",
119 				xt_params->program_name, line);
120 
121 		state->curtable = nft_table_builtin_find(h, table);
122 		if (!state->curtable)
123 			xtables_error(PARAMETER_PROBLEM,
124 				"%s: line %u table name '%s' invalid\n",
125 				xt_params->program_name, line, table);
126 
127 		if (p->tablename && (strcmp(p->tablename, table) != 0))
128 			return;
129 
130 		/* implicit commit if no explicit COMMIT supported */
131 		if (!p->commit)
132 			cb->commit(h);
133 
134 		if (h->noflush == 0) {
135 			DEBUGP("Cleaning all chains of table '%s'\n", table);
136 			if (cb->table_flush)
137 				cb->table_flush(h, table, verbose);
138 		}
139 
140 		ret = 1;
141 		state->in_table = true;
142 
143 		if (cb->table_new)
144 			cb->table_new(h, table);
145 
146 	} else if ((buffer[0] == ':') && state->in_table) {
147 		/* New chain. */
148 		char *policy, *chain = NULL;
149 		struct xt_counters count = {};
150 
151 		chain = strtok(buffer+1, " \t\n");
152 		DEBUGP("line %u, chain '%s'\n", line, chain);
153 		if (!chain)
154 			xtables_error(PARAMETER_PROBLEM,
155 				   "%s: line %u chain name invalid\n",
156 				   xt_params->program_name, line);
157 
158 		if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
159 			xtables_error(PARAMETER_PROBLEM,
160 				   "Invalid chain name `%s' (%u chars max)",
161 				   chain, XT_EXTENSION_MAXNAMELEN - 1);
162 
163 		policy = strtok(NULL, " \t\n");
164 		DEBUGP("line %u, policy '%s'\n", line, policy);
165 		if (!policy)
166 			xtables_error(PARAMETER_PROBLEM,
167 				   "%s: line %u policy invalid\n",
168 				   xt_params->program_name, line);
169 
170 		if (nft_chain_builtin_find(state->curtable, chain)) {
171 			if (counters) {
172 				char *ctrs;
173 				ctrs = strtok(NULL, " \t\n");
174 
175 				if (!ctrs || !parse_counters(ctrs, &count))
176 					xtables_error(PARAMETER_PROBLEM,
177 						   "invalid policy counters for chain '%s'\n",
178 						   chain);
179 
180 			}
181 			if (cb->chain_set &&
182 			    cb->chain_set(h, state->curtable->name,
183 					  chain, policy, &count) < 0) {
184 				xtables_error(OTHER_PROBLEM,
185 					      "Can't set policy `%s' on `%s' line %u: %s\n",
186 					      policy, chain, line,
187 					      strerror(errno));
188 			}
189 			DEBUGP("Setting policy of chain %s to %s\n",
190 			       chain, policy);
191 		} else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
192 			   errno != EEXIST) {
193 			xtables_error(PARAMETER_PROBLEM,
194 				      "cannot create chain '%s' (%s)\n",
195 				      chain, strerror(errno));
196 		} else if (h->family == NFPROTO_BRIDGE &&
197 			   !ebt_cmd_user_chain_policy(h, state->curtable->name,
198 						      chain, policy)) {
199 			xtables_error(OTHER_PROBLEM,
200 				      "Can't set policy `%s' on `%s' line %u: %s\n",
201 				      policy, chain, line,
202 				      strerror(errno));
203 		}
204 		ret = 1;
205 	} else if (state->in_table) {
206 		char *pcnt = NULL;
207 		char *bcnt = NULL;
208 		char *parsestart = buffer;
209 
210 		add_argv(&state->av_store, xt_params->program_name, 0);
211 		add_argv(&state->av_store, "-t", 0);
212 		add_argv(&state->av_store, state->curtable->name, 0);
213 
214 		tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
215 		if (counters && pcnt && bcnt) {
216 			add_argv(&state->av_store, "--set-counters", 0);
217 			add_argv(&state->av_store, pcnt, 0);
218 			add_argv(&state->av_store, bcnt, 0);
219 		}
220 
221 		add_param_to_argv(&state->av_store, parsestart, line);
222 
223 		DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
224 		       state->av_store.argc, state->curtable->name);
225 		debug_print_argv(&state->av_store);
226 
227 		ret = cb->do_command(h, state->av_store.argc,
228 				     state->av_store.argv,
229 				     &state->av_store.argv[2], true);
230 		if (ret < 0) {
231 			if (cb->abort)
232 				ret = cb->abort(h);
233 			else
234 				ret = 0;
235 
236 			if (ret < 0) {
237 				fprintf(stderr,
238 					"failed to abort commit operation\n");
239 			}
240 			exit(1);
241 		}
242 
243 		free_argv(&state->av_store);
244 		fflush(stdout);
245 	}
246 	if (p->tablename && state->curtable &&
247 	    (strcmp(p->tablename, state->curtable->name) != 0))
248 		return;
249 	if (!ret) {
250 		fprintf(stderr, "%s: line %u failed\n",
251 				xt_params->program_name, line);
252 		exit(1);
253 	}
254 }
255 
xtables_restore_parse(struct nft_handle * h,const struct nft_xt_restore_parse * p)256 void xtables_restore_parse(struct nft_handle *h,
257 			   const struct nft_xt_restore_parse *p)
258 {
259 	struct nft_xt_restore_state state = {};
260 	char buffer[10240] = {};
261 
262 	if (!verbose && !h->noflush)
263 		nft_cache_level_set(h, NFT_CL_FAKE, NULL);
264 
265 	line = 0;
266 	while (fgets(buffer, sizeof(buffer), p->in)) {
267 		h->error.lineno = ++line;
268 		DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
269 		xtables_restore_parse_line(h, p, &state, buffer);
270 	}
271 	if (state.in_table && p->commit) {
272 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
273 				xt_params->program_name, line + 1);
274 		exit(1);
275 	} else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
276 		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
277 			      xt_params->program_name);
278 	}
279 }
280 
281 static int
xtables_restore_main(int family,const char * progname,int argc,char * argv[])282 xtables_restore_main(int family, const char *progname, int argc, char *argv[])
283 {
284 	const struct builtin_table *tables;
285 	struct nft_xt_restore_parse p = {
286 		.commit = true,
287 		.cb = &restore_cb,
288 	};
289 	bool noflush = false;
290 	struct nft_handle h;
291 	int c;
292 
293 	line = 0;
294 
295 	xtables_globals.program_name = progname;
296 	c = xtables_init_all(&xtables_globals, family);
297 	if (c < 0) {
298 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
299 				xtables_globals.program_name,
300 				xtables_globals.program_version);
301 		exit(1);
302 	}
303 
304 	while ((c = getopt_long(argc, argv, "bcvVthnM:T:wW", options, NULL)) != -1) {
305 		switch (c) {
306 			case 'b':
307 				fprintf(stderr, "-b/--binary option is not implemented\n");
308 				break;
309 			case 'c':
310 				counters = 1;
311 				break;
312 			case 'v':
313 				verbose = 1;
314 				break;
315 			case 'V':
316 				printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
317 				exit(0);
318 			case 't':
319 				p.testing = 1;
320 				break;
321 			case 'h':
322 				print_usage(prog_name, PACKAGE_VERSION);
323 				exit(0);
324 			case 'n':
325 				noflush = true;
326 				break;
327 			case 'M':
328 				xtables_modprobe_program = optarg;
329 				break;
330 			case 'T':
331 				p.tablename = optarg;
332 				break;
333 			case 'w': /* fallthrough.  Ignored by xt-restore */
334 			case 'W':
335 				if (!optarg && xs_has_arg(argc, argv))
336 					optind++;
337 				break;
338 			default:
339 				fprintf(stderr,
340 					"Try `%s -h' for more information.\n",
341 					prog_name);
342 				exit(1);
343 		}
344 	}
345 
346 	if (optind == argc - 1) {
347 		p.in = fopen(argv[optind], "re");
348 		if (!p.in) {
349 			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
350 				strerror(errno));
351 			exit(1);
352 		}
353 	} else if (optind < argc) {
354 		fprintf(stderr, "Unknown arguments found on commandline\n");
355 		exit(1);
356 	} else {
357 		p.in = stdin;
358 	}
359 
360 	switch (family) {
361 	case NFPROTO_IPV4:
362 	case NFPROTO_IPV6: /* fallthough, same table */
363 		tables = xtables_ipv4;
364 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
365 		init_extensions();
366 		init_extensions4();
367 #endif
368 		break;
369 	case NFPROTO_ARP:
370 		tables = xtables_arp;
371 		break;
372 	case NFPROTO_BRIDGE:
373 		tables = xtables_bridge;
374 		break;
375 	default:
376 		fprintf(stderr, "Unknown family %d\n", family);
377 		return 1;
378 	}
379 
380 	if (nft_init(&h, family, tables) < 0) {
381 		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
382 				xtables_globals.program_name,
383 				xtables_globals.program_version,
384 				strerror(errno));
385 		exit(EXIT_FAILURE);
386 	}
387 	h.noflush = noflush;
388 	h.restore = true;
389 
390 	xtables_restore_parse(&h, &p);
391 
392 	nft_fini(&h);
393 	xtables_fini();
394 	fclose(p.in);
395 	return 0;
396 }
397 
xtables_ip4_restore_main(int argc,char * argv[])398 int xtables_ip4_restore_main(int argc, char *argv[])
399 {
400 	return xtables_restore_main(NFPROTO_IPV4, basename(*argv),
401 				    argc, argv);
402 }
403 
xtables_ip6_restore_main(int argc,char * argv[])404 int xtables_ip6_restore_main(int argc, char *argv[])
405 {
406 	return xtables_restore_main(NFPROTO_IPV6, basename(*argv),
407 				    argc, argv);
408 }
409 
410 static const struct nft_xt_restore_cb ebt_restore_cb = {
411 	.commit		= nft_bridge_commit,
412 	.table_flush	= nft_cmd_table_flush,
413 	.do_command	= do_commandeb,
414 	.chain_set	= nft_cmd_chain_set,
415 	.chain_restore  = nft_cmd_chain_restore,
416 };
417 
418 static const struct option ebt_restore_options[] = {
419 	{.name = "noflush", .has_arg = 0, .val = 'n'},
420 	{ 0 }
421 };
422 
xtables_eb_restore_main(int argc,char * argv[])423 int xtables_eb_restore_main(int argc, char *argv[])
424 {
425 	struct nft_xt_restore_parse p = {
426 		.in = stdin,
427 		.cb = &ebt_restore_cb,
428 	};
429 	bool noflush = false;
430 	struct nft_handle h;
431 	int c;
432 
433 	while ((c = getopt_long(argc, argv, "n",
434 				ebt_restore_options, NULL)) != -1) {
435 		switch(c) {
436 		case 'n':
437 			noflush = 1;
438 			break;
439 		default:
440 			fprintf(stderr,
441 				"Usage: ebtables-restore [ --noflush ]\n");
442 			exit(1);
443 			break;
444 		}
445 	}
446 
447 	nft_init_eb(&h, "ebtables-restore");
448 	h.noflush = noflush;
449 	xtables_restore_parse(&h, &p);
450 	nft_fini_eb(&h);
451 
452 	return 0;
453 }
454 
455 static const struct nft_xt_restore_cb arp_restore_cb = {
456 	.commit		= nft_commit,
457 	.table_flush	= nft_cmd_table_flush,
458 	.do_command	= do_commandarp,
459 	.chain_set	= nft_cmd_chain_set,
460 	.chain_restore  = nft_cmd_chain_restore,
461 };
462 
xtables_arp_restore_main(int argc,char * argv[])463 int xtables_arp_restore_main(int argc, char *argv[])
464 {
465 	struct nft_xt_restore_parse p = {
466 		.in = stdin,
467 		.cb = &arp_restore_cb,
468 	};
469 	struct nft_handle h;
470 
471 	nft_init_arp(&h, "arptables-restore");
472 	xtables_restore_parse(&h, &p);
473 	nft_fini(&h);
474 	xtables_fini();
475 
476 	return 0;
477 }
478