xtables-restore.c 11.9 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
9
#include <getopt.h>
#include <errno.h>
10
#include <libgen.h>
11
12
13
14
15
16
17
18
19
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "iptables.h"
#include "xtables.h"
#include "libiptc/libiptc.h"
#include "xtables-multi.h"
#include "nft.h"
20
#include "nft-bridge.h"
21
#include "nft-cache.h"
22
23
#include <libnftnl/chain.h>

24
static int counters, verbose;
25
26
27
28
29

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

#define prog_name xtables_globals.program_name
44
#define prog_vers xtables_globals.program_version
45
46
47

static void print_usage(const char *name, const char *version)
{
48
	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6] [file]\n"
49
50
			"	   [ --counters ]\n"
			"	   [ --verbose ]\n"
51
			"	   [ --version]\n"
52
53
54
55
			"	   [ --test ]\n"
			"	   [ --help ]\n"
			"	   [ --noflush ]\n"
			"	   [ --table=<TABLE> ]\n"
56
			"	   [ --modprobe=<command> ]\n"
57
58
			"	   [ --ipv4 ]\n"
			"	   [ --ipv6 ]\n", name);
59
60
}

61
static const struct nft_xt_restore_cb restore_cb = {
62
63
	.commit		= nft_commit,
	.abort		= nft_abort,
64
65
	.table_new	= nft_cmd_table_new,
	.table_flush	= nft_cmd_table_flush,
66
	.do_command	= do_commandx,
67
68
	.chain_set	= nft_cmd_chain_set,
	.chain_restore  = nft_cmd_chain_restore,
69
70
};

71
72
73
74
struct nft_xt_restore_state {
	const struct builtin_table *curtable;
	struct argv_store av_store;
	bool in_table;
75
76
};

77
78
79
80
static void xtables_restore_parse_line(struct nft_handle *h,
				       const struct nft_xt_restore_parse *p,
				       struct nft_xt_restore_state *state,
				       char *buffer)
81
{
82
83
84
85
86
87
	const struct nft_xt_restore_cb *cb = p->cb;
	int ret = 0;

	if (buffer[0] == '\n')
		return;
	else if (buffer[0] == '#') {
88
		if (verbose) {
89
			fputs(buffer, stdout);
90
91
			fflush(stdout);
		}
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
		return;
	} else if (state->in_table &&
		   (strncmp(buffer, "COMMIT", 6) == 0) &&
		   (buffer[6] == '\0' || buffer[6] == '\n')) {
		if (!p->testing) {
			/* Commit per table, although we support
			 * global commit at once, stick by now to
			 * the existing behaviour.
			 */
			DEBUGP("Calling commit\n");
			if (cb->commit)
				ret = cb->commit(h);
		} else {
			DEBUGP("Not calling commit, testing\n");
			if (cb->abort)
				ret = cb->abort(h);
		}
		state->in_table = false;

	} else if ((buffer[0] == '*') && (!state->in_table || !p->commit)) {
		/* New table */
		char *table;

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

		state->curtable = nft_table_builtin_find(h, table);
		if (!state->curtable)
			xtables_error(PARAMETER_PROBLEM,
				"%s: line %u table name '%s' invalid\n",
				xt_params->program_name, line, table);

		if (p->tablename && (strcmp(p->tablename, table) != 0))
			return;

131
132
133
134
		/* implicit commit if no explicit COMMIT supported */
		if (!p->commit)
			cb->commit(h);

135
136
137
138
139
		if (h->noflush == 0) {
			DEBUGP("Cleaning all chains of table '%s'\n", table);
			if (cb->table_flush)
				cb->table_flush(h, table);
		}
140

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
		ret = 1;
		state->in_table = true;

		if (cb->table_new)
			cb->table_new(h, table);

	} else if ((buffer[0] == ':') && state->in_table) {
		/* New chain. */
		char *policy, *chain = NULL;
		struct xt_counters count = {};

		chain = strtok(buffer+1, " \t\n");
		DEBUGP("line %u, chain '%s'\n", line, chain);
		if (!chain)
			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);

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

		if (nft_chain_builtin_find(state->curtable, chain)) {
			if (counters) {
				char *ctrs;
				ctrs = strtok(NULL, " \t\n");

				if (!ctrs || !parse_counters(ctrs, &count))
					xtables_error(PARAMETER_PROBLEM,
						   "invalid policy counters for chain '%s'\n",
						   chain);
180
181

			}
182
183
184
185
186
187
188
			if (cb->chain_set &&
			    cb->chain_set(h, state->curtable->name,
					  chain, policy, &count) < 0) {
				xtables_error(OTHER_PROBLEM,
					      "Can't set policy `%s' on `%s' line %u: %s\n",
					      policy, chain, line,
					      strerror(errno));
189
			}
190
191
192
193
194
195
196
197
			DEBUGP("Setting policy of chain %s to %s\n",
			       chain, policy);
		} else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
			   errno != EEXIST) {
			xtables_error(PARAMETER_PROBLEM,
				      "cannot create chain '%s' (%s)\n",
				      chain, strerror(errno));
		} else if (h->family == NFPROTO_BRIDGE &&
198
			   !ebt_cmd_user_chain_policy(h, state->curtable->name,
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
						      chain, policy)) {
			xtables_error(OTHER_PROBLEM,
				      "Can't set policy `%s' on `%s' line %u: %s\n",
				      policy, chain, line,
				      strerror(errno));
		}
		ret = 1;
	} else if (state->in_table) {
		char *pcnt = NULL;
		char *bcnt = NULL;
		char *parsestart = buffer;

		add_argv(&state->av_store, xt_params->program_name, 0);
		add_argv(&state->av_store, "-t", 0);
		add_argv(&state->av_store, state->curtable->name, 0);

		tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
		if (counters && pcnt && bcnt) {
			add_argv(&state->av_store, "--set-counters", 0);
			add_argv(&state->av_store, pcnt, 0);
			add_argv(&state->av_store, bcnt, 0);
		}
221

222
		add_param_to_argv(&state->av_store, parsestart, line);
223

224
225
226
		DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
		       state->av_store.argc, state->curtable->name);
		debug_print_argv(&state->av_store);
