xtables-restore.c 11.8 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
	.table_flush	= nft_cmd_table_flush,
65
	.do_command	= do_commandx,
66
67
	.chain_set	= nft_cmd_chain_set,
	.chain_restore  = nft_cmd_chain_restore,
68
69
};

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

76
77
78
79
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)
80
{
81
82
83
84
85
86
	const struct nft_xt_restore_cb *cb = p->cb;
	int ret = 0;

	if (buffer[0] == '\n')
		return;
	else if (buffer[0] == '#') {
87
		if (verbose) {
88
			fputs(buffer, stdout);
89
90
			fflush(stdout);
		}
91
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
		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;

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

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

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
		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);
179
180

			}
181
182
183
184
185
186
187
			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));
188
			}
189
190
191
192
193
194
195
196
			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 &&
197
			   !ebt_cmd_user_chain_policy(h, state->curtable->name,
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
						      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);
		}
220

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

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

227
228
229
230
231
232
233
234
		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;
235

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

243
244
245
246
247
248
249
250
251
252
253
254
		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);
	}
}
255

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

262
	if (!verbose && !h->noflush)
263
		nft_cache_level_set(h, NFT_CL_FAKE, NULL);
264

265
266
267
268
269
270
271
	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) {
272
273
274
		fprintf(stderr, "%s: COMMIT expected at line %u\n",
				xt_params->program_name, line + 1);
		exit(1);
275
	} else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
276
277
		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
			      xt_params->program_name);
278
	}
279
280
281
282
283
}

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

	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);
	}

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

	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;
	}

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
	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;
	}

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

390
	xtables_restore_parse(&h, &p);
391

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

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

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

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

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,
427
		.cb = &ebt_restore_cb,
428
	};
429
	bool noflush = false;
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
	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");
448
	h.noflush = noflush;
449
	xtables_restore_parse(&h, &p);
450
	nft_fini_eb(&h);
451
452
453
454

	return 0;
}

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

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

	nft_init_arp(&h, "arptables-restore");
472
	xtables_restore_parse(&h, &p);
473
	nft_fini(&h);
474
	xtables_fini();
475
476
477

	return 0;
}