xtables-restore.c 12.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 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
 */

#include <getopt.h>
#include <errno.h>
#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"
19
#include "nft-bridge.h"
20
21
#include <libnftnl/chain.h>

22
static int counters, verbose;
23
24
25
26
27

/* 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'},
28
	{.name = "version",       .has_arg = 0, .val = 'V'},
29
30
31
32
33
34
35
	{.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'},
36
37
	{.name = "wait",          .has_arg = 2, .val = 'w'},
	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
38
39
40
41
	{NULL},
};

#define prog_name xtables_globals.program_name
42
#define prog_vers xtables_globals.program_version
43
44
45

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

59
60
static struct nftnl_chain_list *get_chain_list(struct nft_handle *h,
					       const char *table)
61
62
63
{
	struct nftnl_chain_list *chain_list;

64
	chain_list = nft_chain_list_get(h, table);
65
66
67
68
69
70
71
72
73
74
	if (chain_list == NULL)
		xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");

	return chain_list;
}

struct nft_xt_restore_cb restore_cb = {
	.chain_list	= get_chain_list,
	.commit		= nft_commit,
	.abort		= nft_abort,
75
76
	.table_new	= nft_table_new,
	.table_flush	= nft_table_flush,
77
78
	.do_command	= do_commandx,
	.chain_set	= nft_chain_set,
79
	.chain_restore  = nft_chain_restore,
80
81
};

82
83
84
85
static const struct xtc_ops xtc_ops = {
	.strerror	= nft_strerror,
};

86
87
88
89
void xtables_restore_parse(struct nft_handle *h,
			   struct nft_xt_restore_parse *p,
			   struct nft_xt_restore_cb *cb,
			   int argc, char *argv[])
90
{
91
	const struct builtin_table *curtable = NULL;
92
	char buffer[10240];
93
	int in_table = 0;
94
95
96
97
98
	const struct xtc_ops *ops = &xtc_ops;

	line = 0;

	/* Grab standard input. */
