xref: /aosp_15_r20/external/iptables/iptables/iptables-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 <stdbool.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include "iptables.h"
15 #include "ip6tables.h"
16 #include "xshared.h"
17 #include "xtables.h"
18 #include "libiptc/libiptc.h"
19 #include "libiptc/libip6tc.h"
20 #include "iptables-multi.h"
21 #include "ip6tables-multi.h"
22 
23 static int counters, verbose, noflush, wait;
24 
25 /* Keeping track of external matches and targets.  */
26 static const struct option options[] = {
27 	{.name = "counters",      .has_arg = 0, .val = 'c'},
28 	{.name = "verbose",       .has_arg = 0, .val = 'v'},
29 	{.name = "version",       .has_arg = 0, .val = 'V'},
30 	{.name = "test",          .has_arg = 0, .val = 't'},
31 	{.name = "help",          .has_arg = 0, .val = 'h'},
32 	{.name = "noflush",       .has_arg = 0, .val = 'n'},
33 	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
34 	{.name = "table",         .has_arg = 1, .val = 'T'},
35 	{.name = "wait",          .has_arg = 2, .val = 'w'},
36 	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
37 	{NULL},
38 };
39 
print_usage(const char * name,const char * version)40 static void print_usage(const char *name, const char *version)
41 {
42 	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command] [file]\n"
43 			"	   [ --counters ]\n"
44 			"	   [ --verbose ]\n"
45 			"	   [ --version]\n"
46 			"	   [ --test ]\n"
47 			"	   [ --help ]\n"
48 			"	   [ --noflush ]\n"
49 			"	   [ --wait=<seconds>\n"
50 			"	   [ --table=<TABLE> ]\n"
51 			"	   [ --modprobe=<command> ]\n", name);
52 }
53 
54 struct iptables_restore_cb {
55 	const struct xtc_ops *ops;
56 
57 	int (*for_each_chain)(int (*fn)(const xt_chainlabel,
58 					int, struct xtc_handle *),
59 			      int verbose, int builtinstoo,
60 			      struct xtc_handle *handle);
61 	int (*flush_entries)(const xt_chainlabel, int, struct xtc_handle *);
62 	int (*delete_chain)(const xt_chainlabel, int, struct xtc_handle *);
63 	int (*do_command)(int argc, char *argv[], char **table,
64 			  struct xtc_handle **handle, bool restore);
65 };
66 
67 static struct xtc_handle *
create_handle(const struct iptables_restore_cb * cb,const char * tablename)68 create_handle(const struct iptables_restore_cb *cb, const char *tablename)
69 {
70 	struct xtc_handle *handle;
71 
72 	handle = cb->ops->init(tablename);
73 
74 	if (!handle) {
75 		/* try to insmod the module if iptc_init failed */
76 		xtables_load_ko(xtables_modprobe_program, false);
77 		handle = cb->ops->init(tablename);
78 	}
79 
80 	if (!handle)
81 		xtables_error(PARAMETER_PROBLEM,
82 			      "%s: unable to initialize table '%s'",
83 			      xt_params->program_name, tablename);
84 
85 	return handle;
86 }
87 
88 static int
ip46tables_restore_main(const struct iptables_restore_cb * cb,int argc,char * argv[])89 ip46tables_restore_main(const struct iptables_restore_cb *cb,
90 			int argc, char *argv[])
91 {
92 	struct xtc_handle *handle = NULL;
93 	struct argv_store av_store = {};
94 	char buffer[10240];
95 	int c, lock;
96 	char curtable[XT_TABLE_MAXNAMELEN + 1] = {};
97 	FILE *in;
98 	int in_table = 0, testing = 0;
99 	const char *tablename = NULL;
100 	bool wait_interval_set = false;
101 
102 	line = 0;
103 	lock = XT_LOCK_NOT_ACQUIRED;
104 
105 	while ((c = getopt_long(argc, argv, "bcvVthnwWM:T:", options, NULL)) != -1) {
106 		switch (c) {
107 			case 'b':
108 				fprintf(stderr, "-b/--binary option is not implemented\n");
109 				break;
110 			case 'c':
111 				counters = 1;
112 				break;
113 			case 'v':
114 				verbose++;
115 				break;
116 			case 'V':
117 				printf("%s v%s\n",
118 				       xt_params->program_name,
119 				       xt_params->program_version);
120 				exit(0);
121 			case 't':
122 				testing = 1;
123 				break;
124 			case 'h':
125 				print_usage(xt_params->program_name,
126 					    PACKAGE_VERSION);
127 				exit(0);
128 			case 'n':
129 				noflush = 1;
130 				break;
131 			case 'w':
132 				wait = parse_wait_time(argc, argv);
133 				break;
134 			case 'W':
135 				parse_wait_interval(argc, argv);
136 				wait_interval_set = true;
137 				break;
138 			case 'M':
139 				xtables_modprobe_program = optarg;
140 				break;
141 			case 'T':
142 				tablename = optarg;
143 				break;
144 			default:
145 				fprintf(stderr,
146 					"Try `%s -h' for more information.\n",
147 					xt_params->program_name);
148 				exit(1);
149 		}
150 	}
151 
152 	if (optind == argc - 1) {
153 		in = fopen(argv[optind], "re");
154 		if (!in) {
155 			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
156 				strerror(errno));
157 			exit(1);
158 		}
159 	}
160 	else if (optind < argc) {
161 		fprintf(stderr, "Unknown arguments found on commandline\n");
162 		exit(1);
163 	}
164 	else in = stdin;
165 
166 	if (wait_interval_set && !wait) {
167 		fprintf(stderr, "Option --wait-interval requires option --wait\n");
168 		exit(1);
169 	}
170 
171 	/* Grab standard input. */
172 	while (fgets(buffer, sizeof(buffer), in)) {
173 		int ret = 0;
174 
175 		line++;
176 		if (buffer[0] == '\n')
177 			continue;
178 		else if (buffer[0] == '#') {
179 			if (verbose) {
180 				fputs(buffer, stdout);
181 				fflush(stdout);
182 			}
183 			continue;
184 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
185 			if (!testing) {
186 				DEBUGP("Calling commit\n");
187 				ret = cb->ops->commit(handle);
188 			} else {
189 				DEBUGP("Not calling commit, testing\n");
190 				ret = 1;
191 			}
192 			cb->ops->free(handle);
193 			handle = NULL;
194 
195 			/* Done with the current table, release the lock. */
196 			if (lock >= 0) {
197 				xtables_unlock(lock);
198 				lock = XT_LOCK_NOT_ACQUIRED;
199 			}
200 
201 			in_table = 0;
202 		} else if ((buffer[0] == '*') && (!in_table)) {
203 			/* Acquire a lock before we create a new table handle */
204 			lock = xtables_lock_or_exit(wait);
205 
206 			/* New table */
207 			char *table;
208 
209 			table = strtok(buffer+1, " \t\n");
210 			DEBUGP("line %u, table '%s'\n", line, table);
211 			if (!table)
212 				xtables_error(PARAMETER_PROBLEM,
213 					      "%s: line %u table name invalid",
214 					      xt_params->program_name, line);
215 
216 			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
217 			curtable[XT_TABLE_MAXNAMELEN] = '\0';
218 
219 			if (tablename && strcmp(tablename, table) != 0) {
220 				if (lock >= 0) {
221 					xtables_unlock(lock);
222 					lock = XT_LOCK_NOT_ACQUIRED;
223 				}
224 				continue;
225 			}
226 
227 			handle = create_handle(cb, table);
228 			if (noflush == 0) {
229 				DEBUGP("Cleaning all chains of table '%s'\n",
230 					table);
231 				cb->for_each_chain(cb->flush_entries, verbose, 1,
232 						handle);
233 
234 				DEBUGP("Deleting all user-defined chains "
235 				       "of table '%s'\n", table);
236 				cb->for_each_chain(cb->delete_chain, verbose, 0,
237 						handle);
238 			}
239 
240 			ret = 1;
241 			in_table = 1;
242 
243 		} else if ((buffer[0] == ':') && (in_table)) {
244 			/* New chain. */
245 			char *policy, *chain;
246 
247 			chain = strtok(buffer+1, " \t\n");
248 			DEBUGP("line %u, chain '%s'\n", line, chain);
249 			if (!chain)
250 				xtables_error(PARAMETER_PROBLEM,
251 					      "%s: line %u chain name invalid",
252 					      xt_params->program_name, line);
253 
254 			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
255 				xtables_error(PARAMETER_PROBLEM,
256 					   "Invalid chain name `%s' "
257 					   "(%u chars max)",
258 					   chain, XT_EXTENSION_MAXNAMELEN - 1);
259 
260 			if (cb->ops->builtin(chain, handle) <= 0) {
261 				if (noflush && cb->ops->is_chain(chain, handle)) {
262 					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
263 					if (!cb->ops->flush_entries(chain, handle))
264 						xtables_error(PARAMETER_PROBLEM,
265 							      "error flushing chain '%s':%s",
266 							      chain, strerror(errno));
267 				} else {
268 					DEBUGP("Creating new chain '%s'\n", chain);
269 					if (!cb->ops->create_chain(chain, handle))
270 						xtables_error(PARAMETER_PROBLEM,
271 							      "error creating chain '%s':%s",
272 							      chain, strerror(errno));
273 				}
274 			}
275 
276 			policy = strtok(NULL, " \t\n");
277 			DEBUGP("line %u, policy '%s'\n", line, policy);
278 			if (!policy)
279 				xtables_error(PARAMETER_PROBLEM,
280 					      "%s: line %u policy invalid",
281 					      xt_params->program_name, line);
282 
283 			if (strcmp(policy, "-") != 0) {
284 				char *ctrs = strtok(NULL, " \t\n");
285 				struct xt_counters count = {};
286 
287 				if ((!ctrs && counters) ||
288 				    (ctrs && !parse_counters(ctrs, &count)))
289 					xtables_error(PARAMETER_PROBLEM,
290 						      "invalid policy counters for chain '%s'",
291 						      chain);
292 
293 				DEBUGP("Setting policy of chain %s to %s\n",
294 					chain, policy);
295 
296 				if (!cb->ops->set_policy(chain, policy,
297 							 counters ? &count : NULL,
298 							 handle))
299 					xtables_error(OTHER_PROBLEM,
300 						      "Can't set policy `%s' on `%s' line %u: %s",
301 						      policy, chain, line,
302 						      cb->ops->strerror(errno));
303 			}
304 
305 			xtables_announce_chain(chain);
306 			ret = 1;
307 
308 		} else if (in_table) {
309 			char *pcnt = NULL;
310 			char *bcnt = NULL;
311 			char *parsestart = buffer;
312 			int i;
313 
314 			add_argv(&av_store, argv[0], 0);
315 			add_argv(&av_store, "-t", 0);
316 			add_argv(&av_store, curtable, 0);
317 
318 			for (i = 0; !noflush && i < verbose; i++)
319 				add_argv(&av_store, "-v", 0);
320 
321 			tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
322 			if (counters && pcnt && bcnt) {
323 				add_argv(&av_store, "--set-counters", 0);
324 				add_argv(&av_store, pcnt, 0);
325 				add_argv(&av_store, bcnt, 0);
326 			}
327 
328 			add_param_to_argv(&av_store, parsestart, line);
329 
330 			DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
331 				av_store.argc, curtable);
332 			debug_print_argv(&av_store);
333 
334 			ret = cb->do_command(av_store.argc, av_store.argv,
335 					 &av_store.argv[2], &handle, true);
336 
337 			free_argv(&av_store);
338 			fflush(stdout);
339 		}
340 		if (tablename && strcmp(tablename, curtable) != 0)
341 			continue;
342 		if (!ret) {
343 			fprintf(stderr, "%s: line %u failed\n",
344 					xt_params->program_name, line);
345 			exit(1);
346 		}
347 	}
348 	if (in_table) {
349 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
350 				xt_params->program_name, line + 1);
351 		exit(1);
352 	}
353 
354 	fclose(in);
355 	return 0;
356 }
357 
358 
359 #if defined ENABLE_IPV4
360 static const struct iptables_restore_cb ipt_restore_cb = {
361 	.ops		= &iptc_ops,
362 	.for_each_chain	= for_each_chain4,
363 	.flush_entries	= flush_entries4,
364 	.delete_chain	= delete_chain4,
365 	.do_command	= do_command4,
366 };
367 
368 int
iptables_restore_main(int argc,char * argv[])369 iptables_restore_main(int argc, char *argv[])
370 {
371 	int c, ret;
372 
373 	iptables_globals.program_name = "iptables-restore";
374 	c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
375 	if (c < 0) {
376 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
377 				iptables_globals.program_name,
378 				iptables_globals.program_version);
379 		exit(1);
380 	}
381 	init_extensions();
382 	init_extensions4();
383 
384 	ret = ip46tables_restore_main(&ipt_restore_cb, argc, argv);
385 
386 	xtables_fini();
387 	return ret;
388 }
389 #endif
390 
391 #if defined ENABLE_IPV6
392 static const struct iptables_restore_cb ip6t_restore_cb = {
393 	.ops		= &ip6tc_ops,
394 	.for_each_chain	= for_each_chain6,
395 	.flush_entries	= flush_entries6,
396 	.delete_chain	= delete_chain6,
397 	.do_command	= do_command6,
398 };
399 
400 int
ip6tables_restore_main(int argc,char * argv[])401 ip6tables_restore_main(int argc, char *argv[])
402 {
403 	int c, ret;
404 
405 	ip6tables_globals.program_name = "ip6tables-restore";
406 	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
407 	if (c < 0) {
408 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
409 				ip6tables_globals.program_name,
410 				ip6tables_globals.program_version);
411 		exit(1);
412 	}
413 	init_extensions();
414 	init_extensions6();
415 
416 	ret = ip46tables_restore_main(&ip6t_restore_cb, argc, argv);
417 
418 	xtables_fini();
419 	return ret;
420 }
421 #endif
422