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