xtables.c 32.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Code to take an iptables-style command line and do it. */

/*
 * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
 *
 * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
 *		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
 *		    Marc Boucher <marc+nf@mbsi.ca>
 *		    James Morris <jmorris@intercode.com.au>
 *		    Harald Welte <laforge@gnumonks.org>
 *		    Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
27
#include "config.h"
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <getopt.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <iptables.h>
#include <xtables.h>
#include <fcntl.h>
#include "xshared.h"
#include "nft-shared.h"
#include "nft.h"

#define OPT_FRAGMENT	0x00800U
#define NUMBER_OF_OPT	ARRAY_SIZE(optflags)
static const char optflags[]
= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'};

static struct option original_opts[] = {
	{.name = "append",	  .has_arg = 1, .val = 'A'},
	{.name = "delete",	  .has_arg = 1, .val = 'D'},
	{.name = "check",	  .has_arg = 1, .val = 'C'},
	{.name = "insert",	  .has_arg = 1, .val = 'I'},
	{.name = "replace",	  .has_arg = 1, .val = 'R'},
	{.name = "list",	  .has_arg = 2, .val = 'L'},
	{.name = "list-rules",	  .has_arg = 2, .val = 'S'},
	{.name = "flush",	  .has_arg = 2, .val = 'F'},
	{.name = "zero",	  .has_arg = 2, .val = 'Z'},
	{.name = "new-chain",	  .has_arg = 1, .val = 'N'},
	{.name = "delete-chain",  .has_arg = 2, .val = 'X'},
	{.name = "rename-chain",  .has_arg = 1, .val = 'E'},
	{.name = "policy",	  .has_arg = 1, .val = 'P'},
	{.name = "source",	  .has_arg = 1, .val = 's'},
	{.name = "destination",   .has_arg = 1, .val = 'd'},
	{.name = "src",		  .has_arg = 1, .val = 's'}, /* synonym */
	{.name = "dst",		  .has_arg = 1, .val = 'd'}, /* synonym */
	{.name = "protocol",	  .has_arg = 1, .val = 'p'},
	{.name = "in-interface",  .has_arg = 1, .val = 'i'},
	{.name = "jump",	  .has_arg = 1, .val = 'j'},
	{.name = "table",	  .has_arg = 1, .val = 't'},
	{.name = "match",	  .has_arg = 1, .val = 'm'},
	{.name = "numeric",	  .has_arg = 0, .val = 'n'},
	{.name = "out-interface", .has_arg = 1, .val = 'o'},
	{.name = "verbose",	  .has_arg = 0, .val = 'v'},
	{.name = "wait",	  .has_arg = 2, .val = 'w'},
78
	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
	{.name = "exact",	  .has_arg = 0, .val = 'x'},
	{.name = "fragments",	  .has_arg = 0, .val = 'f'},
	{.name = "version",	  .has_arg = 0, .val = 'V'},
	{.name = "help",	  .has_arg = 2, .val = 'h'},
	{.name = "line-numbers",  .has_arg = 0, .val = '0'},
	{.name = "modprobe",	  .has_arg = 1, .val = 'M'},
	{.name = "set-counters",  .has_arg = 1, .val = 'c'},
	{.name = "goto",	  .has_arg = 1, .val = 'g'},
	{.name = "ipv4",	  .has_arg = 0, .val = '4'},
	{.name = "ipv6",	  .has_arg = 0, .val = '6'},
	{NULL},
};

void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));

struct xtables_globals xtables_globals = {
	.option_offset = 0,
96
	.program_version = PACKAGE_VERSION,
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
	.orig_opts = original_opts,
	.exit_err = xtables_exit_error,
	.compat_rev = nft_compatible_revision,
};

/* Table of legal combinations of commands and options.  If any of the
 * given commands make an option legal, that option is legal (applies to
 * CMD_LIST and CMD_ZERO only).
 * Key:
 *  +  compulsory
 *  x  illegal
 *     optional
 */

static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o --line -c -f */
/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
/*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' ','x','x'},
/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*ZERO_NUM*/  {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x'},
/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*CHECK*/     {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
};

static const int inverse_for_options[NUMBER_OF_OPT] =
{
/* -n */ 0,
/* -s */ IPT_INV_SRCIP,
/* -d */ IPT_INV_DSTIP,
/* -p */ XT_INV_PROTO,
/* -j */ 0,
/* -v */ 0,
/* -x */ 0,
/* -i */ IPT_INV_VIA_IN,
/* -o */ IPT_INV_VIA_OUT,
/*--line*/ 0,
/* -c */ 0,
/* -f */ IPT_INV_FRAG,
};

148
149
150
#define opts xt_params->opts
#define prog_name xt_params->program_name
#define prog_vers xt_params->program_version
151
152
153
154
155
156
157
158
159
160
161
162
163

static void __attribute__((noreturn))
exit_tryhelp(int status)
{
	if (line != -1)
		fprintf(stderr, "Error occurred at line: %d\n", line);
	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
			prog_name, prog_name);
	xtables_free_opts(1);
	exit(status);
}

