iptables-restore.c 10.6 KB
Newer Older
1
2
3
4
5
6
/* Code to restore the iptables state, from file by iptables-save.
 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
 * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
 *
 * This code is distributed under the terms of GNU GPL v2
 */
7
#include "config.h"
8
#include <getopt.h>
9
#include <errno.h>
10
11
12
13
14
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "iptables.h"
15
#include "ip6tables.h"
16
#include "xshared.h"
17
18
#include "xtables.h"
#include "libiptc/libiptc.h"
19
#include "libiptc/libip6tc.h"
20
#include "iptables-multi.h"
21
#include "ip6tables-multi.h"
22

23
24
25
26
27
static int counters, verbose, noflush, wait;

static struct timeval wait_interval = {
	.tv_sec	= 1,
};
28
29
30

/* Keeping track of external matches and targets.  */
static const struct option options[] = {
31
32
33
34
35
36
37
38
39
40
	{.name = "counters",      .has_arg = 0, .val = 'c'},
	{.name = "verbose",       .has_arg = 0, .val = 'v'},
	{.name = "version",       .has_arg = 0, .val = 'V'},
	{.name = "test",          .has_arg = 0, .val = 't'},
	{.name = "help",          .has_arg = 0, .val = 'h'},
	{.name = "noflush",       .has_arg = 0, .val = 'n'},
	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
	{.name = "table",         .has_arg = 1, .val = 'T'},
	{.name = "wait",          .has_arg = 2, .val = 'w'},
	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
41
42
43
44
45
	{NULL},
};

static void print_usage(const char *name, const char *version)
{
46
	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command] [file]\n"
47
48
			"	   [ --counters ]\n"
			"	   [ --verbose ]\n"
49
			"	   [ --version]\n"
50
51
52
			"	   [ --test ]\n"
			"	   [ --help ]\n"
			"	   [ --noflush ]\n"
53
54
			"	   [ --wait=<seconds>\n"
			"	   [ --wait-interval=<usecs>\n"
55
			"	   [ --table=<TABLE> ]\n"
56
			"	   [ --modprobe=<command> ]\n", name);
57
58
}

59
60
61
62
63
64
65
66
67
68
69
70
71
72
struct iptables_restore_cb {
	const struct xtc_ops *ops;

	int (*for_each_chain)(int (*fn)(const xt_chainlabel,
					int, struct xtc_handle *),
			      int verbose, int builtinstoo,
			      struct xtc_handle *handle);
	int (*flush_entries)(const xt_chainlabel, int, struct xtc_handle *);
	int (*delete_chain)(const xt_chainlabel, int, struct xtc_handle *);
	int (*do_command)(int argc, char *argv[], char **table,
			  struct xtc_handle **handle, bool restore);
};