227

228
229
230
231
232
233
234
235
		ret = cb->do_command(h, state->av_store.argc,
				     state->av_store.argv,
				     &state->av_store.argv[2], true);
		if (ret < 0) {
			if (cb->abort)
				ret = cb->abort(h);
			else
				ret = 0;
236

237
238
239
			if (ret < 0) {
				fprintf(stderr,
					"failed to abort commit operation\n");
240
			}
241
242
			exit(1);
		}
243

244
245
246
247
248
249
250
251
252
253
254
255
		free_argv(&state->av_store);
		fflush(stdout);
	}
	if (p->tablename && state->curtable &&
	    (strcmp(p->tablename, state->curtable->name) != 0))
		return;
	if (!ret) {
		fprintf(stderr, "%s: line %u failed\n",
				xt_params->program_name, line);
		exit(1);
	}
}
256

257
258
259
260
void xtables_restore_parse(struct nft_handle *h,
			   const struct nft_xt_restore_parse *p)
{
	struct nft_xt_restore_state state = {};
261
	char buffer[10240] = {};
262

263
264
	if (!h->noflush)
		nft_cache_level_set(h, NFT_CL_FAKE, NULL);
265

266
267
268
269
270
271
272
	line = 0;
	while (fgets(buffer, sizeof(buffer), p->in)) {
		h->error.lineno = ++line;
		DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
		xtables_restore_parse_line(h, p, &state, buffer);
	}
	if (state.in_table && p->commit) {
273
274
275
		fprintf(stderr, "%s: COMMIT expected at line %u\n",
				xt_params->program_name, line + 1);
		exit(1);
276
	} else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
277
278
		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
			      xt_params->program_name);
279
	}
280
281
282
283
284
}

static int
xtables_restore_main(int family, const char *progname, int argc, char *argv[])
{
285
	const struct builtin_table *tables;
286
287
	struct nft_xt_restore_parse p = {
		.commit = true,
288
		.cb = &restore_cb,
289
	};
290
291
292
	bool noflush = false;
	struct nft_handle h;
	int c;
293
294
295
296
297
298
299
300
301
302
303
304

	line = 0;

	xtables_globals.program_name = progname;
	c = xtables_init_all(&xtables_globals, family);
	if (c < 0) {
		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
				xtables_globals.program_name,
				xtables_globals.program_version);
		exit(1);
	}