static void
164
printhelp(const struct xtables_rule_match *matches)
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
{
	printf("%s v%s\n\n"
"Usage: %s -[ACD] chain rule-specification [options]\n"
"	%s -I chain [rulenum] rule-specification [options]\n"
"	%s -R chain rulenum rule-specification [options]\n"
"	%s -D chain rulenum [options]\n"
"	%s -[LS] [chain [rulenum]] [options]\n"
"	%s -[FZ] [chain] [options]\n"
"	%s -[NX] chain\n"
"	%s -E old-chain-name new-chain-name\n"
"	%s -P chain target [options]\n"
"	%s -h (print this help information)\n\n",
	       prog_name, prog_vers, prog_name, prog_name,
	       prog_name, prog_name, prog_name, prog_name,
	       prog_name, prog_name, prog_name, prog_name);

	printf(
"Commands:\n"
"Either long or short options are allowed.\n"
"  --append  -A chain		Append to chain\n"
"  --check   -C chain		Check for the existence of a rule\n"
"  --delete  -D chain		Delete matching rule from chain\n"
"  --delete  -D chain rulenum\n"
"				Delete rule rulenum (1 = first) from chain\n"
"  --insert  -I chain [rulenum]\n"
"				Insert in chain as rulenum (default 1=first)\n"
"  --replace -R chain rulenum\n"
"				Replace rule rulenum (1 = first) in chain\n"
"  --list    -L [chain [rulenum]]\n"
"				List the rules in a chain or all chains\n"
"  --list-rules -S [chain [rulenum]]\n"
"				Print the rules in a chain or all chains\n"
"  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
"  --zero    -Z [chain [rulenum]]\n"
"				Zero counters in chain or all chains\n"
"  --new     -N chain		Create a new user-defined chain\n"
"  --delete-chain\n"
"	     -X [chain]		Delete a user-defined chain\n"
"  --policy  -P chain target\n"
"				Change policy on chain to target\n"
"  --rename-chain\n"
"	     -E old-chain new-chain\n"
"				Change chain name, (moving any references)\n"

"Options:\n"
"    --ipv4	-4		Nothing (line is ignored by ip6tables-restore)\n"
"    --ipv6	-6		Error (line is ignored by iptables-restore)\n"
"[!] --proto	-p proto	protocol: by number or name, eg. `tcp'\n"
"[!] --source	-s address[/mask][...]\n"
"				source specification\n"
"[!] --destination -d address[/mask][...]\n"
"				destination specification\n"
"[!] --in-interface -i input name[+]\n"
"				network interface name ([+] for wildcard)\n"
" --jump	-j target\n"
"				target for rule (may load target extension)\n"
#ifdef IPT_F_GOTO
"  --goto      -g chain\n"
"			       jump to chain with no return\n"
#endif
"  --match	-m match\n"
"				extended match (may load extension)\n"
"  --numeric	-n		numeric output of addresses and ports\n"
"[!] --out-interface -o output name[+]\n"
"				network interface name ([+] for wildcard)\n"
"  --table	-t table	table to manipulate (default: `filter')\n"
"  --verbose	-v		verbose mode\n"
232
233
234
"  --wait	-w [seconds]	maximum wait to acquire xtables lock before give up\n"
"  --wait-interval -W [usecs]	wait time to try to acquire xtables lock\n"
"				default is 1 second\n"
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
"  --line-numbers		print line numbers when listing\n"
"  --exact	-x		expand numbers (display exact values)\n"
"[!] --fragment	-f		match second or further fragments only\n"
"  --modprobe=<command>		try to insert modules using this command\n"
"  --set-counters PKTS BYTES	set the counter during insert/append\n"
"[!] --version	-V		print package version.\n");

	print_extension_helps(xtables_targets, matches);
}

void
xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
{
	va_list args;

	va_start(args, msg);
251
	fprintf(stderr, "%s v%s (nf_tables): ", prog_name, prog_vers);
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
	vfprintf(stderr, msg, args);
	va_end(args);
	fprintf(stderr, "\n");
	if (status == PARAMETER_PROBLEM)
		exit_tryhelp(status);
	if (status == VERSION_PROBLEM)
		fprintf(stderr,
			"Perhaps iptables or your kernel needs to be upgraded.\n");
	/* On error paths, make sure that we don't leak memory */
	xtables_free_opts(1);
	exit(status);
}