static struct xtc_handle *
73
create_handle(const struct iptables_restore_cb *cb, const char *tablename)
74
75
76
{
	struct xtc_handle *handle;

77
	handle = cb->ops->init(tablename);
78
79
80
81

	if (!handle) {
		/* try to insmod the module if iptc_init failed */
		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
90
91
	return handle;
}

92
static int
93
94
ip46tables_restore_main(const struct iptables_restore_cb *cb,
			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
102
103
104
105
	FILE *in;
	int in_table = 0, testing = 0;
	const char *tablename = NULL;

	line = 0;
106
	lock = XT_LOCK_NOT_ACQUIRED;
107

108
	while ((c = getopt_long(argc, argv, "bcvVthnwWM:T:", options, NULL)) != -1) {
109
110
		switch (c) {
			case 'b':
111
				fprintf(stderr, "-b/--binary option is not implemented\n");
112
113
114
115
116
117
118
				break;
			case 'c':
				counters = 1;
				break;
			case 'v':
				verbose = 1;
				break;
119
			case 'V':
120
121
122
				printf("%s v%s (legacy)\n",
				       xt_params->program_name,
				       xt_params->program_version);
123
				exit(0);
124
125
126
127
			case 't':
				testing = 1;
				break;
			case 'h':
128
				print_usage(xt_params->program_name,
129
					    PACKAGE_VERSION);
130
				exit(0);
131
132
133
			case 'n':
				noflush = 1;
				break;
134
135
136
137
138
139
			case 'w':
				wait = parse_wait_time(argc, argv);
				break;
			case 'W':
				parse_wait_interval(argc, argv, &wait_interval);
				break;
140
141
142
143
144
145
			case 'M':
				xtables_modprobe_program = optarg;
				break;
			case 'T':
				tablename = optarg;
				break;
146
147
			default:
				fprintf(stderr,
148
149
					"Try `%s -h' for more information.\n",
					xt_params->program_name);
150
				exit(1);
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
		}
	}

	if (optind == argc - 1) {
		in = fopen(argv[optind], "re");
		if (!in) {
			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
				strerror(errno));
			exit(1);
		}
	}
	else if (optind < argc) {
		fprintf(stderr, "Unknown arguments found on commandline\n");
		exit(1);
	}
	else in = stdin;

168
169
170
171
172
	if (!wait_interval.tv_sec && !wait) {
		fprintf(stderr, "Option --wait-interval requires option --wait\n");
		exit(1);
	}

173
174
175
176
177
178
179
180
	/* Grab standard input. */
	while (fgets(buffer, sizeof(buffer), in)) {
		int ret = 0;

		line++;
		if (buffer[0] == '\n')
			continue;
		else if (buffer[0] == '#') {
181
			if (verbose) {
182
				fputs(buffer, stdout);
183
184
				fflush(stdout);
			}
185
186
187
188
			continue;
		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
			if (!testing) {
				DEBUGP("Calling commit\n");
189
190
				ret = cb->ops->commit(handle);
				cb->ops->free(handle);
191
192
193
194
195
				handle = NULL;
			} else {
				DEBUGP("Not calling commit, testing\n");
				ret = 1;
			}
196
197
198
199
200
201
202

			/* Done with the current table, release the lock. */
			if (lock >= 0) {
				xtables_unlock(lock);
				lock = XT_LOCK_NOT_ACQUIRED;
			}

203
204
			in_table = 0;
		} else if ((buffer[0] == '*') && (!in_table)) {
205
206
207
			/* Acquire a lock before we create a new table handle */
			lock = xtables_lock_or_exit(wait, &wait_interval);

208
209
210
211
212
			/* New table */
			char *table;

			table = strtok(buffer+1, " \t\n");
			DEBUGP("line %u, table '%s'\n", line, table);
213
			if (!table)
214
215
216
				xtables_error(PARAMETER_PROBLEM,
					"%s: line %u table name invalid\n",
					xt_params->program_name, line);
217

218
219
220
			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
			curtable[XT_TABLE_MAXNAMELEN] = '\0';

221
			if (tablename && strcmp(tablename, table) != 0) {
222
223
224
225
				if (lock >= 0) {
					xtables_unlock(lock);
					lock = XT_LOCK_NOT_ACQUIRED;
				}
226
				continue;
227
			}
228
			if (handle)
229
				cb->ops->free(handle);
230

231
			handle = create_handle(cb, table);
232
233
234
			if (noflush == 0) {
				DEBUGP("Cleaning all chains of table '%s'\n",
					table);
235
				cb->for_each_chain(cb->flush_entries, verbose, 1,
236
237
238
239
						handle);

				DEBUGP("Deleting all user-defined chains "
				       "of table '%s'\n", table);
240
				cb->for_each_chain(cb->delete_chain, verbose, 0,
241
242
243
244
245
246
247
248
249
250
251
252
						handle);
			}

			ret = 1;
			in_table = 1;

		} else if ((buffer[0] == ':') && (in_table)) {
			/* New chain. */
			char *policy, *chain;

			chain = strtok(buffer+1, " \t\n");
			DEBUGP("line %u, chain '%s'\n", line, chain);
253
			if (!chain)
254
255
256
257
258
259
260
261
262
263
				xtables_error(PARAMETER_PROBLEM,
					   "%s: line %u chain name invalid\n",
					   xt_params->program_name, line);

			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
				xtables_error(PARAMETER_PROBLEM,
					   "Invalid chain name `%s' "
					   "(%u chars max)",
					   chain, XT_EXTENSION_MAXNAMELEN - 1);

264
265
			if (cb->ops->builtin(chain, handle) <= 0) {
				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
269
270
271
272
273
						xtables_error(PARAMETER_PROBLEM,
							   "error flushing chain "
							   "'%s':%s\n", chain,
							   strerror(errno));
				} else {
					DEBUGP("Creating new chain '%s'\n", chain);
274
					if (!cb->ops->create_chain(chain, handle))
275
276
277
278
279
280
281
282
283
						xtables_error(PARAMETER_PROBLEM,
							   "error creating chain "
							   "'%s':%s\n", chain,
							   strerror(errno));
				}
			}

			policy = strtok(NULL, " \t\n");
			DEBUGP("line %u, policy '%s'\n", line, policy);
284
			if (!policy)
285
286
287
288
289
				xtables_error(PARAMETER_PROBLEM,
					   "%s: line %u policy invalid\n",
					   xt_params->program_name, line);

			if (strcmp(policy, "-") != 0) {
290
				struct xt_counters count = {};
291
292
293
294
295
296
297

				if (counters) {
					char *ctrs;
					ctrs = strtok(NULL, " \t\n");

					if (!ctrs || !parse_counters(ctrs, &count))
						xtables_error(PARAMETER_PROBLEM,
298
299
							  "invalid policy counters "
							  "for chain '%s'\n", chain);
300
301
302
303
304
				}

				DEBUGP("Setting policy of chain %s to %s\n",
					chain, policy);

305
				if (!cb->ops->set_policy(chain, policy, &count,
306
307
308
309
310
						     handle))
					xtables_error(OTHER_PROBLEM,
						"Can't set policy `%s'"
						" on `%s' line %u: %s\n",
						policy, chain, line,
311
						cb->ops->strerror(errno));
312
313
314
315
316
317
318
			}

			ret = 1;

		} else if (in_table) {
			char *pcnt = NULL;
			char *bcnt = NULL;
319
			char *parsestart = buffer;
320

321
322
323
			add_argv(&av_store, argv[0], 0);
			add_argv(&av_store, "-t", 0);
			add_argv(&av_store, curtable, 0);
324

325
			tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
326
			if (counters && pcnt && bcnt) {
327
328
329
				add_argv(&av_store, "--set-counters", 0);
				add_argv(&av_store, pcnt, 0);
				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
336
				av_store.argc, curtable);
			debug_print_argv(&av_store);
337

338
339
			ret = cb->do_command(av_store.argc, av_store.argv,
					 &av_store.argv[2], &handle, true);
340

341
			free_argv(&av_store);
342
343
			fflush(stdout);
		}
344
		if (tablename && strcmp(tablename, curtable) != 0)
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
			continue;
		if (!ret) {
			fprintf(stderr, "%s: line %u failed\n",
					xt_params->program_name, line);
			exit(1);
		}
	}
	if (in_table) {
		fprintf(stderr, "%s: COMMIT expected at line %u\n",
				xt_params->program_name, line + 1);
		exit(1);
	}

	fclose(in);
	return 0;
}
361
362
363


