• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Code to take an iptables-style command line and do it. */
2 
3 /*
4  * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
5  *
6  * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
7  *		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
8  *		    Marc Boucher <marc+nf@mbsi.ca>
9  *		    James Morris <jmorris@intercode.com.au>
10  *		    Harald Welte <laforge@gnumonks.org>
11  *		    Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
12  *
13  *	This program is free software; you can redistribute it and/or modify
14  *	it under the terms of the GNU General Public License as published by
15  *	the Free Software Foundation; either version 2 of the License, or
16  *	(at your option) any later version.
17  *
18  *	This program is distributed in the hope that it will be useful,
19  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *	GNU General Public License for more details.
22  *
23  *	You should have received a copy of the GNU General Public License
24  *	along with this program; if not, write to the Free Software
25  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27 #include "config.h"
28 #include <getopt.h>
29 #include <string.h>
30 #include <netdb.h>
31 #include <errno.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37 #include <limits.h>
38 #include <unistd.h>
39 #include <iptables.h>
40 #include <xtables.h>
41 #include <fcntl.h>
42 #include "xshared.h"
43 #include "nft-shared.h"
44 #include "nft.h"
45 
46 static struct option original_opts[] = {
47 	{.name = "append",	  .has_arg = 1, .val = 'A'},
48 	{.name = "delete",	  .has_arg = 1, .val = 'D'},
49 	{.name = "check",	  .has_arg = 1, .val = 'C'},
50 	{.name = "insert",	  .has_arg = 1, .val = 'I'},
51 	{.name = "replace",	  .has_arg = 1, .val = 'R'},
52 	{.name = "list",	  .has_arg = 2, .val = 'L'},
53 	{.name = "list-rules",	  .has_arg = 2, .val = 'S'},
54 	{.name = "flush",	  .has_arg = 2, .val = 'F'},
55 	{.name = "zero",	  .has_arg = 2, .val = 'Z'},
56 	{.name = "new-chain",	  .has_arg = 1, .val = 'N'},
57 	{.name = "delete-chain",  .has_arg = 2, .val = 'X'},
58 	{.name = "rename-chain",  .has_arg = 1, .val = 'E'},
59 	{.name = "policy",	  .has_arg = 1, .val = 'P'},
60 	{.name = "source",	  .has_arg = 1, .val = 's'},
61 	{.name = "destination",   .has_arg = 1, .val = 'd'},
62 	{.name = "src",		  .has_arg = 1, .val = 's'}, /* synonym */
63 	{.name = "dst",		  .has_arg = 1, .val = 'd'}, /* synonym */
64 	{.name = "protocol",	  .has_arg = 1, .val = 'p'},
65 	{.name = "in-interface",  .has_arg = 1, .val = 'i'},
66 	{.name = "jump",	  .has_arg = 1, .val = 'j'},
67 	{.name = "table",	  .has_arg = 1, .val = 't'},
68 	{.name = "match",	  .has_arg = 1, .val = 'm'},
69 	{.name = "numeric",	  .has_arg = 0, .val = 'n'},
70 	{.name = "out-interface", .has_arg = 1, .val = 'o'},
71 	{.name = "verbose",	  .has_arg = 0, .val = 'v'},
72 	{.name = "wait",	  .has_arg = 2, .val = 'w'},
73 	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
74 	{.name = "exact",	  .has_arg = 0, .val = 'x'},
75 	{.name = "fragments",	  .has_arg = 0, .val = 'f'},
76 	{.name = "version",	  .has_arg = 0, .val = 'V'},
77 	{.name = "help",	  .has_arg = 2, .val = 'h'},
78 	{.name = "line-numbers",  .has_arg = 0, .val = '0'},
79 	{.name = "modprobe",	  .has_arg = 1, .val = 'M'},
80 	{.name = "set-counters",  .has_arg = 1, .val = 'c'},
81 	{.name = "goto",	  .has_arg = 1, .val = 'g'},
82 	{.name = "ipv4",	  .has_arg = 0, .val = '4'},
83 	{.name = "ipv6",	  .has_arg = 0, .val = '6'},
84 	{NULL},
85 };
86 
87 void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
88 
89 struct xtables_globals xtables_globals = {
90 	.option_offset = 0,
91 	.program_version = PACKAGE_VERSION,
92 	.orig_opts = original_opts,
93 	.exit_err = xtables_exit_error,
94 	.compat_rev = nft_compatible_revision,
95 };
96 
97 static const int inverse_for_options[NUMBER_OF_OPT] =
98 {
99 /* -n */ 0,
100 /* -s */ IPT_INV_SRCIP,
101 /* -d */ IPT_INV_DSTIP,
102 /* -p */ XT_INV_PROTO,
103 /* -j */ 0,
104 /* -v */ 0,
105 /* -x */ 0,
106 /* -i */ IPT_INV_VIA_IN,
107 /* -o */ IPT_INV_VIA_OUT,
108 /*--line*/ 0,
109 /* -c */ 0,
110 /* -f */ IPT_INV_FRAG,
111 };
112 
113 #define opts xt_params->opts
114 #define prog_name xt_params->program_name
115 #define prog_vers xt_params->program_version
116 
117 static void __attribute__((noreturn))
exit_tryhelp(int status)118 exit_tryhelp(int status)
119 {
120 	if (line != -1)
121 		fprintf(stderr, "Error occurred at line: %d\n", line);
122 	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
123 			prog_name, prog_name);
124 	xtables_free_opts(1);
125 	exit(status);
126 }
127 
128 static void
printhelp(const struct xtables_rule_match * matches)129 printhelp(const struct xtables_rule_match *matches)
130 {
131 	printf("%s v%s\n\n"
132 "Usage: %s -[ACD] chain rule-specification [options]\n"
133 "	%s -I chain [rulenum] rule-specification [options]\n"
134 "	%s -R chain rulenum rule-specification [options]\n"
135 "	%s -D chain rulenum [options]\n"
136 "	%s -[LS] [chain [rulenum]] [options]\n"
137 "	%s -[FZ] [chain] [options]\n"
138 "	%s -[NX] chain\n"
139 "	%s -E old-chain-name new-chain-name\n"
140 "	%s -P chain target [options]\n"
141 "	%s -h (print this help information)\n\n",
142 	       prog_name, prog_vers, prog_name, prog_name,
143 	       prog_name, prog_name, prog_name, prog_name,
144 	       prog_name, prog_name, prog_name, prog_name);
145 
146 	printf(
147 "Commands:\n"
148 "Either long or short options are allowed.\n"
149 "  --append  -A chain		Append to chain\n"
150 "  --check   -C chain		Check for the existence of a rule\n"
151 "  --delete  -D chain		Delete matching rule from chain\n"
152 "  --delete  -D chain rulenum\n"
153 "				Delete rule rulenum (1 = first) from chain\n"
154 "  --insert  -I chain [rulenum]\n"
155 "				Insert in chain as rulenum (default 1=first)\n"
156 "  --replace -R chain rulenum\n"
157 "				Replace rule rulenum (1 = first) in chain\n"
158 "  --list    -L [chain [rulenum]]\n"
159 "				List the rules in a chain or all chains\n"
160 "  --list-rules -S [chain [rulenum]]\n"
161 "				Print the rules in a chain or all chains\n"
162 "  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
163 "  --zero    -Z [chain [rulenum]]\n"
164 "				Zero counters in chain or all chains\n"
165 "  --new     -N chain		Create a new user-defined chain\n"
166 "  --delete-chain\n"
167 "	     -X [chain]		Delete a user-defined chain\n"
168 "  --policy  -P chain target\n"
169 "				Change policy on chain to target\n"
170 "  --rename-chain\n"
171 "	     -E old-chain new-chain\n"
172 "				Change chain name, (moving any references)\n"
173 
174 "Options:\n"
175 "    --ipv4	-4		Nothing (line is ignored by ip6tables-restore)\n"
176 "    --ipv6	-6		Error (line is ignored by iptables-restore)\n"
177 "[!] --proto	-p proto	protocol: by number or name, eg. `tcp'\n"
178 "[!] --source	-s address[/mask][...]\n"
179 "				source specification\n"
180 "[!] --destination -d address[/mask][...]\n"
181 "				destination specification\n"
182 "[!] --in-interface -i input name[+]\n"
183 "				network interface name ([+] for wildcard)\n"
184 " --jump	-j target\n"
185 "				target for rule (may load target extension)\n"
186 #ifdef IPT_F_GOTO
187 "  --goto      -g chain\n"
188 "			       jump to chain with no return\n"
189 #endif
190 "  --match	-m match\n"
191 "				extended match (may load extension)\n"
192 "  --numeric	-n		numeric output of addresses and ports\n"
193 "[!] --out-interface -o output name[+]\n"
194 "				network interface name ([+] for wildcard)\n"
195 "  --table	-t table	table to manipulate (default: `filter')\n"
196 "  --verbose	-v		verbose mode\n"
197 "  --wait	-w [seconds]	maximum wait to acquire xtables lock before give up\n"
198 "  --wait-interval -W [usecs]	wait time to try to acquire xtables lock\n"
199 "				default is 1 second\n"
200 "  --line-numbers		print line numbers when listing\n"
201 "  --exact	-x		expand numbers (display exact values)\n"
202 "[!] --fragment	-f		match second or further fragments only\n"
203 "  --modprobe=<command>		try to insert modules using this command\n"
204 "  --set-counters PKTS BYTES	set the counter during insert/append\n"
205 "[!] --version	-V		print package version.\n");
206 
207 	print_extension_helps(xtables_targets, matches);
208 }
209 
210 void
xtables_exit_error(enum xtables_exittype status,const char * msg,...)211 xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
212 {
213 	va_list args;
214 
215 	va_start(args, msg);
216 	fprintf(stderr, "%s v%s (nf_tables): ", prog_name, prog_vers);
217 	vfprintf(stderr, msg, args);
218 	va_end(args);
219 	fprintf(stderr, "\n");
220 	if (status == PARAMETER_PROBLEM)
221 		exit_tryhelp(status);
222 	if (status == VERSION_PROBLEM)
223 		fprintf(stderr,
224 			"Perhaps iptables or your kernel needs to be upgraded.\n");
225 	/* On error paths, make sure that we don't leak memory */
226 	xtables_free_opts(1);
227 	exit(status);
228 }
229 
230 /*
231  *	All functions starting with "parse" should succeed, otherwise
232  *	the program fails.
233  *	Most routines return pointers to static data that may change
234  *	between calls to the same or other routines with a few exceptions:
235  *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
236  *	return global static data.
237 */
238 
239 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
240 
241 static void
set_option(unsigned int * options,unsigned int option,uint8_t * invflg,int invert)242 set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
243 	   int invert)
244 {
245 	if (*options & option)
246 		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
247 			   opt2char(option));
248 	*options |= option;
249 
250 	if (invert) {
251 		unsigned int i;
252 		for (i = 0; 1 << i != option; i++);
253 
254 		if (!inverse_for_options[i])
255 			xtables_error(PARAMETER_PROBLEM,
256 				   "cannot have ! before -%c",
257 				   opt2char(option));
258 		*invflg |= inverse_for_options[i];
259 	}
260 }
261 
262 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)263 add_entry(const char *chain,
264 	  const char *table,
265 	  struct iptables_command_state *cs,
266 	  int rulenum, int family,
267 	  const struct addr_mask s,
268 	  const struct addr_mask d,
269 	  bool verbose, struct nft_handle *h, bool append)
270 {
271 	unsigned int i, j;
272 	int ret = 1;
273 
274 	for (i = 0; i < s.naddrs; i++) {
275 		if (family == AF_INET) {
276 			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
277 			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
278 			for (j = 0; j < d.naddrs; j++) {
279 				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
280 				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
281 
282 				if (append) {
283 					ret = nft_cmd_rule_append(h, chain, table,
284 							      cs, NULL,
285 							      verbose);
286 				} else {
287 					ret = nft_cmd_rule_insert(h, chain, table,
288 							      cs, rulenum,
289 							      verbose);
290 				}
291 			}
292 		} else if (family == AF_INET6) {
293 			memcpy(&cs->fw6.ipv6.src,
294 			       &s.addr.v6[i], sizeof(struct in6_addr));
295 			memcpy(&cs->fw6.ipv6.smsk,
296 			       &s.mask.v6[i], sizeof(struct in6_addr));
297 			for (j = 0; j < d.naddrs; j++) {
298 				memcpy(&cs->fw6.ipv6.dst,
299 				       &d.addr.v6[j], sizeof(struct in6_addr));
300 				memcpy(&cs->fw6.ipv6.dmsk,
301 				       &d.mask.v6[j], sizeof(struct in6_addr));
302 				if (append) {
303 					ret = nft_cmd_rule_append(h, chain, table,
304 							      cs, NULL,
305 							      verbose);
306 				} else {
307 					ret = nft_cmd_rule_insert(h, chain, table,
308 							      cs, rulenum,
309 							      verbose);
310 				}
311 			}
312 		}
313 	}
314 
315 	return ret;
316 }
317 
318 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)319 replace_entry(const char *chain, const char *table,
320 	      struct iptables_command_state *cs,
321 	      unsigned int rulenum,
322 	      int family,
323 	      const struct addr_mask s,
324 	      const struct addr_mask d,
325 	      bool verbose, struct nft_handle *h)
326 {
327 	if (family == AF_INET) {
328 		cs->fw.ip.src.s_addr = s.addr.v4->s_addr;
329 		cs->fw.ip.dst.s_addr = d.addr.v4->s_addr;
330 		cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr;
331 		cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr;
332 	} else if (family == AF_INET6) {
333 		memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr));
334 		memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr));
335 		memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr));
336 		memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr));
337 	} else
338 		return 1;
339 
340 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
341 }
342 
343 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)344 delete_entry(const char *chain, const char *table,
345 	     struct iptables_command_state *cs,
346 	     int family,
347 	     const struct addr_mask s,
348 	     const struct addr_mask d,
349 	     bool verbose,
350 	     struct nft_handle *h)
351 {
352 	unsigned int i, j;
353 	int ret = 1;
354 
355 	for (i = 0; i < s.naddrs; i++) {
356 		if (family == AF_INET) {
357 			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
358 			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
359 			for (j = 0; j < d.naddrs; j++) {
360 				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
361 				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
362 				ret = nft_cmd_rule_delete(h, chain,
363 						      table, cs, verbose);
364 			}
365 		} else if (family == AF_INET6) {
366 			memcpy(&cs->fw6.ipv6.src,
367 			       &s.addr.v6[i], sizeof(struct in6_addr));
368 			memcpy(&cs->fw6.ipv6.smsk,
369 			       &s.mask.v6[i], sizeof(struct in6_addr));
370 			for (j = 0; j < d.naddrs; j++) {
371 				memcpy(&cs->fw6.ipv6.dst,
372 				       &d.addr.v6[j], sizeof(struct in6_addr));
373 				memcpy(&cs->fw6.ipv6.dmsk,
374 				       &d.mask.v6[j], sizeof(struct in6_addr));
375 				ret = nft_cmd_rule_delete(h, chain,
376 						      table, cs, verbose);
377 			}
378 		}
379 	}
380 
381 	return ret;
382 }
383 
384 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)385 check_entry(const char *chain, const char *table,
386 	    struct iptables_command_state *cs,
387 	    int family,
388 	    const struct addr_mask s,
389 	    const struct addr_mask d,
390 	    bool verbose, struct nft_handle *h)
391 {
392 	unsigned int i, j;
393 	int ret = 1;
394 
395 	for (i = 0; i < s.naddrs; i++) {
396 		if (family == AF_INET) {
397 			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
398 			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
399 			for (j = 0; j < d.naddrs; j++) {
400 				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
401 				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
402 				ret = nft_cmd_rule_check(h, chain,
403 						     table, cs, verbose);
404 			}
405 		} else if (family == AF_INET6) {
406 			memcpy(&cs->fw6.ipv6.src,
407 			       &s.addr.v6[i], sizeof(struct in6_addr));
408 			memcpy(&cs->fw6.ipv6.smsk,
409 			       &s.mask.v6[i], sizeof(struct in6_addr));
410 			for (j = 0; j < d.naddrs; j++) {
411 				memcpy(&cs->fw6.ipv6.dst,
412 				       &d.addr.v6[j], sizeof(struct in6_addr));
413 				memcpy(&cs->fw6.ipv6.dmsk,
414 				       &d.mask.v6[j], sizeof(struct in6_addr));
415 				ret = nft_cmd_rule_check(h, chain,
416 						     table, cs, verbose);
417 			}
418 		}
419 	}
420 
421 	return ret;
422 }
423 
424 static int
list_entries(struct nft_handle * h,const char * chain,const char * table,int rulenum,int verbose,int numeric,int expanded,int linenumbers)425 list_entries(struct nft_handle *h, const char *chain, const char *table,
426 	     int rulenum, int verbose, int numeric, int expanded,
427 	     int linenumbers)
428 {
429 	unsigned int format;
430 
431 	format = FMT_OPTIONS;
432 	if (!verbose)
433 		format |= FMT_NOCOUNTS;
434 	else
435 		format |= FMT_VIA;
436 
437 	if (numeric)
438 		format |= FMT_NUMERIC;
439 
440 	if (!expanded)
441 		format |= FMT_KILOMEGAGIGA;
442 
443 	if (linenumbers)
444 		format |= FMT_LINENUMBERS;
445 
446 	return nft_cmd_rule_list(h, chain, table, rulenum, format);
447 }
448 
449 static int
list_rules(struct nft_handle * h,const char * chain,const char * table,int rulenum,int counters)450 list_rules(struct nft_handle *h, const char *chain, const char *table,
451 	   int rulenum, int counters)
452 {
453 	if (counters)
454 	    counters = -1;		/* iptables -c format */
455 
456 	return nft_cmd_rule_list_save(h, chain, table, rulenum, counters);
457 }
458 
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)459 void do_parse(struct nft_handle *h, int argc, char *argv[],
460 	      struct nft_xt_cmd_parse *p, struct iptables_command_state *cs,
461 	      struct xtables_args *args)
462 {
463 	struct xtables_match *m;
464 	struct xtables_rule_match *matchp;
465 	bool wait_interval_set = false;
466 	struct timeval wait_interval;
467 	struct xtables_target *t;
468 	bool table_set = false;
469 	int wait = 0;
470 
471 	memset(cs, 0, sizeof(*cs));
472 	cs->jumpto = "";
473 	cs->argv = argv;
474 
475 	/* re-set optind to 0 in case do_command4 gets called
476 	 * a second time */
477 	optind = 0;
478 
479 	/* clear mflags in case do_command4 gets called a second time
480 	 * (we clear the global list of all matches for security)*/
481 	for (m = xtables_matches; m; m = m->next)
482 		m->mflags = 0;
483 
484 	for (t = xtables_targets; t; t = t->next) {
485 		t->tflags = 0;
486 		t->used = 0;
487 	}
488 
489 	/* Suppress error messages: we may add new options if we
490 	   demand-load a protocol. */
491 	opterr = 0;
492 
493 	opts = xt_params->orig_opts;
494 	while ((cs->c = getopt_long(argc, argv,
495 	   "-: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",
496 					   opts, NULL)) != -1) {
497 		switch (cs->c) {
498 			/*
499 			 * Command selection
500 			 */
501 		case 'A':
502 			add_command(&p->command, CMD_APPEND, CMD_NONE,
503 				    cs->invert);
504 			p->chain = optarg;
505 			break;
506 
507 		case 'C':
508 			add_command(&p->command, CMD_CHECK, CMD_NONE,
509 				    cs->invert);
510 			p->chain = optarg;
511 			break;
512 
513 		case 'D':
514 			add_command(&p->command, CMD_DELETE, CMD_NONE,
515 				    cs->invert);
516 			p->chain = optarg;
517 			if (xs_has_arg(argc, argv)) {
518 				p->rulenum = parse_rulenumber(argv[optind++]);
519 				p->command = CMD_DELETE_NUM;
520 			}
521 			break;
522 
523 		case 'R':
524 			add_command(&p->command, CMD_REPLACE, CMD_NONE,
525 				    cs->invert);
526 			p->chain = optarg;
527 			if (xs_has_arg(argc, argv))
528 				p->rulenum = parse_rulenumber(argv[optind++]);
529 			else
530 				xtables_error(PARAMETER_PROBLEM,
531 					   "-%c requires a rule number",
532 					   cmd2char(CMD_REPLACE));
533 			break;
534 
535 		case 'I':
536 			add_command(&p->command, CMD_INSERT, CMD_NONE,
537 				    cs->invert);
538 			p->chain = optarg;
539 			if (xs_has_arg(argc, argv))
540 				p->rulenum = parse_rulenumber(argv[optind++]);
541 			else
542 				p->rulenum = 1;
543 			break;
544 
545 		case 'L':
546 			add_command(&p->command, CMD_LIST,
547 				    CMD_ZERO | CMD_ZERO_NUM, cs->invert);
548 			if (optarg)
549 				p->chain = optarg;
550 			else if (xs_has_arg(argc, argv))
551 				p->chain = argv[optind++];
552 			if (xs_has_arg(argc, argv))
553 				p->rulenum = parse_rulenumber(argv[optind++]);
554 			break;
555 
556 		case 'S':
557 			add_command(&p->command, CMD_LIST_RULES,
558 				    CMD_ZERO|CMD_ZERO_NUM, cs->invert);
559 			if (optarg)
560 				p->chain = optarg;
561 			else if (xs_has_arg(argc, argv))
562 				p->chain = argv[optind++];
563 			if (xs_has_arg(argc, argv))
564 				p->rulenum = parse_rulenumber(argv[optind++]);
565 			break;
566 
567 		case 'F':
568 			add_command(&p->command, CMD_FLUSH, CMD_NONE,
569 				    cs->invert);
570 			if (optarg)
571 				p->chain = optarg;
572 			else if (xs_has_arg(argc, argv))
573 				p->chain = argv[optind++];
574 			break;
575 
576 		case 'Z':
577 			add_command(&p->command, CMD_ZERO,
578 				    CMD_LIST|CMD_LIST_RULES, cs->invert);
579 			if (optarg)
580 				p->chain = optarg;
581 			else if (xs_has_arg(argc, argv))
582 				p->chain = argv[optind++];
583 			if (xs_has_arg(argc, argv)) {
584 				p->rulenum = parse_rulenumber(argv[optind++]);
585 				p->command = CMD_ZERO_NUM;
586 			}
587 			break;
588 
589 		case 'N':
590 			if (optarg && (*optarg == '-' || *optarg == '!'))
591 				xtables_error(PARAMETER_PROBLEM,
592 					   "chain name not allowed to start "
593 					   "with `%c'\n", *optarg);
594 			if (xtables_find_target(optarg, XTF_TRY_LOAD))
595 				xtables_error(PARAMETER_PROBLEM,
596 					   "chain name may not clash "
597 					   "with target name\n");
598 			add_command(&p->command, CMD_NEW_CHAIN, CMD_NONE,
599 				    cs->invert);
600 			p->chain = optarg;
601 			break;
602 
603 		case 'X':
604 			add_command(&p->command, CMD_DELETE_CHAIN, CMD_NONE,
605 				    cs->invert);
606 			if (optarg)
607 				p->chain = optarg;
608 			else if (xs_has_arg(argc, argv))
609 				p->chain = argv[optind++];
610 			break;
611 
612 		case 'E':
613 			add_command(&p->command, CMD_RENAME_CHAIN, CMD_NONE,
614 				    cs->invert);
615 			p->chain = optarg;
616 			if (xs_has_arg(argc, argv))
617 				p->newname = argv[optind++];
618 			else
619 				xtables_error(PARAMETER_PROBLEM,
620 					   "-%c requires old-chain-name and "
621 					   "new-chain-name",
622 					    cmd2char(CMD_RENAME_CHAIN));
623 			break;
624 
625 		case 'P':
626 			add_command(&p->command, CMD_SET_POLICY, CMD_NONE,
627 				    cs->invert);
628 			p->chain = optarg;
629 			if (xs_has_arg(argc, argv))
630 				p->policy = argv[optind++];
631 			else
632 				xtables_error(PARAMETER_PROBLEM,
633 					   "-%c requires a chain and a policy",
634 					   cmd2char(CMD_SET_POLICY));
635 			break;
636 
637 		case 'h':
638 			if (!optarg)
639 				optarg = argv[optind];
640 
641 			/* iptables -p icmp -h */
642 			if (!cs->matches && cs->protocol)
643 				xtables_find_match(cs->protocol,
644 					XTF_TRY_LOAD, &cs->matches);
645 
646 			printhelp(cs->matches);
647 			p->command = CMD_NONE;
648 			return;
649 
650 			/*
651 			 * Option selection
652 			 */
653 		case 'p':
654 			set_option(&cs->options, OPT_PROTOCOL,
655 				   &args->invflags, cs->invert);
656 
657 			/* Canonicalize into lower case */
658 			for (cs->protocol = optarg; *cs->protocol; cs->protocol++)
659 				*cs->protocol = tolower(*cs->protocol);
660 
661 			cs->protocol = optarg;
662 			args->proto = xtables_parse_protocol(cs->protocol);
663 
664 			if (args->proto == 0 &&
665 			    (args->invflags & XT_INV_PROTO))
666 				xtables_error(PARAMETER_PROBLEM,
667 					   "rule would never match protocol");
668 
669 			/* This needs to happen here to parse extensions */
670 			h->ops->proto_parse(cs, args);
671 			break;
672 
673 		case 's':
674 			set_option(&cs->options, OPT_SOURCE,
675 				   &args->invflags, cs->invert);
676 			args->shostnetworkmask = optarg;
677 			break;
678 
679 		case 'd':
680 			set_option(&cs->options, OPT_DESTINATION,
681 				   &args->invflags, cs->invert);
682 			args->dhostnetworkmask = optarg;
683 			break;
684 
685 #ifdef IPT_F_GOTO
686 		case 'g':
687 			set_option(&cs->options, OPT_JUMP, &args->invflags,
688 				   cs->invert);
689 			args->goto_set = true;
690 			cs->jumpto = xt_parse_target(optarg);
691 			break;
692 #endif
693 
694 		case 'j':
695 			set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags,
696 				   cs->invert);
697 			command_jump(cs, optarg);
698 			break;
699 
700 
701 		case 'i':
702 			if (*optarg == '\0')
703 				xtables_error(PARAMETER_PROBLEM,
704 					"Empty interface is likely to be "
705 					"undesired");
706 			set_option(&cs->options, OPT_VIANAMEIN,
707 				   &args->invflags, cs->invert);
708 			xtables_parse_interface(optarg,
709 						args->iniface,
710 						args->iniface_mask);
711 			break;
712 
713 		case 'o':
714 			if (*optarg == '\0')
715 				xtables_error(PARAMETER_PROBLEM,
716 					"Empty interface is likely to be "
717 					"undesired");
718 			set_option(&cs->options, OPT_VIANAMEOUT,
719 				   &args->invflags, cs->invert);
720 			xtables_parse_interface(optarg,
721 						args->outiface,
722 						args->outiface_mask);
723 			break;
724 
725 		case 'f':
726 			if (args->family == AF_INET6) {
727 				xtables_error(PARAMETER_PROBLEM,
728 					"`-f' is not supported in IPv6, "
729 					"use -m frag instead");
730 			}
731 			set_option(&cs->options, OPT_FRAGMENT, &args->invflags,
732 				   cs->invert);
733 			args->flags |= IPT_F_FRAG;
734 			break;
735 
736 		case 'v':
737 			if (!p->verbose)
738 				set_option(&cs->options, OPT_VERBOSE,
739 					   &args->invflags, cs->invert);
740 			p->verbose++;
741 			break;
742 
743 		case 'm':
744 			command_match(cs);
745 			break;
746 
747 		case 'n':
748 			set_option(&cs->options, OPT_NUMERIC, &args->invflags,
749 				   cs->invert);
750 			break;
751 
752 		case 't':
753 			if (cs->invert)
754 				xtables_error(PARAMETER_PROBLEM,
755 					   "unexpected ! flag before --table");
756 			if (p->restore && table_set)
757 				xtables_error(PARAMETER_PROBLEM,
758 					      "The -t option (seen in line %u) cannot be used in %s.\n",
759 					      line, xt_params->program_name);
760 			if (!nft_table_builtin_find(h, optarg))
761 				xtables_error(VERSION_PROBLEM,
762 					      "table '%s' does not exist",
763 					      optarg);
764 			p->table = optarg;
765 			table_set = true;
766 			break;
767 
768 		case 'x':
769 			set_option(&cs->options, OPT_EXPANDED, &args->invflags,
770 				   cs->invert);
771 			break;
772 
773 		case 'V':
774 			if (cs->invert)
775 				printf("Not %s ;-)\n", prog_vers);
776 			else
777 				printf("%s v%s (nf_tables)\n",
778 				       prog_name, prog_vers);
779 			exit(0);
780 
781 		case 'w':
782 			if (p->restore) {
783 				xtables_error(PARAMETER_PROBLEM,
784 					      "You cannot use `-w' from "
785 					      "iptables-restore");
786 			}
787 
788 			wait = parse_wait_time(argc, argv);
789 			break;
790 
791 		case 'W':
792 			if (p->restore) {
793 				xtables_error(PARAMETER_PROBLEM,
794 					      "You cannot use `-W' from "
795 					      "iptables-restore");
796 			}
797 
798 			parse_wait_interval(argc, argv, &wait_interval);
799 			wait_interval_set = true;
800 			break;
801 
802 		case '0':
803 			set_option(&cs->options, OPT_LINENUMBERS,
804 				   &args->invflags, cs->invert);
805 			break;
806 
807 		case 'M':
808 			xtables_modprobe_program = optarg;
809 			break;
810 
811 		case 'c':
812 			set_option(&cs->options, OPT_COUNTERS, &args->invflags,
813 				   cs->invert);
814 			args->pcnt = optarg;
815 			args->bcnt = strchr(args->pcnt + 1, ',');
816 			if (args->bcnt)
817 			    args->bcnt++;
818 			if (!args->bcnt && xs_has_arg(argc, argv))
819 				args->bcnt = argv[optind++];
820 			if (!args->bcnt)
821 				xtables_error(PARAMETER_PROBLEM,
822 					"-%c requires packet and byte counter",
823 					opt2char(OPT_COUNTERS));
824 
825 			if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1)
826 				xtables_error(PARAMETER_PROBLEM,
827 					"-%c packet counter not numeric",
828 					opt2char(OPT_COUNTERS));
829 
830 			if (sscanf(args->bcnt, "%llu", &args->bcnt_cnt) != 1)
831 				xtables_error(PARAMETER_PROBLEM,
832 					"-%c byte counter not numeric",
833 					opt2char(OPT_COUNTERS));
834 			break;
835 
836 		case '4':
837 			if (args->family == AF_INET)
838 				break;
839 
840 			if (p->restore && args->family == AF_INET6)
841 				return;
842 
843 			exit_tryhelp(2);
844 
845 		case '6':
846 			if (args->family == AF_INET6)
847 				break;
848 
849 			if (p->restore && args->family == AF_INET)
850 				return;
851 
852 			exit_tryhelp(2);
853 
854 		case 1: /* non option */
855 			if (optarg[0] == '!' && optarg[1] == '\0') {
856 				if (cs->invert)
857 					xtables_error(PARAMETER_PROBLEM,
858 						   "multiple consecutive ! not"
859 						   " allowed");
860 				cs->invert = true;
861 				optarg[0] = '\0';
862 				continue;
863 			}
864 			fprintf(stderr, "Bad argument `%s'\n", optarg);
865 			exit_tryhelp(2);
866 
867 		default:
868 			if (command_default(cs, &xtables_globals) == 1)
869 				/* cf. ip6tables.c */
870 				continue;
871 			break;
872 		}
873 		cs->invert = false;
874 	}
875 
876 	if (strcmp(p->table, "nat") == 0 &&
877 	    ((p->policy != NULL && strcmp(p->policy, "DROP") == 0) ||
878 	    (cs->jumpto != NULL && strcmp(cs->jumpto, "DROP") == 0)))
879 		xtables_error(PARAMETER_PROBLEM,
880 			"\nThe \"nat\" table is not intended for filtering, "
881 			"the use of DROP is therefore inhibited.\n\n");
882 
883 	if (!wait && wait_interval_set)
884 		xtables_error(PARAMETER_PROBLEM,
885 			      "--wait-interval only makes sense with --wait\n");
886 
887 	for (matchp = cs->matches; matchp; matchp = matchp->next)
888 		xtables_option_mfcall(matchp->match);
889 	if (cs->target != NULL)
890 		xtables_option_tfcall(cs->target);
891 
892 	/* Fix me: must put inverse options checking here --MN */
893 
894 	if (optind < argc)
895 		xtables_error(PARAMETER_PROBLEM,
896 			   "unknown arguments found on commandline");
897 	if (!p->command)
898 		xtables_error(PARAMETER_PROBLEM, "no command specified");
899 	if (cs->invert)
900 		xtables_error(PARAMETER_PROBLEM,
901 			   "nothing appropriate following !");
902 
903 	/* Set only if required, needed by xtables-restore */
904 	if (h->family == AF_UNSPEC)
905 		h->family = args->family;
906 
907 	h->ops->post_parse(p->command, cs, args);
908 
909 	if (p->command == CMD_REPLACE &&
910 	    (args->s.naddrs != 1 || args->d.naddrs != 1))
911 		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
912 			   "specify a unique address");
913 
914 	generic_opt_check(p->command, cs->options);
915 
916 	if (p->chain != NULL && strlen(p->chain) >= XT_EXTENSION_MAXNAMELEN)
917 		xtables_error(PARAMETER_PROBLEM,
918 			   "chain name `%s' too long (must be under %u chars)",
919 			   p->chain, XT_EXTENSION_MAXNAMELEN);
920 
921 	if (p->command == CMD_APPEND ||
922 	    p->command == CMD_DELETE ||
923 	    p->command == CMD_DELETE_NUM ||
924 	    p->command == CMD_CHECK ||
925 	    p->command == CMD_INSERT ||
926 	    p->command == CMD_REPLACE) {
927 		if (strcmp(p->chain, "PREROUTING") == 0
928 		    || strcmp(p->chain, "INPUT") == 0) {
929 			/* -o not valid with incoming packets. */
930 			if (cs->options & OPT_VIANAMEOUT)
931 				xtables_error(PARAMETER_PROBLEM,
932 					   "Can't use -%c with %s\n",
933 					   opt2char(OPT_VIANAMEOUT),
934 					   p->chain);
935 		}
936 
937 		if (strcmp(p->chain, "POSTROUTING") == 0
938 		    || strcmp(p->chain, "OUTPUT") == 0) {
939 			/* -i not valid with outgoing packets */
940 			if (cs->options & OPT_VIANAMEIN)
941 				xtables_error(PARAMETER_PROBLEM,
942 					   "Can't use -%c with %s\n",
943 					   opt2char(OPT_VIANAMEIN),
944 					   p->chain);
945 		}
946 	}
947 }
948 
do_commandx(struct nft_handle * h,int argc,char * argv[],char ** table,bool restore)949 int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
950 		bool restore)
951 {
952 	int ret = 1;
953 	struct nft_xt_cmd_parse p = {
954 		.table		= *table,
955 		.restore	= restore,
956 	};
957 	struct iptables_command_state cs;
958 	struct xtables_args args = {
959 		.family = h->family,
960 	};
961 
962 	do_parse(h, argc, argv, &p, &cs, &args);
963 
964 	switch (p.command) {
965 	case CMD_APPEND:
966 		ret = add_entry(p.chain, p.table, &cs, 0, h->family,
967 				args.s, args.d,
968 				cs.options & OPT_VERBOSE, h, true);
969 		break;
970 	case CMD_DELETE:
971 		ret = delete_entry(p.chain, p.table, &cs, h->family,
972 				   args.s, args.d,
973 				   cs.options & OPT_VERBOSE, h);
974 		break;
975 	case CMD_DELETE_NUM:
976 		ret = nft_cmd_rule_delete_num(h, p.chain, p.table,
977 					      p.rulenum - 1, p.verbose);
978 		break;
979 	case CMD_CHECK:
980 		ret = check_entry(p.chain, p.table, &cs, h->family,
981 				  args.s, args.d,
982 				  cs.options & OPT_VERBOSE, h);
983 		break;
984 	case CMD_REPLACE:
985 		ret = replace_entry(p.chain, p.table, &cs, p.rulenum - 1,
986 				    h->family, args.s, args.d,
987 				    cs.options & OPT_VERBOSE, h);
988 		break;
989 	case CMD_INSERT:
990 		ret = add_entry(p.chain, p.table, &cs, p.rulenum - 1,
991 				h->family, args.s, args.d,
992 				cs.options&OPT_VERBOSE, h, false);
993 		break;
994 	case CMD_FLUSH:
995 		ret = nft_cmd_rule_flush(h, p.chain, p.table,
996 					 cs.options & OPT_VERBOSE);
997 		break;
998 	case CMD_ZERO:
999 		ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
1000 						  cs.options & OPT_VERBOSE);
1001 		break;
1002 	case CMD_ZERO_NUM:
1003 		ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
1004 					     p.rulenum - 1);
1005 		break;
1006 	case CMD_LIST:
1007 	case CMD_LIST|CMD_ZERO:
1008 	case CMD_LIST|CMD_ZERO_NUM:
1009 		ret = list_entries(h, p.chain, p.table, p.rulenum,
1010 				   cs.options & OPT_VERBOSE,
1011 				   cs.options & OPT_NUMERIC,
1012 				   cs.options & OPT_EXPANDED,
1013 				   cs.options & OPT_LINENUMBERS);
1014 		if (ret && (p.command & CMD_ZERO)) {
1015 			ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
1016 						      cs.options & OPT_VERBOSE);
1017 		}
1018 		if (ret && (p.command & CMD_ZERO_NUM)) {
1019 			ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
1020 						     p.rulenum - 1);
1021 		}
1022 		nft_check_xt_legacy(h->family, false);
1023 		break;
1024 	case CMD_LIST_RULES:
1025 	case CMD_LIST_RULES|CMD_ZERO:
1026 	case CMD_LIST_RULES|CMD_ZERO_NUM:
1027 		ret = list_rules(h, p.chain, p.table, p.rulenum,
1028 				 cs.options & OPT_VERBOSE);
1029 		if (ret && (p.command & CMD_ZERO)) {
1030 			ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
1031 						      cs.options & OPT_VERBOSE);
1032 		}
1033 		if (ret && (p.command & CMD_ZERO_NUM)) {
1034 			ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
1035 						     p.rulenum - 1);
1036 		}
1037 		nft_check_xt_legacy(h->family, false);
1038 		break;
1039 	case CMD_NEW_CHAIN:
1040 		ret = nft_cmd_chain_user_add(h, p.chain, p.table);
1041 		break;
1042 	case CMD_DELETE_CHAIN:
1043 		ret = nft_cmd_chain_user_del(h, p.chain, p.table,
1044 					 cs.options & OPT_VERBOSE);
1045 		break;
1046 	case CMD_RENAME_CHAIN:
1047 		ret = nft_cmd_chain_user_rename(h, p.chain, p.table, p.newname);
1048 		break;
1049 	case CMD_SET_POLICY:
1050 		ret = nft_cmd_chain_set(h, p.table, p.chain, p.policy, NULL);
1051 		break;
1052 	case CMD_NONE:
1053 	/* do_parse ignored the line (eg: -4 with ip6tables-restore) */
1054 		break;
1055 	default:
1056 		/* We should never reach this... */
1057 		exit_tryhelp(2);
1058 	}
1059 
1060 	*table = p.table;
1061 
1062 	nft_clear_iptables_command_state(&cs);
1063 
1064 	if (h->family == AF_INET) {
1065 		free(args.s.addr.v4);
1066 		free(args.s.mask.v4);
1067 		free(args.d.addr.v4);
1068 		free(args.d.mask.v4);
1069 	} else if (h->family == AF_INET6) {
1070 		free(args.s.addr.v6);
1071 		free(args.s.mask.v6);
1072 		free(args.d.addr.v6);
1073 		free(args.d.mask.v6);
1074 	}
1075 	xtables_free_opts(1);
1076 
1077 	return ret;
1078 }
1079