static void
generic_opt_check(int command, int options)
{
	int i, j, legal = 0;

	/* Check that commands are valid with options.	Complicated by the
	 * fact that if an option is legal with *any* command given, it is
	 * legal overall (ie. -z and -l).
	 */
	for (i = 0; i < NUMBER_OF_OPT; i++) {
		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */

		for (j = 0; j < NUMBER_OF_CMD; j++) {
			if (!(command & (1<<j)))
				continue;

			if (!(options & (1<<i))) {
				if (commands_v_options[j][i] == '+')
					xtables_error(PARAMETER_PROBLEM,
						   "You need to supply the `-%c' "
						   "option for this command\n",
						   optflags[i]);
			} else {
				if (commands_v_options[j][i] != 'x')
					legal = 1;
				else if (legal == 0)
					legal = -1;
			}
		}
		if (legal == -1)
			xtables_error(PARAMETER_PROBLEM,
				   "Illegal option `-%c' with this command\n",
				   optflags[i]);
	}
}

static char
opt2char(int option)
{
	const char *ptr;
	for (ptr = optflags; option > 1; option >>= 1, ptr++);

	return *ptr;
}

/*
 *	All functions starting with "parse" should succeed, otherwise
 *	the program fails.
 *	Most routines return pointers to static data that may change
 *	between calls to the same or other routines with a few exceptions:
 *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
 *	return global static data.
*/

/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */

static void
set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
	   int invert)
{
	if (*options & option)
		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
			   opt2char(option));
	*options |= option;

	if (invert) {
		unsigned int i;
		for (i = 0; 1 << i != option; i++);

		if (!inverse_for_options[i])
			xtables_error(PARAMETER_PROBLEM,
				   "cannot have ! before -%c",
				   opt2char(option));
		*invflg |= inverse_for_options[i];
	}
}

static int
add_entry(const char *chain,
	  const char *table,
	  struct iptables_command_state *cs,
	  int rulenum, int family,
	  const struct addr_mask s,
	  const struct addr_mask d,
	  bool verbose, struct nft_handle *h, bool append)
{
	unsigned int i, j;
	int ret = 1;

	for (i = 0; i < s.naddrs; i++) {
		if (family == AF_INET) {
			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
			for (j = 0; j < d.naddrs; j++) {
				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;

				if (append) {
363
					ret = nft_cmd_rule_append(h, chain, table,
364
							      cs, NULL,
365
366
							      verbose);
				} else {
367
					ret = nft_cmd_rule_insert(h, chain, table,
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
							      cs, rulenum,
							      verbose);
				}
			}
		} else if (family == AF_INET6) {
			memcpy(&cs->fw6.ipv6.src,
			       &s.addr.v6[i], sizeof(struct in6_addr));
			memcpy(&cs->fw6.ipv6.smsk,
			       &s.mask.v6[i], sizeof(struct in6_addr));
			for (j = 0; j < d.naddrs; j++) {
				memcpy(&cs->fw6.ipv6.dst,
				       &d.addr.v6[j], sizeof(struct in6_addr));
				memcpy(&cs->fw6.ipv6.dmsk,
				       &d.mask.v6[j], sizeof(struct in6_addr));
				if (append) {
383
					ret = nft_cmd_rule_append(h, chain, table,
384
							      cs, NULL,
385
386
							      verbose);
				} else {
387
					ret = nft_cmd_rule_insert(h, chain, table,
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
							      cs, rulenum,
							      verbose);
				}
			}
		}
	}

	return ret;
}

static int
replace_entry(const char *chain, const char *table,
	      struct iptables_command_state *cs,
	      unsigned int rulenum,
	      int family,
	      const struct addr_mask s,
	      const struct addr_mask d,
	      bool verbose, struct nft_handle *h)
{
	if (family == AF_INET) {
		cs->fw.ip.src.s_addr = s.addr.v4->s_addr;
		cs->fw.ip.dst.s_addr = d.addr.v4->s_addr;
		cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr;
		cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr;
	} else if (family == AF_INET6) {
		memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr));
		memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr));
		memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr));
		memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr));
	} else
		return 1;

420
	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
}

static int
delete_entry(const char *chain, const char *table,
	     struct iptables_command_state *cs,
	     int family,
	     const struct addr_mask s,
	     const struct addr_mask d,
	     bool verbose,
	     struct nft_handle *h)
{
	unsigned int i, j;
	int ret = 1;

	for (i = 0; i < s.naddrs; i++) {
		if (family == AF_INET) {
			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
			for (j = 0; j < d.naddrs; j++) {
				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
442
				ret = nft_cmd_rule_delete(h, chain,
443
444
445
446
447
448
449
450
451
452
453
454
						      table, cs, verbose);
			}
		} else if (family == AF_INET6) {
			memcpy(&cs->fw6.ipv6.src,
			       &s.addr.v6[i], sizeof(struct in6_addr));
			memcpy(&cs->fw6.ipv6.smsk,
			       &s.mask.v6[i], sizeof(struct in6_addr));
			for (j = 0; j < d.naddrs; j++) {
				memcpy(&cs->fw6.ipv6.dst,
				       &d.addr.v6[j], sizeof(struct in6_addr));
				memcpy(&cs->fw6.ipv6.dmsk,
				       &d.mask.v6[j], sizeof(struct in6_addr));
455
				ret = nft_cmd_rule_delete(h, chain,
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
						      table, cs, verbose);
			}
		}
	}