99
	while (fgets(buffer, sizeof(buffer), p->in)) {
100
101
102
		int ret = 0;

		line++;
103
104
		h->error.lineno = line;

105
106
107
108
109
110
111
		if (buffer[0] == '\n')
			continue;
		else if (buffer[0] == '#') {
			if (verbose)
				fputs(buffer, stdout);
			continue;
		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
112
			if (!p->testing) {
113
114
115
116
117
				/* Commit per table, although we support
				 * global commit at once, stick by now to
				 * the existing behaviour.
				 */
				DEBUGP("Calling commit\n");
118
119
				if (cb->commit)
					ret = cb->commit(h);
120
121
			} else {
				DEBUGP("Not calling commit, testing\n");
122
123
				if (cb->abort)
					ret = cb->abort(h);
124
125
126
			}
			in_table = 0;

127
		} else if ((buffer[0] == '*') && (!in_table || !p->commit)) {
128
129
130
131
132
133
134
135
136
137
138
			/* 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);
				exit(1);
			}
139
140
141
142
143
			curtable = nft_table_builtin_find(h, table);
			if (!curtable)
				xtables_error(PARAMETER_PROBLEM,
					"%s: line %u table name '%s' invalid\n",
					xt_params->program_name, line, table);
144

145
			if (p->tablename && (strcmp(p->tablename, table) != 0))
146
147
				continue;

148
149
150
			nft_build_cache(h);

			if (h->noflush == 0) {
151
152
				DEBUGP("Cleaning all chains of table '%s'\n",
					table);
153
154
				if (cb->table_flush)
					cb->table_flush(h, table);
155
156
157
158
159
			}

			ret = 1;
			in_table = 1;

160
161
162
			if (cb->table_new)
				cb->table_new(h, table);

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
		} else if ((buffer[0] == ':') && (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);
				exit(1);
			}

			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);
				exit(1);
			}

192
			if (nft_chain_builtin_find(curtable, chain)) {
193
194
195
196
197
198
199
200
201
202
				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);

				}
203
				if (cb->chain_set &&
204
205
				    cb->chain_set(h, curtable->name,
					          chain, policy, &count) < 0) {
206
207
208
209
210
211
212
213
					xtables_error(OTHER_PROBLEM,
						      "Can't set policy `%s'"
						      " on `%s' line %u: %s\n",
						      policy, chain, line,
						      ops->strerror(errno));
				}
				DEBUGP("Setting policy of chain %s to %s\n",
				       chain, policy);
214
215
216
217
218
219
220
221
222
223
224
225
226
227
			} else if (cb->chain_restore(h, chain, curtable->name) < 0 &&
				   errno != EEXIST) {
				xtables_error(PARAMETER_PROBLEM,
					      "cannot create chain "
					      "'%s' (%s)\n", chain,
					      strerror(errno));
			} else if (h->family == NFPROTO_BRIDGE &&
				   !ebt_set_user_chain_policy(h, curtable->name,
							      chain, policy)) {
				xtables_error(OTHER_PROBLEM,
					      "Can't set policy `%s'"
					      " on `%s' line %u: %s\n",
					      policy, chain, line,
					      ops->strerror(errno));
228
			}
229
			ret = 1;
230
231
232
233
234
235
236
237
238
239
240
		} else if (in_table) {
			int a;
			char *pcnt = NULL;
			char *bcnt = NULL;
			char *parsestart;

			/* reset the newargv */
			newargc = 0;

			if (buffer[0] == '[') {
				/* we have counters in our input */
241
242
				char *ptr = strchr(buffer, ']');

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
				if (!ptr)
					xtables_error(PARAMETER_PROBLEM,
						   "Bad line %u: need ]\n",
						   line);

				pcnt = strtok(buffer+1, ":");
				if (!pcnt)
					xtables_error(PARAMETER_PROBLEM,
						   "Bad line %u: need :\n",
						   line);

				bcnt = strtok(NULL, "]");
				if (!bcnt)
					xtables_error(PARAMETER_PROBLEM,
						   "Bad line %u: need ]\n",
						   line);

				/* start command parsing after counter */
				parsestart = ptr + 1;
			} else {
				/* start command parsing at start of line */
				parsestart = buffer;
			}

267
268
269
			add_argv(argv[0], 0);
			add_argv("-t", 0);
			add_argv(curtable->name, 0);
270
271

			if (counters && pcnt && bcnt) {
272
273
274
				add_argv("--set-counters", 0);
				add_argv((char *) pcnt, 0);
				add_argv((char *) bcnt, 0);
275
276
			}

277
			add_param_to_argv(parsestart, line);
278
279

			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
280
				newargc, curtable->name);
281
282
283
284

			for (a = 0; a < newargc; a++)
				DEBUGP("argv[%u]: %s\n", a, newargv[a]);

285
286
			ret = cb->do_command(h, newargc, newargv,
					    &newargv[2], true);
287
			if (ret < 0) {
288
289
290
291
292
				if (cb->abort)
					ret = cb->abort(h);
				else
					ret = 0;

293
294
295
296
297
298
299
300
301
302
				if (ret < 0) {
					fprintf(stderr, "failed to abort "
							"commit operation\n");
				}
				exit(1);
			}

			free_argv();
			fflush(stdout);
		}
303
304
		if (p->tablename && curtable &&
		    (strcmp(p->tablename, curtable->name) != 0))
305
306
307
308
309
310
311
			continue;
		if (!ret) {
			fprintf(stderr, "%s: line %u failed\n",
					xt_params->program_name, line);
			exit(1);
		}
	}
312
	if (in_table && p->commit) {
313
314
315
		fprintf(stderr, "%s: COMMIT expected at line %u\n",
				xt_params->program_name, line + 1);
		exit(1);
316
317
318
	} else if (in_table && cb->commit && !cb->commit(h)) {
		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
			      xt_params->program_name);
319
	}
320
321
322
323
324
}