305
	while ((c = getopt_long(argc, argv, "bcvVthnM:T:wW", options, NULL)) != -1) {
306
307
308
309
310
311
312
313
314
315
		switch (c) {
			case 'b':
				fprintf(stderr, "-b/--binary option is not implemented\n");
				break;
			case 'c':
				counters = 1;
				break;
			case 'v':
				verbose = 1;
				break;
316
317
318
			case 'V':
				printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
				exit(0);
319
320
321
322
			case 't':
				p.testing = 1;
				break;
			case 'h':
323
				print_usage(prog_name, PACKAGE_VERSION);
324
				exit(0);
325
			case 'n':
326
				noflush = true;
327
328
329
330
331
332
333
				break;
			case 'M':
				xtables_modprobe_program = optarg;
				break;
			case 'T':
				p.tablename = optarg;
				break;
334
335
			case 'w': /* fallthrough.  Ignored by xt-restore */
			case 'W':
336
337
				if (!optarg && xs_has_arg(argc, argv))
					optind++;
338
				break;
339
340
			default:
				fprintf(stderr,
341
342
					"Try `%s -h' for more information.\n",
					prog_name);
343
				exit(1);
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
		}
	}

	if (optind == argc - 1) {
		p.in = fopen(argv[optind], "re");
		if (!p.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 {
		p.in = stdin;
	}

361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
	switch (family) {
	case NFPROTO_IPV4:
	case NFPROTO_IPV6: /* fallthough, same table */
		tables = xtables_ipv4;
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
		init_extensions();
		init_extensions4();
#endif
		break;
	case NFPROTO_ARP:
		tables = xtables_arp;
		break;
	case NFPROTO_BRIDGE:
		tables = xtables_bridge;
		break;
	default:
		fprintf(stderr, "Unknown family %d\n", family);
		return 1;
	}

381
	if (nft_init(&h, family, tables) < 0) {
382
383
384
385
386
387
		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
				xtables_globals.program_name,
				xtables_globals.program_version,
				strerror(errno));
		exit(EXIT_FAILURE);
	}
388
389
	h.noflush = noflush;
	h.restore = true;
390

391
	xtables_restore_parse(&h, &p);
392

393
	nft_fini(&h);
394
	xtables_fini();
395
	fclose(p.in);
396
397
398
399
400
	return 0;
}

int xtables_ip4_restore_main(int argc, char *argv[])
{
401
	return xtables_restore_main(NFPROTO_IPV4, basename(*argv),
402
403
404
405
406
				    argc, argv);
}

int xtables_ip6_restore_main(int argc, char *argv[])
{
407
	return xtables_restore_main(NFPROTO_IPV6, basename(*argv),
408
409
				    argc, argv);
}
410

411
412
static const struct nft_xt_restore_cb ebt_restore_cb = {
	.commit		= nft_bridge_commit,
413
414
	.table_new	= nft_cmd_table_new,
	.table_flush	= nft_cmd_table_flush,
415
	.do_command	= do_commandeb,
416
417
	.chain_set	= nft_cmd_chain_set,
	.chain_restore  = nft_cmd_chain_restore,
418
419
420
421
422
423
424
425
426
427
428
};

static const struct option ebt_restore_options[] = {
	{.name = "noflush", .has_arg = 0, .val = 'n'},
	{ 0 }
};

int xtables_eb_restore_main(int argc, char *argv[])
{
	struct nft_xt_restore_parse p = {
		.in = stdin,
429
		.cb = &ebt_restore_cb,
430
	};
431
	bool noflush = false;
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
	struct nft_handle h;
	int c;

	while ((c = getopt_long(argc, argv, "n",
				ebt_restore_options, NULL)) != -1) {
		switch(c) {
		case 'n':
			noflush = 1;
			break;
		default:
			fprintf(stderr,
				"Usage: ebtables-restore [ --noflush ]\n");
			exit(1);
			break;
		}
	}

	nft_init_eb(&h, "ebtables-restore");
450
	h.noflush = noflush;
451
	xtables_restore_parse(&h, &p);
452
	nft_fini_eb(&h);
453
454
455
456

	return 0;
}

457
static const struct nft_xt_restore_cb arp_restore_cb = {
458
	.commit		= nft_commit,
459
460
	.table_new	= nft_cmd_table_new,
	.table_flush	= nft_cmd_table_flush,
461
	.do_command	= do_commandarp,
462
463
	.chain_set	= nft_cmd_chain_set,
	.chain_restore  = nft_cmd_chain_restore,
464
465
466
467
468
469
};

int xtables_arp_restore_main(int argc, char *argv[])
{
	struct nft_xt_restore_parse p = {
		.in = stdin,
470
		.cb = &arp_restore_cb,
471
472
473
474
	};
	struct nft_handle h;

	nft_init_arp(&h, "arptables-restore");
475
	xtables_restore_parse(&h, &p);
476
	nft_fini(&h);
477
	xtables_fini();
478
479
480

	return 0;
}