	return ret;
}

static int
check_entry(const char *chain, const char *table,
	    struct iptables_command_state *cs,
	    int family,
	    const struct addr_mask s,
	    const struct addr_mask d,
	    bool verbose, struct nft_handle *h)
{
	unsigned int i, j;
	int ret = 1;

	for (i = 0; i < s.naddrs; i++) {
		if (family == AF_INET) {
			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
			for (j = 0; j < d.naddrs; j++) {
				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
482
				ret = nft_cmd_rule_check(h, chain,
483
484
485
486
487
488
489
490
491
492
493
494
						     table, cs, verbose);
			}
		} else if (family == AF_INET6) {
			memcpy(&cs->fw6.ipv6.src,
			       &s.addr.v6[i], sizeof(struct in6_addr));
			memcpy(&cs->fw6.ipv6.smsk,
			       &s.mask.v6[i], sizeof(struct in6_addr));
			for (j = 0; j < d.naddrs; j++) {
				memcpy(&cs->fw6.ipv6.dst,
				       &d.addr.v6[j], sizeof(struct in6_addr));
				memcpy(&cs->fw6.ipv6.dmsk,
				       &d.mask.v6[j], sizeof(struct in6_addr));
495
				ret = nft_cmd_rule_check(h, chain,
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
						     table, cs, verbose);
			}
		}
	}

	return ret;
}

static int
list_entries(struct nft_handle *h, const char *chain, const char *table,
	     int rulenum, int verbose, int numeric, int expanded,
	     int linenumbers)
{
	unsigned int format;

	format = FMT_OPTIONS;
	if (!verbose)
		format |= FMT_NOCOUNTS;
	else
		format |= FMT_VIA;

	if (numeric)
		format |= FMT_NUMERIC;

	if (!expanded)
		format |= FMT_KILOMEGAGIGA;

	if (linenumbers)
		format |= FMT_LINENUMBERS;

526
	return nft_cmd_rule_list(h, chain, table, rulenum, format);
527
528
529
530
531
532
533
534
535
}

static int
list_rules(struct nft_handle *h, const char *chain, const char *table,
	   int rulenum, int counters)
{
	if (counters)
	    counters = -1;		/* iptables -c format */

536
	return nft_cmd_rule_list_save(h, chain, table, rulenum, counters);
537
538
}

539
540
541
void do_parse(struct nft_handle *h, int argc, char *argv[],
	      struct nft_xt_cmd_parse *p, struct iptables_command_state *cs,
	      struct xtables_args *args)