#if defined ENABLE_IPV4
364
static const struct iptables_restore_cb ipt_restore_cb = {
365
366
367
368
369
370
371
372
373
374
	.ops		= &iptc_ops,
	.for_each_chain	= for_each_chain4,
	.flush_entries	= flush_entries4,
	.delete_chain	= delete_chain4,
	.do_command	= do_command4,
};

int
iptables_restore_main(int argc, char *argv[])
{
375
	int c, ret;
376
377
378
379
380
381
382
383
384
385
386
387
388
389

	iptables_globals.program_name = "iptables-restore";
	c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
	if (c < 0) {
		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
				iptables_globals.program_name,
				iptables_globals.program_version);
		exit(1);
	}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
	init_extensions();
	init_extensions4();
#endif

390
391
392
393
	ret = ip46tables_restore_main(&ipt_restore_cb, argc, argv);

	xtables_fini();
	return ret;
394
395
396
397
}
#endif

#if defined ENABLE_IPV6
398
static const struct iptables_restore_cb ip6t_restore_cb = {
399
400
401
402
403
404
405
406
407
408
	.ops		= &ip6tc_ops,
	.for_each_chain	= for_each_chain6,
	.flush_entries	= flush_entries6,
	.delete_chain	= delete_chain6,
	.do_command	= do_command6,
};

int
ip6tables_restore_main(int argc, char *argv[])
{
409
	int c, ret;
410
411
412
413
414
415
416
417
418
419
420
421
422
423

	ip6tables_globals.program_name = "ip6tables-restore";
	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
	if (c < 0) {
		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
				ip6tables_globals.program_name,
				ip6tables_globals.program_version);
		exit(1);
	}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
	init_extensions();
	init_extensions6();
#endif

424
425
426
427
	ret = ip46tables_restore_main(&ip6t_restore_cb, argc, argv);

	xtables_fini();
	return ret;
428
429
}
#endif