static int
xtables_restore_main(int family, const char *progname, int argc, char *argv[])
{
325
	const struct builtin_table *tables;
326
327
328
329
330
	struct nft_handle h = {
		.family = family,
		.restore = true,
	};
	int c;
331
332
333
	struct nft_xt_restore_parse p = {
		.commit = true,
	};
334
335
336
337
338
339
340
341
342
343
344
345

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

346
	while ((c = getopt_long(argc, argv, "bcvVthnM:T:46wW", options, NULL)) != -1) {
347
348
349
350
351
352
353
354
355
356
		switch (c) {
			case 'b':
				fprintf(stderr, "-b/--binary option is not implemented\n");
				break;
			case 'c':
				counters = 1;
				break;
			case 'v':
				verbose = 1;
				break;
357
358
359
			case 'V':
				printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
				exit(0);
360
361
362
363
364
365
			case 't':
				p.testing = 1;
				break;
			case 'h':
				print_usage("xtables-restore",
					    IPTABLES_VERSION);
366
				exit(0);
367
			case 'n':
368
				h.noflush = 1;
369
370
371
372
373
374
375
376
377
378
379
380
381
382
				break;
			case 'M':
				xtables_modprobe_program = optarg;
				break;
			case 'T':
				p.tablename = optarg;
				break;
			case '4':
				h.family = AF_INET;
				break;
			case '6':
				h.family = AF_INET6;
				xtables_set_nfproto(AF_INET6);
				break;
383
384
			case 'w': /* fallthrough.  Ignored by xt-restore */
			case 'W':
385
386
				if (!optarg && xs_has_arg(argc, argv))
					optind++;
387
				break;
388
389
390
391
			default:
				fprintf(stderr,
					"Try `xtables-restore -h' for more information.\n");
				exit(1);
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
		}
	}

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

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
	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;
	}

	if (nft_init(&h, tables) < 0) {
		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
				xtables_globals.program_name,
				xtables_globals.program_version,
				strerror(errno));
		exit(EXIT_FAILURE);
	}

437
	xtables_restore_parse(&h, &p, &restore_cb, argc, argv);
438

439
	nft_fini(&h);
440
	fclose(p.in);
441
442
443
444
445
446
447
448
449
450
451
452
453
454
	return 0;
}

int xtables_ip4_restore_main(int argc, char *argv[])
{
	return xtables_restore_main(NFPROTO_IPV4, "iptables-restore",
				    argc, argv);
}

int xtables_ip6_restore_main(int argc, char *argv[])
{
	return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore",
				    argc, argv);
}
455

456
457
458
459
460
461
462
static int ebt_table_flush(struct nft_handle *h, const char *table)
{
	/* drop any pending policy rule add/removal jobs */
	nft_abort_policy_rule(h, table);
	return nft_table_flush(h, table);
}

463
464
465
466
struct nft_xt_restore_cb ebt_restore_cb = {
	.chain_list	= get_chain_list,
	.commit		= nft_commit,
	.table_new	= nft_table_new,
467
	.table_flush	= ebt_table_flush,
468
469
	.do_command	= do_commandeb,
	.chain_set	= nft_chain_set,
470
	.chain_restore  = nft_chain_restore,
471
472
473
474
475
476
477
478
479
480
481
482
};

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,
	};
483
	bool noflush = false;
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
	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");
502
	h.noflush = noflush;
503
504
505
506
507
508
509
510
511
512
513
514
515
	xtables_restore_parse(&h, &p, &ebt_restore_cb, argc, argv);
	nft_fini(&h);

	return 0;
}

struct nft_xt_restore_cb arp_restore_cb = {
	.chain_list	= get_chain_list,
	.commit		= nft_commit,
	.table_new	= nft_table_new,
	.table_flush	= nft_table_flush,
	.do_command	= do_commandarp,
	.chain_set	= nft_chain_set,
516
	.chain_restore  = nft_chain_restore,
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
};

int xtables_arp_restore_main(int argc, char *argv[])
{
	struct nft_xt_restore_parse p = {
		.in = stdin,
	};
	struct nft_handle h;

	nft_init_arp(&h, "arptables-restore");
	xtables_restore_parse(&h, &p, &arp_restore_cb, argc, argv);
	nft_fini(&h);

	return 0;
}