542
543
544
{
	struct xtables_match *m;
	struct xtables_rule_match *matchp;
545
546
	bool wait_interval_set = false;
	struct timeval wait_interval;
547
	struct xtables_target *t;
548
	bool table_set = false;
549
	int wait = 0;
550

551
552
553
	memset(cs, 0, sizeof(*cs));
	cs->jumpto = "";
	cs->argv = argv;
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573

	/* re-set optind to 0 in case do_command4 gets called
	 * a second time */
	optind = 0;

	/* clear mflags in case do_command4 gets called a second time
	 * (we clear the global list of all matches for security)*/
	for (m = xtables_matches; m; m = m->next)
		m->mflags = 0;

	for (t = xtables_targets; t; t = t->next) {
		t->tflags = 0;
		t->used = 0;
	}

	/* Suppress error messages: we may add new options if we
	   demand-load a protocol. */
	opterr = 0;

	opts = xt_params->orig_opts;
574
575
	while ((cs->c = getopt_long(argc, argv,
	   "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::W::nt:m:xc:g:46",
576
					   opts, NULL)) != -1) {
577
		switch (cs->c) {
578
579
580
581
			/*
			 * Command selection
			 */
		case 'A':
582
583
584
			add_command(&p->command, CMD_APPEND, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
585
586
587
			break;

		case 'C':
588
589
590
			add_command(&p->command, CMD_CHECK, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
591
592
593
			break;

		case 'D':
594
595
596
			add_command(&p->command, CMD_DELETE, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
597
			if (xs_has_arg(argc, argv)) {
598
599
				p->rulenum = parse_rulenumber(argv[optind++]);
				p->command = CMD_DELETE_NUM;
600
601
602
603
			}
			break;

		case 'R':
604
605
606
			add_command(&p->command, CMD_REPLACE, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
607
			if (xs_has_arg(argc, argv))
608
				p->rulenum = parse_rulenumber(argv[optind++]);
609
610
611
612
613
614
615
			else
				xtables_error(PARAMETER_PROBLEM,
					   "-%c requires a rule number",
					   cmd2char(CMD_REPLACE));
			break;

		case 'I':
616
617
618
			add_command(&p->command, CMD_INSERT, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
619
			if (xs_has_arg(argc, argv))
620
621
622
				p->rulenum = parse_rulenumber(argv[optind++]);
			else
				p->rulenum = 1;
623
624
625
			break;

		case 'L':
626
627
628
629
			add_command(&p->command, CMD_LIST,
				    CMD_ZERO | CMD_ZERO_NUM, cs->invert);
			if (optarg)
				p->chain = optarg;
630
			else if (xs_has_arg(argc, argv))
631
				p->chain = argv[optind++];
632
			if (xs_has_arg(argc, argv))
633
				p->rulenum = parse_rulenumber(argv[optind++]);
634
635
636
			break;

		case 'S':
637
638
639
640
			add_command(&p->command, CMD_LIST_RULES,
				    CMD_ZERO|CMD_ZERO_NUM, cs->invert);
			if (optarg)
				p->chain = optarg;
641
			else if (xs_has_arg(argc, argv))
642
				p->chain = argv[optind++];
643
			if (xs_has_arg(argc, argv))
644
				p->rulenum = parse_rulenumber(argv[optind++]);
645
646
647
			break;

		case 'F':
648
649
650
651
			add_command(&p->command, CMD_FLUSH, CMD_NONE,
				    cs->invert);
			if (optarg)
				p->chain = optarg;
652
			else if (xs_has_arg(argc, argv))
653
				p->chain = argv[optind++];
654
655
656
			break;

		case 'Z':
657
658
659
660
			add_command(&p->command, CMD_ZERO,
				    CMD_LIST|CMD_LIST_RULES, cs->invert);
			if (optarg)
				p->chain = optarg;
661
			else if (xs_has_arg(argc, argv))
662
				p->chain = argv[optind++];
663
			if (xs_has_arg(argc, argv)) {
664
665
				p->rulenum = parse_rulenumber(argv[optind++]);
				p->command = CMD_ZERO_NUM;
666
667
668
669
670
671
672
673
674
675
676
677
			}
			break;

		case 'N':
			if (optarg && (*optarg == '-' || *optarg == '!'))
				xtables_error(PARAMETER_PROBLEM,
					   "chain name not allowed to start "
					   "with `%c'\n", *optarg);
			if (xtables_find_target(optarg, XTF_TRY_LOAD))
				xtables_error(PARAMETER_PROBLEM,
					   "chain name may not clash "
					   "with target name\n");
678
679
680
			add_command(&p->command, CMD_NEW_CHAIN, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
681
682
683
			break;

		case 'X':
684
685
686
687
			add_command(&p->command, CMD_DELETE_CHAIN, CMD_NONE,
				    cs->invert);
			if (optarg)
				p->chain = optarg;
688
			else if (xs_has_arg(argc, argv))
689
				p->chain = argv[optind++];
690
691
692
			break;

		case 'E':
693
694
695
			add_command(&p->command, CMD_RENAME_CHAIN, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
696
			if (xs_has_arg(argc, argv))
697
				p->newname = argv[optind++];
698
699
700
701
702
703
704
705
			else
				xtables_error(PARAMETER_PROBLEM,
					   "-%c requires old-chain-name and "
					   "new-chain-name",
					    cmd2char(CMD_RENAME_CHAIN));
			break;

		case 'P':
706
707
708
			add_command(&p->command, CMD_SET_POLICY, CMD_NONE,
				    cs->invert);
			p->chain = optarg;
709
			if (xs_has_arg(argc, argv))
710
				p->policy = argv[optind++];
711
712
713
714
715
716
717
718
719
720
721
			else
				xtables_error(PARAMETER_PROBLEM,
					   "-%c requires a chain and a policy",
					   cmd2char(CMD_SET_POLICY));
			break;

		case 'h':
			if (!optarg)
				optarg = argv[optind];

			/* iptables -p icmp -h */
722
723
724
			if (!cs->matches && cs->protocol)
				xtables_find_match(cs->protocol,
					XTF_TRY_LOAD, &cs->matches);
725

726
727
728
			printhelp(cs->matches);
			p->command = CMD_NONE;
			return;
729
730
731
732
733

			/*
			 * Option selection
			 */
		case 'p':
734
735
			set_option(&cs->options, OPT_PROTOCOL,
				   &args->invflags, cs->invert);
736
737

			/* Canonicalize into lower case */
738
739
			for (cs->protocol = optarg; *cs->protocol; cs->protocol++)
				*cs->protocol = tolower(*cs->protocol);
740

741
742
			cs->protocol = optarg;
			args->proto = xtables_parse_protocol(cs->protocol);
743

744
745
			if (args->proto == 0 &&
			    (args->invflags & XT_INV_PROTO))
746
747
748
749
				xtables_error(PARAMETER_PROBLEM,
					   "rule would never match protocol");

			/* This needs to happen here to parse extensions */
750
			h->ops->proto_parse(cs, args);
751
752
753
			break;

		case 's':
754
755
756
			set_option(&cs->options, OPT_SOURCE,
				   &args->invflags, cs->invert);
			args->shostnetworkmask = optarg;
757
758
759
			break;

		case 'd':
760
761
762
			set_option(&cs->options, OPT_DESTINATION,
				   &args->invflags, cs->invert);
			args->dhostnetworkmask = optarg;
763
764
765
766
			break;

#ifdef IPT_F_GOTO
		case 'g':
767
768
769
			set_option(&cs->options, OPT_JUMP, &args->invflags,
				   cs->invert);
			args->goto_set = true;
770
			cs->jumpto = xt_parse_target(optarg);
771
772
773
774
			break;
#endif

		case 'j':
775
776
			set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags,
				   cs->invert);
777
			command_jump(cs, optarg);
778
779
780
781
782
783
784
785
			break;


		case 'i':
			if (*optarg == '\0')
				xtables_error(PARAMETER_PROBLEM,
					"Empty interface is likely to be "
					"undesired");
786
787
			set_option(&cs->options, OPT_VIANAMEIN,
				   &args->invflags, cs->invert);
788
			xtables_parse_interface(optarg,
789
790
						args->iniface,
						args->iniface_mask);
791
792
793
794
795
796
797
			break;

		case 'o':
			if (*optarg == '\0')
				xtables_error(PARAMETER_PROBLEM,
					"Empty interface is likely to be "
					"undesired");
798
799
			set_option(&cs->options, OPT_VIANAMEOUT,
				   &args->invflags, cs->invert);
800
			xtables_parse_interface(optarg,
801
802
						args->outiface,
						args->outiface_mask);
803
804
805
			break;

		case 'f':
806
			if (args->family == AF_INET6) {
807
808
809
810
				xtables_error(PARAMETER_PROBLEM,
					"`-f' is not supported in IPv6, "
					"use -m frag instead");
			}
811
812
813
			set_option(&cs->options, OPT_FRAGMENT, &args->invflags,
				   cs->invert);
			args->flags |= IPT_F_FRAG;
814
815
816
			break;

		case 'v':
817
818
819
820
			if (!p->verbose)
				set_option(&cs->options, OPT_VERBOSE,
					   &args->invflags, cs->invert);
			p->verbose++;
821
822
823
			break;

		case 'm':
824
			command_match(cs);
825
826
827
			break;

		case 'n':
828
829
			set_option(&cs->options, OPT_NUMERIC, &args->invflags,
				   cs->invert);
830
831
832
			break;

		case 't':
833
			if (cs->invert)
834
835
				xtables_error(PARAMETER_PROBLEM,
					   "unexpected ! flag before --table");
836
837
838
839
			if (p->restore && table_set)
				xtables_error(PARAMETER_PROBLEM,
					      "The -t option (seen in line %u) cannot be used in %s.\n",
					      line, xt_params->program_name);
840
841
842
843
			if (!nft_table_builtin_find(h, optarg))
				xtables_error(VERSION_PROBLEM,
					      "table '%s' does not exist",
					      optarg);
844
			p->table = optarg;
845
			table_set = true;
846
847
848
			break;

		case 'x':
849
850
			set_option(&cs->options, OPT_EXPANDED, &args->invflags,
				   cs->invert);
851
852
853
			break;

		case 'V':
854
			if (cs->invert)
855
856
				printf("Not %s ;-)\n", prog_vers);
			else
857
				printf("%s v%s (nf_tables)\n",
858
859
860
861
				       prog_name, prog_vers);
			exit(0);

		case 'w':
862
			if (p->restore) {
863
864
865
866
				xtables_error(PARAMETER_PROBLEM,
					      "You cannot use `-w' from "
					      "iptables-restore");
			}
867
868

			wait = parse_wait_time(argc, argv);
869
870
			break;

871
872
873
874
875
876
877
		case 'W':
			if (p->restore) {
				xtables_error(PARAMETER_PROBLEM,
					      "You cannot use `-W' from "
					      "iptables-restore");
			}

878
			parse_wait_interval(argc, argv, &wait_interval);
879
880
881
			wait_interval_set = true;
			break;

882
		case '0':
883
884
			set_option(&cs->options, OPT_LINENUMBERS,
				   &args->invflags, cs->invert);
885
886
887
888
889
890
891
			break;

		case 'M':
			xtables_modprobe_program = optarg;
			break;

		case 'c':
892
893
894
895
896
897
			set_option(&cs->options, OPT_COUNTERS, &args->invflags,
				   cs->invert);
			args->pcnt = optarg;
			args->bcnt = strchr(args->pcnt + 1, ',');
			if (args->bcnt)
			    args->bcnt++;
898
			if (!args->bcnt && xs_has_arg(argc, argv))
899
900
				args->bcnt = argv[optind++];
			if (!args->bcnt)
901
902
903
904
				xtables_error(PARAMETER_PROBLEM,
					"-%c requires packet and byte counter",
					opt2char(OPT_COUNTERS));

905
			if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1)
906
907
908
909
				xtables_error(PARAMETER_PROBLEM,
					"-%c packet counter not numeric",
					opt2char(OPT_COUNTERS));

910
			if (sscanf(args->bcnt, "%llu", &args->bcnt_cnt) != 1)
911
912
913
914
915
916
				xtables_error(PARAMETER_PROBLEM,
					"-%c byte counter not numeric",
					opt2char(OPT_COUNTERS));
			break;

		case '4':
917
918
919
			if (args->family == AF_INET)
				break;

920
921
922
			if (p->restore && args->family == AF_INET6)
				return;

923
			exit_tryhelp(2);
924
925

		case '6':
926
927
928
			if (args->family == AF_INET6)
				break;

929
930
931
			if (p->restore && args->family == AF_INET)
				return;

932
			exit_tryhelp(2);
933
934
935

		case 1: /* non option */
			if (optarg[0] == '!' && optarg[1] == '\0') {
936
				if (cs->invert)
937
938
939
					xtables_error(PARAMETER_PROBLEM,
						   "multiple consecutive ! not"
						   " allowed");
940
				cs->invert = true;
941
942
943
944
945
946
947
				optarg[0] = '\0';
				continue;
			}
			fprintf(stderr, "Bad argument `%s'\n", optarg);
			exit_tryhelp(2);

		default:
948
			if (command_default(cs, &xtables_globals) == 1)
949
950
951
952
				/* cf. ip6tables.c */
				continue;
			break;
		}
953
		cs->invert = false;
954
955
	}

956
957
958
	if (strcmp(p->table, "nat") == 0 &&
	    ((p->policy != NULL && strcmp(p->policy, "DROP") == 0) ||
	    (cs->jumpto != NULL && strcmp(cs->jumpto, "DROP") == 0)))
959
960
961
962
		xtables_error(PARAMETER_PROBLEM,
			"\nThe \"nat\" table is not intended for filtering, "
			"the use of DROP is therefore inhibited.\n\n");

963
964
965
966
967
	if (!wait && wait_interval_set)
		xtables_error(PARAMETER_PROBLEM,
			      "--wait-interval only makes sense with --wait\n");

	for (matchp = cs->matches; matchp; matchp = matchp->next)
968
		xtables_option_mfcall(matchp->match);
969
970
	if (cs->target != NULL)
		xtables_option_tfcall(cs->target);
971
972
973
974
975
976

	/* Fix me: must put inverse options checking here --MN */

	if (optind < argc)
		xtables_error(PARAMETER_PROBLEM,
			   "unknown arguments found on commandline");
977
	if (!p->command)
978
		xtables_error(PARAMETER_PROBLEM, "no command specified");
979
	if (cs->invert)
980
981
982
983
984
		xtables_error(PARAMETER_PROBLEM,
			   "nothing appropriate following !");

	/* Set only if required, needed by xtables-restore */
	if (h->family == AF_UNSPEC)
985
		h->family = args->family;
986

987
	h->ops->post_parse(p->command, cs, args);
988

989
990
	if (p->command == CMD_REPLACE &&
	    (args->s.naddrs != 1 || args->d.naddrs != 1))
991
992
993
		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
			   "specify a unique address");

994
	generic_opt_check(p->command, cs->options);
995

996
	if (p->chain != NULL && strlen(p->chain) >= XT_EXTENSION_MAXNAMELEN)
997
998
		xtables_error(PARAMETER_PROBLEM,
			   "chain name `%s' too long (must be under %u chars)",
999
1000
1001
1002
			   p->chain, XT_EXTENSION_MAXNAMELEN);

	if (p->command == CMD_APPEND ||
	    p->command == CMD_DELETE ||
1003
	    p->command == CMD_DELETE_NUM ||
1004
1005
1006
1007
1008
	    p->command == CMD_CHECK ||
	    p->command == CMD_INSERT ||
	    p->command == CMD_REPLACE) {
		if (strcmp(p->chain, "PREROUTING") == 0
		    || strcmp(p->chain, "INPUT") == 0) {
1009
			/* -o not valid with incoming packets. */
1010
			if (cs->options & OPT_VIANAMEOUT)
1011
1012
1013
				xtables_error(PARAMETER_PROBLEM,
					   "Can't use -%c with %s\n",
					   opt2char(OPT_VIANAMEOUT),
1014
					   p->chain);
1015
1016
		}

1017
1018
		if (strcmp(p->chain, "POSTROUTING") == 0
		    || strcmp(p->chain, "OUTPUT") == 0) {
1019
			/* -i not valid with outgoing packets */
1020
			if (cs->options & OPT_VIANAMEIN)
1021
1022
1023
				xtables_error(PARAMETER_PROBLEM,
					   "Can't use -%c with %s\n",
					   opt2char(OPT_VIANAMEIN),
1024
					   p->chain);
1025
1026
		}
	}
1027
}
1028

1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
		bool restore)
{
	int ret = 1;
	struct nft_xt_cmd_parse p = {
		.table		= *table,
		.restore	= restore,
	};
	struct iptables_command_state cs;
	struct xtables_args args = {
		.family = h->family,
	};

	do_parse(h, argc, argv, &p, &cs, &args);

	switch (p.command) {
1045
	case CMD_APPEND:
1046
1047
1048
		ret = add_entry(p.chain, p.table, &cs, 0, h->family,
				args.s, args.d,
				cs.options & OPT_VERBOSE, h, true);
1049
1050
		break;
	case CMD_DELETE:
1051
1052
1053
		ret = delete_entry(p.chain, p.table, &cs, h->family,
				   args.s, args.d,
				   cs.options & OPT_VERBOSE, h);
1054
1055
		break;
	case CMD_DELETE_NUM:
1056
1057
		ret = nft_cmd_rule_delete_num(h, p.chain, p.table,
					      p.rulenum - 1, p.verbose);
1058
1059
		break;
	case CMD_CHECK:
1060
1061
1062
		ret = check_entry(p.chain, p.table, &cs, h->family,
				  args.s, args.d,
				  cs.options & OPT_VERBOSE, h);
1063
1064
		break;
	case CMD_REPLACE:
1065
		ret = replace_entry(p.chain, p.table, &cs, p.rulenum - 1,
1066
				    h->family, args.s, args.d,
1067
				    cs.options & OPT_VERBOSE, h);
1068
1069
		break;
	case CMD_INSERT:
1070
1071
1072
		ret = add_entry(p.chain, p.table, &cs, p.rulenum - 1,
				h->family, args.s, args.d,
				cs.options&OPT_VERBOSE, h, false);
1073
1074
		break;
	case CMD_FLUSH:
1075
1076
		ret = nft_cmd_rule_flush(h, p.chain, p.table,
					 cs.options & OPT_VERBOSE);
1077
1078
		break;
	case CMD_ZERO:
1079
1080
		ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
						  cs.options & OPT_VERBOSE);
1081
1082
		break;
	case CMD_ZERO_NUM:
1083
		ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
1084
					     p.rulenum - 1);
1085
1086
1087
1088
		break;
	case CMD_LIST:
	case CMD_LIST|CMD_ZERO:
	case CMD_LIST|CMD_ZERO_NUM:
1089
1090
1091
1092
1093
1094
		ret = list_entries(h, p.chain, p.table, p.rulenum,
				   cs.options & OPT_VERBOSE,
				   cs.options & OPT_NUMERIC,
				   cs.options & OPT_EXPANDED,
				   cs.options & OPT_LINENUMBERS);
		if (ret && (p.command & CMD_ZERO)) {
1095
			ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
1096
						      cs.options & OPT_VERBOSE);
1097
1098
		}
		if (ret && (p.command & CMD_ZERO_NUM)) {
1099
			ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
1100
1101
						     p.rulenum - 1);
		}
1102
		nft_check_xt_legacy(h->family, false);
1103
1104
1105
1106
		break;
	case CMD_LIST_RULES:
	case CMD_LIST_RULES|CMD_ZERO:
	case CMD_LIST_RULES|CMD_ZERO_NUM:
1107
1108
1109
		ret = list_rules(h, p.chain, p.table, p.rulenum,
				 cs.options & OPT_VERBOSE);
		if (ret && (p.command & CMD_ZERO)) {
1110
			ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
1111
						      cs.options & OPT_VERBOSE);
1112
1113
		}
		if (ret && (p.command & CMD_ZERO_NUM)) {
1114
			ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
1115
1116
						     p.rulenum - 1);
		}
