xref: /aosp_15_r20/external/iptables/iptables/xtables-restore.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /* Code to restore the iptables state, from file by iptables-save.
2  * (C) 2000-2002 by Harald Welte <[email protected]>
3  * based on previous code from Rusty Russell <[email protected]>
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",
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",
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",
156 				      xt_params->program_name, line);
157 
158 		xtables_announce_chain(chain);
159 		assert_valid_chain_name(chain);
160 
161 		policy = strtok(NULL, " \t\n");
162 		DEBUGP("line %u, policy '%s'\n", line, policy);
163 		if (!policy)
164 			xtables_error(PARAMETER_PROBLEM,
165 				      "%s: line %u policy invalid",
166 				      xt_params->program_name, line);
167 
168 		if (nft_chain_builtin_find(state->curtable, chain)) {
169 			char *ctrs = strtok(NULL, " \t\n");
170 
171 			if ((!ctrs && counters) ||
172 			    (ctrs && !parse_counters(ctrs, &count)))
173 				xtables_error(PARAMETER_PROBLEM,
174 					      "invalid policy counters for chain '%s'",
175 					      chain);
176 			if (cb->chain_set &&
177 			    cb->chain_set(h, state->curtable->name,
178 					  chain, policy,
179 					  counters ? &count : NULL) < 0) {
180 				xtables_error(OTHER_PROBLEM,
181 					      "Can't set policy `%s' on `%s' line %u: %s",
182 					      policy, chain, line,
183 					      strerror(errno));
184 			}
185 			DEBUGP("Setting policy of chain %s to %s\n",
186 			       chain, policy);
187 		} else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
188 			   errno != EEXIST) {
189 			xtables_error(PARAMETER_PROBLEM,
190 				      "cannot create chain '%s' (%s)",
191 				      chain, strerror(errno));
192 		} else if (h->family == NFPROTO_BRIDGE &&
193 			   !ebt_cmd_user_chain_policy(h, state->curtable->name,
194 						      chain, policy)) {
195 			xtables_error(OTHER_PROBLEM,
196 				      "Can't set policy `%s' on `%s' line %u: %s",
197 				      policy, chain, line,
198 				      strerror(errno));
199 		}
200 		ret = 1;
201 	} else if (state->in_table) {
202 		char *pcnt = NULL;
203 		char *bcnt = NULL;
204 		char *parsestart = buffer;
205 		int i;
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 		for (i = 0; !h->noflush && i < verbose; i++)
212 			add_argv(&state->av_store, "-v", 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",
251 				xt_params->program_name, h->error.lineno);
252 		if (errno)
253 			fprintf(stderr,	": %s.", nft_strerror(errno));
254 		fprintf(stderr, "\n");
255 		exit(1);
256 	}
257 }
258 
xtables_restore_parse(struct nft_handle * h,const struct nft_xt_restore_parse * p)259 void xtables_restore_parse(struct nft_handle *h,
260 			   const struct nft_xt_restore_parse *p)
261 {
262 	struct nft_xt_restore_state state = {};
263 	char buffer[10240] = {};
264 
265 	if (!verbose && !h->noflush)
266 		nft_cache_level_set(h, NFT_CL_FAKE, NULL);
267 
268 	line = 0;
269 	while (fgets(buffer, sizeof(buffer), p->in)) {
270 		h->error.lineno = ++line;
271 		DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
272 		xtables_restore_parse_line(h, p, &state, buffer);
273 	}
274 	if (state.in_table && p->commit) {
275 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
276 				xt_params->program_name, line + 1);
277 		exit(1);
278 	} else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
279 		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
280 			      xt_params->program_name);
281 	}
282 }
283 
284 static int
xtables_restore_main(int family,const char * progname,int argc,char * argv[])285 xtables_restore_main(int family, const char *progname, int argc, char *argv[])
286 {
287 	struct nft_xt_restore_parse p = {
288 		.commit = true,
289 		.cb = &restore_cb,
290 	};
291 	bool noflush = false;
292 	struct nft_handle h;
293 	int c;
294 
295 	line = 0;
296 
297 	xtables_globals.program_name = progname;
298 	c = xtables_init_all(&xtables_globals, family);
299 	if (c < 0) {
300 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
301 				xtables_globals.program_name,
302 				xtables_globals.program_version);
303 		exit(1);
304 	}
305 
306 	while ((c = getopt_long(argc, argv, "bcvVthnM:T:wW", options, NULL)) != -1) {
307 		switch (c) {
308 			case 'b':
309 				fprintf(stderr, "-b/--binary option is not implemented\n");
310 				break;
311 			case 'c':
312 				counters = 1;
313 				break;
314 			case 'v':
315 				verbose++;
316 				break;
317 			case 'V':
318 				printf("%s v%s\n", prog_name, prog_vers);
319 				exit(0);
320 			case 't':
321 				p.testing = 1;
322 				break;
323 			case 'h':
324 				print_usage(prog_name, PACKAGE_VERSION);
325 				exit(0);
326 			case 'n':
327 				noflush = true;
328 				break;
329 			case 'M':
330 				xtables_modprobe_program = optarg;
331 				break;
332 			case 'T':
333 				p.tablename = optarg;
334 				break;
335 			case 'w': /* fallthrough.  Ignored by xt-restore */
336 			case 'W':
337 				if (!optarg && xs_has_arg(argc, argv))
338 					optind++;
339 				break;
340 			default:
341 				fprintf(stderr,
342 					"Try `%s -h' for more information.\n",
343 					prog_name);
344 				exit(1);
345 		}
346 	}
347 
348 	if (optind == argc - 1) {
349 		p.in = fopen(argv[optind], "re");
350 		if (!p.in) {
351 			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
352 				strerror(errno));
353 			exit(1);
354 		}
355 	} else if (optind < argc) {
356 		fprintf(stderr, "Unknown arguments found on commandline\n");
357 		exit(1);
358 	} else {
359 		p.in = stdin;
360 	}
361 
362 	init_extensions();
363 	switch (family) {
364 	case NFPROTO_IPV4:
365 		init_extensions4();
366 		break;
367 	case NFPROTO_IPV6:
368 		init_extensions6();
369 		break;
370 	case NFPROTO_ARP:
371 		init_extensionsa();
372 		break;
373 	case NFPROTO_BRIDGE:
374 		init_extensionsb();
375 		break;
376 	default:
377 		fprintf(stderr, "Unknown family %d\n", family);
378 		return 1;
379 	}
380 
381 	if (nft_init(&h, family) < 0) {
382 		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
383 				xtables_globals.program_name,
384 				xtables_globals.program_version,
385 				strerror(errno));
386 		exit(EXIT_FAILURE);
387 	}
388 	h.noflush = noflush;
389 	h.restore = true;
390 
391 	xtables_restore_parse(&h, &p);
392 
393 	nft_fini(&h);
394 	xtables_fini();
395 	fclose(p.in);
396 	return 0;
397 }
398 
xtables_ip4_restore_main(int argc,char * argv[])399 int xtables_ip4_restore_main(int argc, char *argv[])
400 {
401 	return xtables_restore_main(NFPROTO_IPV4, basename(*argv),
402 				    argc, argv);
403 }
404 
xtables_ip6_restore_main(int argc,char * argv[])405 int xtables_ip6_restore_main(int argc, char *argv[])
406 {
407 	return xtables_restore_main(NFPROTO_IPV6, basename(*argv),
408 				    argc, argv);
409 }
410 
411 static const struct nft_xt_restore_cb ebt_restore_cb = {
412 	.commit		= nft_bridge_commit,
413 	.table_flush	= nft_cmd_table_flush,
414 	.do_command	= do_commandeb,
415 	.chain_set	= nft_cmd_chain_set,
416 	.chain_restore  = nft_cmd_chain_restore,
417 };
418 
419 static const struct option ebt_restore_options[] = {
420 	{.name = "noflush", .has_arg = 0, .val = 'n'},
421 	{.name = "verbose", .has_arg = 0, .val = 'v'},
422 	{ 0 }
423 };
424 
xtables_eb_restore_main(int argc,char * argv[])425 int xtables_eb_restore_main(int argc, char *argv[])
426 {
427 	struct nft_xt_restore_parse p = {
428 		.in = stdin,
429 		.cb = &ebt_restore_cb,
430 	};
431 	bool noflush = false;
432 	struct nft_handle h;
433 	int c;
434 
435 	while ((c = getopt_long(argc, argv, "nv",
436 				ebt_restore_options, NULL)) != -1) {
437 		switch(c) {
438 		case 'n':
439 			noflush = 1;
440 			break;
441 		case 'v':
442 			verbose++;
443 			break;
444 		default:
445 			fprintf(stderr,
446 				"Usage: ebtables-restore [ --verbose ] [ --noflush ]\n");
447 			exit(1);
448 			break;
449 		}
450 	}
451 
452 	nft_init_eb(&h, "ebtables-restore");
453 	h.noflush = noflush;
454 	xtables_restore_parse(&h, &p);
455 	nft_fini_eb(&h);
456 
457 	return 0;
458 }
459 
460 static const struct nft_xt_restore_cb arp_restore_cb = {
461 	.commit		= nft_commit,
462 	.table_flush	= nft_cmd_table_flush,
463 	.do_command	= do_commandx,
464 	.chain_set	= nft_cmd_chain_set,
465 	.chain_restore  = nft_cmd_chain_restore,
466 };
467 
xtables_arp_restore_main(int argc,char * argv[])468 int xtables_arp_restore_main(int argc, char *argv[])
469 {
470 	struct nft_xt_restore_parse p = {
471 		.in = stdin,
472 		.cb = &arp_restore_cb,
473 	};
474 	struct nft_handle h;
475 
476 	nft_init_arp(&h, "arptables-restore");
477 	xtables_restore_parse(&h, &p);
478 	nft_fini(&h);
479 	xtables_fini();
480 
481 	return 0;
482 }
483