1117
		nft_check_xt_legacy(h->family, false);
1118
1119
		break;
	case CMD_NEW_CHAIN:
1120
		ret = nft_cmd_chain_user_add(h, p.chain, p.table);
1121
1122
		break;
	case CMD_DELETE_CHAIN:
1123
		ret = nft_cmd_chain_user_del(h, p.chain, p.table,
1124
					 cs.options & OPT_VERBOSE);
1125
1126
		break;
	case CMD_RENAME_CHAIN:
1127
		ret = nft_cmd_chain_user_rename(h, p.chain, p.table, p.newname);
1128
1129
		break;
	case CMD_SET_POLICY:
1130
		ret = nft_cmd_chain_set(h, p.table, p.chain, p.policy, NULL);
1131
		break;
1132
1133
1134
	case CMD_NONE:
	/* do_parse ignored the line (eg: -4 with ip6tables-restore) */
		break;
1135
1136
1137
1138
1139
	default:
		/* We should never reach this... */
		exit_tryhelp(2);
	}

1140
	*table = p.table;
1141

1142
	nft_clear_iptables_command_state(&cs);
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158

	if (h->family == AF_INET) {
		free(args.s.addr.v4);
		free(args.s.mask.v4);
		free(args.d.addr.v4);
		free(args.d.mask.v4);
	} else if (h->family == AF_INET6) {
		free(args.s.addr.v6);
		free(args.s.mask.v6);
		free(args.d.addr.v6);
		free(args.d.mask.v6);
	}
	xtables_free_opts(1);

	return ret;
}