Home | History | Annotate | Download | only in iptables
      1 /* Code to take an iptables-style command line and do it. */
      2 
      3 /*
      4  * Author: Paul.Russell (at) rustcorp.com.au and mneuling (at) radlogic.com.au
      5  *
      6  * (C) 2000-2002 by the netfilter coreteam <coreteam (at) netfilter.org>:
      7  *		    Paul 'Rusty' Russell <rusty (at) rustcorp.com.au>
      8  *		    Marc Boucher <marc+nf (at) mbsi.ca>
      9  *		    James Morris <jmorris (at) intercode.com.au>
     10  *		    Harald Welte <laforge (at) gnumonks.org>
     11  *		    Jozsef Kadlecsik <kadlec (at) 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 
     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 #ifndef TRUE
     47 #define TRUE 1
     48 #endif
     49 #ifndef FALSE
     50 #define FALSE 0
     51 #endif
     52 
     53 #define NUMBER_OF_CMD	16
     54 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
     55 				 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
     56 
     57 #define OPT_FRAGMENT	0x00800U
     58 #define NUMBER_OF_OPT	ARRAY_SIZE(optflags)
     59 static const char optflags[]
     60 = { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'};
     61 
     62 static struct option original_opts[] = {
     63 	{.name = "append",	  .has_arg = 1, .val = 'A'},
     64 	{.name = "delete",	  .has_arg = 1, .val = 'D'},
     65 	{.name = "check",	  .has_arg = 1, .val = 'C'},
     66 	{.name = "insert",	  .has_arg = 1, .val = 'I'},
     67 	{.name = "replace",	  .has_arg = 1, .val = 'R'},
     68 	{.name = "list",	  .has_arg = 2, .val = 'L'},
     69 	{.name = "list-rules",	  .has_arg = 2, .val = 'S'},
     70 	{.name = "flush",	  .has_arg = 2, .val = 'F'},
     71 	{.name = "zero",	  .has_arg = 2, .val = 'Z'},
     72 	{.name = "new-chain",	  .has_arg = 1, .val = 'N'},
     73 	{.name = "delete-chain",  .has_arg = 2, .val = 'X'},
     74 	{.name = "rename-chain",  .has_arg = 1, .val = 'E'},
     75 	{.name = "policy",	  .has_arg = 1, .val = 'P'},
     76 	{.name = "source",	  .has_arg = 1, .val = 's'},
     77 	{.name = "destination",   .has_arg = 1, .val = 'd'},
     78 	{.name = "src",		  .has_arg = 1, .val = 's'}, /* synonym */
     79 	{.name = "dst",		  .has_arg = 1, .val = 'd'}, /* synonym */
     80 	{.name = "protocol",	  .has_arg = 1, .val = 'p'},
     81 	{.name = "in-interface",  .has_arg = 1, .val = 'i'},
     82 	{.name = "jump",	  .has_arg = 1, .val = 'j'},
     83 	{.name = "table",	  .has_arg = 1, .val = 't'},
     84 	{.name = "match",	  .has_arg = 1, .val = 'm'},
     85 	{.name = "numeric",	  .has_arg = 0, .val = 'n'},
     86 	{.name = "out-interface", .has_arg = 1, .val = 'o'},
     87 	{.name = "verbose",	  .has_arg = 0, .val = 'v'},
     88 	{.name = "wait",	  .has_arg = 2, .val = 'w'},
     89 	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
     90 	{.name = "exact",	  .has_arg = 0, .val = 'x'},
     91 	{.name = "fragments",	  .has_arg = 0, .val = 'f'},
     92 	{.name = "version",	  .has_arg = 0, .val = 'V'},
     93 	{.name = "help",	  .has_arg = 2, .val = 'h'},
     94 	{.name = "line-numbers",  .has_arg = 0, .val = '0'},
     95 	{.name = "modprobe",	  .has_arg = 1, .val = 'M'},
     96 	{.name = "set-counters",  .has_arg = 1, .val = 'c'},
     97 	{.name = "goto",	  .has_arg = 1, .val = 'g'},
     98 	{.name = "ipv4",	  .has_arg = 0, .val = '4'},
     99 	{.name = "ipv6",	  .has_arg = 0, .val = '6'},
    100 	{NULL},
    101 };
    102 
    103 void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
    104 
    105 struct xtables_globals xtables_globals = {
    106 	.option_offset = 0,
    107 	.program_version = IPTABLES_VERSION,
    108 	.orig_opts = original_opts,
    109 	.exit_err = xtables_exit_error,
    110 	.compat_rev = nft_compatible_revision,
    111 };
    112 
    113 /* Table of legal combinations of commands and options.  If any of the
    114  * given commands make an option legal, that option is legal (applies to
    115  * CMD_LIST and CMD_ZERO only).
    116  * Key:
    117  *  +  compulsory
    118  *  x  illegal
    119  *     optional
    120  */
    121 
    122 static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
    123 /* Well, it's better than "Re: Linux vs FreeBSD" */
    124 {
    125 	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o --line -c -f */
    126 /*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
    127 /*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
    128 /*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
    129 /*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
    130 /*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
    131 /*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' ','x','x'},
    132 /*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x','x'},
    133 /*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x','x'},
    134 /*ZERO_NUM*/  {'x','x','x','x','x',' ','x','x','x','x','x','x'},
    135 /*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
    136 /*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
    137 /*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x'},
    138 /*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x','x'},
    139 /*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
    140 /*CHECK*/     {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
    141 };
    142 
    143 static const int inverse_for_options[NUMBER_OF_OPT] =
    144 {
    145 /* -n */ 0,
    146 /* -s */ IPT_INV_SRCIP,
    147 /* -d */ IPT_INV_DSTIP,
    148 /* -p */ XT_INV_PROTO,
    149 /* -j */ 0,
    150 /* -v */ 0,
    151 /* -x */ 0,
    152 /* -i */ IPT_INV_VIA_IN,
    153 /* -o */ IPT_INV_VIA_OUT,
    154 /*--line*/ 0,
    155 /* -c */ 0,
    156 /* -f */ IPT_INV_FRAG,
    157 };
    158 
    159 #define opts xtables_globals.opts
    160 #define prog_name xtables_globals.program_name
    161 #define prog_vers xtables_globals.program_version
    162 
    163 static void __attribute__((noreturn))
    164 exit_tryhelp(int status)
    165 {
    166 	if (line != -1)
    167 		fprintf(stderr, "Error occurred at line: %d\n", line);
    168 	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
    169 			prog_name, prog_name);
    170 	xtables_free_opts(1);
    171 	exit(status);
    172 }
    173 
    174 static void
    175 exit_printhelp(const struct xtables_rule_match *matches)
    176 {
    177 	printf("%s v%s\n\n"
    178 "Usage: %s -[ACD] chain rule-specification [options]\n"
    179 "	%s -I chain [rulenum] rule-specification [options]\n"
    180 "	%s -R chain rulenum rule-specification [options]\n"
    181 "	%s -D chain rulenum [options]\n"
    182 "	%s -[LS] [chain [rulenum]] [options]\n"
    183 "	%s -[FZ] [chain] [options]\n"
    184 "	%s -[NX] chain\n"
    185 "	%s -E old-chain-name new-chain-name\n"
    186 "	%s -P chain target [options]\n"
    187 "	%s -h (print this help information)\n\n",
    188 	       prog_name, prog_vers, prog_name, prog_name,
    189 	       prog_name, prog_name, prog_name, prog_name,
    190 	       prog_name, prog_name, prog_name, prog_name);
    191 
    192 	printf(
    193 "Commands:\n"
    194 "Either long or short options are allowed.\n"
    195 "  --append  -A chain		Append to chain\n"
    196 "  --check   -C chain		Check for the existence of a rule\n"
    197 "  --delete  -D chain		Delete matching rule from chain\n"
    198 "  --delete  -D chain rulenum\n"
    199 "				Delete rule rulenum (1 = first) from chain\n"
    200 "  --insert  -I chain [rulenum]\n"
    201 "				Insert in chain as rulenum (default 1=first)\n"
    202 "  --replace -R chain rulenum\n"
    203 "				Replace rule rulenum (1 = first) in chain\n"
    204 "  --list    -L [chain [rulenum]]\n"
    205 "				List the rules in a chain or all chains\n"
    206 "  --list-rules -S [chain [rulenum]]\n"
    207 "				Print the rules in a chain or all chains\n"
    208 "  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
    209 "  --zero    -Z [chain [rulenum]]\n"
    210 "				Zero counters in chain or all chains\n"
    211 "  --new     -N chain		Create a new user-defined chain\n"
    212 "  --delete-chain\n"
    213 "	     -X [chain]		Delete a user-defined chain\n"
    214 "  --policy  -P chain target\n"
    215 "				Change policy on chain to target\n"
    216 "  --rename-chain\n"
    217 "	     -E old-chain new-chain\n"
    218 "				Change chain name, (moving any references)\n"
    219 
    220 "Options:\n"
    221 "    --ipv4	-4		Nothing (line is ignored by ip6tables-restore)\n"
    222 "    --ipv6	-6		Error (line is ignored by iptables-restore)\n"
    223 "[!] --proto	-p proto	protocol: by number or name, eg. `tcp'\n"
    224 "[!] --source	-s address[/mask][...]\n"
    225 "				source specification\n"
    226 "[!] --destination -d address[/mask][...]\n"
    227 "				destination specification\n"
    228 "[!] --in-interface -i input name[+]\n"
    229 "				network interface name ([+] for wildcard)\n"
    230 " --jump	-j target\n"
    231 "				target for rule (may load target extension)\n"
    232 #ifdef IPT_F_GOTO
    233 "  --goto      -g chain\n"
    234 "			       jump to chain with no return\n"
    235 #endif
    236 "  --match	-m match\n"
    237 "				extended match (may load extension)\n"
    238 "  --numeric	-n		numeric output of addresses and ports\n"
    239 "[!] --out-interface -o output name[+]\n"
    240 "				network interface name ([+] for wildcard)\n"
    241 "  --table	-t table	table to manipulate (default: `filter')\n"
    242 "  --verbose	-v		verbose mode\n"
    243 "  --wait	-w [seconds]	maximum wait to acquire xtables lock before give up\n"
    244 "  --wait-interval -W [usecs]	wait time to try to acquire xtables lock\n"
    245 "				default is 1 second\n"
    246 "  --line-numbers		print line numbers when listing\n"
    247 "  --exact	-x		expand numbers (display exact values)\n"
    248 "[!] --fragment	-f		match second or further fragments only\n"
    249 "  --modprobe=<command>		try to insert modules using this command\n"
    250 "  --set-counters PKTS BYTES	set the counter during insert/append\n"
    251 "[!] --version	-V		print package version.\n");
    252 
    253 	print_extension_helps(xtables_targets, matches);
    254 	exit(0);
    255 }
    256 
    257 void
    258 xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
    259 {
    260 	va_list args;
    261 
    262 	va_start(args, msg);
    263 	fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
    264 	vfprintf(stderr, msg, args);
    265 	va_end(args);
    266 	fprintf(stderr, "\n");
    267 	if (status == PARAMETER_PROBLEM)
    268 		exit_tryhelp(status);
    269 	if (status == VERSION_PROBLEM)
    270 		fprintf(stderr,
    271 			"Perhaps iptables or your kernel needs to be upgraded.\n");
    272 	/* On error paths, make sure that we don't leak memory */
    273 	xtables_free_opts(1);
    274 	exit(status);
    275 }
    276 
    277 static void
    278 generic_opt_check(int command, int options)
    279 {
    280 	int i, j, legal = 0;
    281 
    282 	/* Check that commands are valid with options.	Complicated by the
    283 	 * fact that if an option is legal with *any* command given, it is
    284 	 * legal overall (ie. -z and -l).
    285 	 */
    286 	for (i = 0; i < NUMBER_OF_OPT; i++) {
    287 		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
    288 
    289 		for (j = 0; j < NUMBER_OF_CMD; j++) {
    290 			if (!(command & (1<<j)))
    291 				continue;
    292 
    293 			if (!(options & (1<<i))) {
    294 				if (commands_v_options[j][i] == '+')
    295 					xtables_error(PARAMETER_PROBLEM,
    296 						   "You need to supply the `-%c' "
    297 						   "option for this command\n",
    298 						   optflags[i]);
    299 			} else {
    300 				if (commands_v_options[j][i] != 'x')
    301 					legal = 1;
    302 				else if (legal == 0)
    303 					legal = -1;
    304 			}
    305 		}
    306 		if (legal == -1)
    307 			xtables_error(PARAMETER_PROBLEM,
    308 				   "Illegal option `-%c' with this command\n",
    309 				   optflags[i]);
    310 	}
    311 }
    312 
    313 static char
    314 opt2char(int option)
    315 {
    316 	const char *ptr;
    317 	for (ptr = optflags; option > 1; option >>= 1, ptr++);
    318 
    319 	return *ptr;
    320 }
    321 
    322 static char
    323 cmd2char(int option)
    324 {
    325 	const char *ptr;
    326 	for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
    327 
    328 	return *ptr;
    329 }
    330 
    331 static void
    332 add_command(unsigned int *cmd, const int newcmd, const int othercmds,
    333 	    int invert)
    334 {
    335 	if (invert)
    336 		xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
    337 	if (*cmd & (~othercmds))
    338 		xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
    339 			   cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
    340 	*cmd |= newcmd;
    341 }
    342 
    343 /*
    344  *	All functions starting with "parse" should succeed, otherwise
    345  *	the program fails.
    346  *	Most routines return pointers to static data that may change
    347  *	between calls to the same or other routines with a few exceptions:
    348  *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
    349  *	return global static data.
    350 */
    351 
    352 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
    353 /* Can't be zero. */
    354 static int
    355 parse_rulenumber(const char *rule)
    356 {
    357 	unsigned int rulenum;
    358 
    359 	if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
    360 		xtables_error(PARAMETER_PROBLEM,
    361 			   "Invalid rule number `%s'", rule);
    362 
    363 	return rulenum;
    364 }
    365 
    366 static const char *
    367 parse_target(const char *targetname)
    368 {
    369 	const char *ptr;
    370 
    371 	if (strlen(targetname) < 1)
    372 		xtables_error(PARAMETER_PROBLEM,
    373 			   "Invalid target name (too short)");
    374 
    375 	if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
    376 		xtables_error(PARAMETER_PROBLEM,
    377 			   "Invalid target name `%s' (%u chars max)",
    378 			   targetname, XT_EXTENSION_MAXNAMELEN - 1);
    379 
    380 	for (ptr = targetname; *ptr; ptr++)
    381 		if (isspace(*ptr))
    382 			xtables_error(PARAMETER_PROBLEM,
    383 				   "Invalid target name `%s'", targetname);
    384 	return targetname;
    385 }
    386 
    387 static void
    388 set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
    389 	   int invert)
    390 {
    391 	if (*options & option)
    392 		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
    393 			   opt2char(option));
    394 	*options |= option;
    395 
    396 	if (invert) {
    397 		unsigned int i;
    398 		for (i = 0; 1 << i != option; i++);
    399 
    400 		if (!inverse_for_options[i])
    401 			xtables_error(PARAMETER_PROBLEM,
    402 				   "cannot have ! before -%c",
    403 				   opt2char(option));
    404 		*invflg |= inverse_for_options[i];
    405 	}
    406 }
    407 
    408 static int
    409 add_entry(const char *chain,
    410 	  const char *table,
    411 	  struct iptables_command_state *cs,
    412 	  int rulenum, int family,
    413 	  const struct addr_mask s,
    414 	  const struct addr_mask d,
    415 	  bool verbose, struct nft_handle *h, bool append)
    416 {
    417 	unsigned int i, j;
    418 	int ret = 1;
    419 
    420 	for (i = 0; i < s.naddrs; i++) {
    421 		if (family == AF_INET) {
    422 			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
    423 			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
    424 			for (j = 0; j < d.naddrs; j++) {
    425 				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
    426 				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
    427 
    428 				if (append) {
    429 					ret = nft_rule_append(h, chain, table,
    430 							      cs, 0,
    431 							      verbose);
    432 				} else {
    433 					ret = nft_rule_insert(h, chain, table,
    434 							      cs, rulenum,
    435 							      verbose);
    436 				}
    437 			}
    438 		} else if (family == AF_INET6) {
    439 			memcpy(&cs->fw6.ipv6.src,
    440 			       &s.addr.v6[i], sizeof(struct in6_addr));
    441 			memcpy(&cs->fw6.ipv6.smsk,
    442 			       &s.mask.v6[i], sizeof(struct in6_addr));
    443 			for (j = 0; j < d.naddrs; j++) {
    444 				memcpy(&cs->fw6.ipv6.dst,
    445 				       &d.addr.v6[j], sizeof(struct in6_addr));
    446 				memcpy(&cs->fw6.ipv6.dmsk,
    447 				       &d.mask.v6[j], sizeof(struct in6_addr));
    448 				if (append) {
    449 					ret = nft_rule_append(h, chain, table,
    450 							      cs, 0,
    451 							      verbose);
    452 				} else {
    453 					ret = nft_rule_insert(h, chain, table,
    454 							      cs, rulenum,
    455 							      verbose);
    456 				}
    457 			}
    458 		}
    459 	}
    460 
    461 	return ret;
    462 }
    463 
    464 static int
    465 replace_entry(const char *chain, const char *table,
    466 	      struct iptables_command_state *cs,
    467 	      unsigned int rulenum,
    468 	      int family,
    469 	      const struct addr_mask s,
    470 	      const struct addr_mask d,
    471 	      bool verbose, struct nft_handle *h)
    472 {
    473 	if (family == AF_INET) {
    474 		cs->fw.ip.src.s_addr = s.addr.v4->s_addr;
    475 		cs->fw.ip.dst.s_addr = d.addr.v4->s_addr;
    476 		cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr;
    477 		cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr;
    478 	} else if (family == AF_INET6) {
    479 		memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr));
    480 		memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr));
    481 		memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr));
    482 		memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr));
    483 	} else
    484 		return 1;
    485 
    486 	return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
    487 }
    488 
    489 static int
    490 delete_entry(const char *chain, const char *table,
    491 	     struct iptables_command_state *cs,
    492 	     int family,
    493 	     const struct addr_mask s,
    494 	     const struct addr_mask d,
    495 	     bool verbose,
    496 	     struct nft_handle *h)
    497 {
    498 	unsigned int i, j;
    499 	int ret = 1;
    500 
    501 	for (i = 0; i < s.naddrs; i++) {
    502 		if (family == AF_INET) {
    503 			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
    504 			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
    505 			for (j = 0; j < d.naddrs; j++) {
    506 				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
    507 				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
    508 				ret = nft_rule_delete(h, chain,
    509 						      table, cs, verbose);
    510 			}
    511 		} else if (family == AF_INET6) {
    512 			memcpy(&cs->fw6.ipv6.src,
    513 			       &s.addr.v6[i], sizeof(struct in6_addr));
    514 			memcpy(&cs->fw6.ipv6.smsk,
    515 			       &s.mask.v6[i], sizeof(struct in6_addr));
    516 			for (j = 0; j < d.naddrs; j++) {
    517 				memcpy(&cs->fw6.ipv6.dst,
    518 				       &d.addr.v6[j], sizeof(struct in6_addr));
    519 				memcpy(&cs->fw6.ipv6.dmsk,
    520 				       &d.mask.v6[j], sizeof(struct in6_addr));
    521 				ret = nft_rule_delete(h, chain,
    522 						      table, cs, verbose);
    523 			}
    524 		}
    525 	}
    526 
    527 	return ret;
    528 }
    529 
    530 static int
    531 check_entry(const char *chain, const char *table,
    532 	    struct iptables_command_state *cs,
    533 	    int family,
    534 	    const struct addr_mask s,
    535 	    const struct addr_mask d,
    536 	    bool verbose, struct nft_handle *h)
    537 {
    538 	unsigned int i, j;
    539 	int ret = 1;
    540 
    541 	for (i = 0; i < s.naddrs; i++) {
    542 		if (family == AF_INET) {
    543 			cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
    544 			cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
    545 			for (j = 0; j < d.naddrs; j++) {
    546 				cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
    547 				cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
    548 				ret = nft_rule_check(h, chain,
    549 						     table, cs, verbose);
    550 			}
    551 		} else if (family == AF_INET6) {
    552 			memcpy(&cs->fw6.ipv6.src,
    553 			       &s.addr.v6[i], sizeof(struct in6_addr));
    554 			memcpy(&cs->fw6.ipv6.smsk,
    555 			       &s.mask.v6[i], sizeof(struct in6_addr));
    556 			for (j = 0; j < d.naddrs; j++) {
    557 				memcpy(&cs->fw6.ipv6.dst,
    558 				       &d.addr.v6[j], sizeof(struct in6_addr));
    559 				memcpy(&cs->fw6.ipv6.dmsk,
    560 				       &d.mask.v6[j], sizeof(struct in6_addr));
    561 				ret = nft_rule_check(h, chain,
    562 						     table, cs, verbose);
    563 			}
    564 		}
    565 	}
    566 
    567 	return ret;
    568 }
    569 
    570 static int
    571 list_entries(struct nft_handle *h, const char *chain, const char *table,
    572 	     int rulenum, int verbose, int numeric, int expanded,
    573 	     int linenumbers)
    574 {
    575 	unsigned int format;
    576 
    577 	format = FMT_OPTIONS;
    578 	if (!verbose)
    579 		format |= FMT_NOCOUNTS;
    580 	else
    581 		format |= FMT_VIA;
    582 
    583 	if (numeric)
    584 		format |= FMT_NUMERIC;
    585 
    586 	if (!expanded)
    587 		format |= FMT_KILOMEGAGIGA;
    588 
    589 	if (linenumbers)
    590 		format |= FMT_LINENUMBERS;
    591 
    592 	return nft_rule_list(h, chain, table, rulenum, format);
    593 }
    594 
    595 static int
    596 list_rules(struct nft_handle *h, const char *chain, const char *table,
    597 	   int rulenum, int counters)
    598 {
    599 	if (counters)
    600 	    counters = -1;		/* iptables -c format */
    601 
    602 	nft_rule_list_save(h, chain, table, rulenum, counters);
    603 
    604 	/* iptables does not return error if rule number not found */
    605 	return 1;
    606 }
    607 
    608 static void command_jump(struct iptables_command_state *cs)
    609 {
    610 	size_t size;
    611 
    612 	set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags, cs->invert);
    613 	cs->jumpto = parse_target(optarg);
    614 	/* TRY_LOAD (may be chain name) */
    615 	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
    616 
    617 	if (cs->target == NULL)
    618 		return;
    619 
    620 	size = XT_ALIGN(sizeof(struct xt_entry_target))
    621 		+ cs->target->size;
    622 
    623 	cs->target->t = xtables_calloc(1, size);
    624 	cs->target->t->u.target_size = size;
    625 	if (cs->target->real_name == NULL) {
    626 		strcpy(cs->target->t->u.user.name, cs->jumpto);
    627 	} else {
    628 		/* Alias support for userspace side */
    629 		strcpy(cs->target->t->u.user.name, cs->target->real_name);
    630 		if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
    631 			fprintf(stderr, "Notice: The %s target is converted into %s target "
    632 				"in rule listing and saving.\n",
    633 				cs->jumpto, cs->target->real_name);
    634 	}
    635 	cs->target->t->u.user.revision = cs->target->revision;
    636 	xs_init_target(cs->target);
    637 
    638 	if (cs->target->x6_options != NULL)
    639 		opts = xtables_options_xfrm(xtables_globals.orig_opts, opts,
    640 					    cs->target->x6_options,
    641 					    &cs->target->option_offset);
    642 	else
    643 		opts = xtables_merge_options(xtables_globals.orig_opts, opts,
    644 					     cs->target->extra_opts,
    645 					     &cs->target->option_offset);
    646 	if (opts == NULL)
    647 		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
    648 }
    649 
    650 static void command_match(struct iptables_command_state *cs)
    651 {
    652 	struct xtables_match *m;
    653 	size_t size;
    654 
    655 	if (cs->invert)
    656 		xtables_error(PARAMETER_PROBLEM,
    657 			   "unexpected ! flag before --match");
    658 
    659 	m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
    660 	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
    661 	m->m = xtables_calloc(1, size);
    662 	m->m->u.match_size = size;
    663 	if (m->real_name == NULL) {
    664 		strcpy(m->m->u.user.name, m->name);
    665 	} else {
    666 		strcpy(m->m->u.user.name, m->real_name);
    667 		if (!(m->ext_flags & XTABLES_EXT_ALIAS))
    668 			fprintf(stderr, "Notice: the %s match is converted into %s match "
    669 				"in rule listing and saving.\n", m->name, m->real_name);
    670 	}
    671 	m->m->u.user.revision = m->revision;
    672 	xs_init_match(m);
    673 	if (m == m->next)
    674 		return;
    675 	/* Merge options for non-cloned matches */
    676 	if (m->x6_options != NULL)
    677 		opts = xtables_options_xfrm(xtables_globals.orig_opts, opts,
    678 					    m->x6_options, &m->option_offset);
    679 	else if (m->extra_opts != NULL)
    680 		opts = xtables_merge_options(xtables_globals.orig_opts, opts,
    681 					     m->extra_opts, &m->option_offset);
    682 	if (opts == NULL)
    683 		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
    684 }
    685 
    686 void do_parse(struct nft_handle *h, int argc, char *argv[],
    687 	      struct nft_xt_cmd_parse *p, struct iptables_command_state *cs,
    688 	      struct xtables_args *args)
    689 {
    690 	struct xtables_match *m;
    691 	struct xtables_rule_match *matchp;
    692 	bool wait_interval_set = false;
    693 	struct timeval wait_interval;
    694 	struct xtables_target *t;
    695 	int wait = 0;
    696 
    697 	memset(cs, 0, sizeof(*cs));
    698 	cs->jumpto = "";
    699 	cs->argv = argv;
    700 
    701 	/* re-set optind to 0 in case do_command4 gets called
    702 	 * a second time */
    703 	optind = 0;
    704 
    705 	/* clear mflags in case do_command4 gets called a second time
    706 	 * (we clear the global list of all matches for security)*/
    707 	for (m = xtables_matches; m; m = m->next)
    708 		m->mflags = 0;
    709 
    710 	for (t = xtables_targets; t; t = t->next) {
    711 		t->tflags = 0;
    712 		t->used = 0;
    713 	}
    714 
    715 	/* Suppress error messages: we may add new options if we
    716 	   demand-load a protocol. */
    717 	opterr = 0;
    718 
    719 	h->ops = nft_family_ops_lookup(h->family);
    720 	if (h->ops == NULL)
    721 		xtables_error(PARAMETER_PROBLEM, "Unknown family");
    722 
    723 	opts = xt_params->orig_opts;
    724 	while ((cs->c = getopt_long(argc, argv,
    725 	   "-: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",
    726 					   opts, NULL)) != -1) {
    727 		switch (cs->c) {
    728 			/*
    729 			 * Command selection
    730 			 */
    731 		case 'A':
    732 			add_command(&p->command, CMD_APPEND, CMD_NONE,
    733 				    cs->invert);
    734 			p->chain = optarg;
    735 			break;
    736 
    737 		case 'C':
    738 			add_command(&p->command, CMD_CHECK, CMD_NONE,
    739 				    cs->invert);
    740 			p->chain = optarg;
    741 			break;
    742 
    743 		case 'D':
    744 			add_command(&p->command, CMD_DELETE, CMD_NONE,
    745 				    cs->invert);
    746 			p->chain = optarg;
    747 			if (xs_has_arg(argc, argv)) {
    748 				p->rulenum = parse_rulenumber(argv[optind++]);
    749 				p->command = CMD_DELETE_NUM;
    750 			}
    751 			break;
    752 
    753 		case 'R':
    754 			add_command(&p->command, CMD_REPLACE, CMD_NONE,
    755 				    cs->invert);
    756 			p->chain = optarg;
    757 			if (xs_has_arg(argc, argv))
    758 				p->rulenum = parse_rulenumber(argv[optind++]);
    759 			else
    760 				xtables_error(PARAMETER_PROBLEM,
    761 					   "-%c requires a rule number",
    762 					   cmd2char(CMD_REPLACE));
    763 			break;
    764 
    765 		case 'I':
    766 			add_command(&p->command, CMD_INSERT, CMD_NONE,
    767 				    cs->invert);
    768 			p->chain = optarg;
    769 			if (xs_has_arg(argc, argv))
    770 				p->rulenum = parse_rulenumber(argv[optind++]);
    771 			else
    772 				p->rulenum = 1;
    773 			break;
    774 
    775 		case 'L':
    776 			add_command(&p->command, CMD_LIST,
    777 				    CMD_ZERO | CMD_ZERO_NUM, cs->invert);
    778 			if (optarg)
    779 				p->chain = optarg;
    780 			else if (xs_has_arg(argc, argv))
    781 				p->chain = argv[optind++];
    782 			if (xs_has_arg(argc, argv))
    783 				p->rulenum = parse_rulenumber(argv[optind++]);
    784 			break;
    785 
    786 		case 'S':
    787 			add_command(&p->command, CMD_LIST_RULES,
    788 				    CMD_ZERO|CMD_ZERO_NUM, cs->invert);
    789 			if (optarg)
    790 				p->chain = optarg;
    791 			else if (xs_has_arg(argc, argv))
    792 				p->chain = argv[optind++];
    793 			if (xs_has_arg(argc, argv))
    794 				p->rulenum = parse_rulenumber(argv[optind++]);
    795 			break;
    796 
    797 		case 'F':
    798 			add_command(&p->command, CMD_FLUSH, CMD_NONE,
    799 				    cs->invert);
    800 			if (optarg)
    801 				p->chain = optarg;
    802 			else if (xs_has_arg(argc, argv))
    803 				p->chain = argv[optind++];
    804 			break;
    805 
    806 		case 'Z':
    807 			add_command(&p->command, CMD_ZERO,
    808 				    CMD_LIST|CMD_LIST_RULES, cs->invert);
    809 			if (optarg)
    810 				p->chain = optarg;
    811 			else if (xs_has_arg(argc, argv))
    812 				p->chain = argv[optind++];
    813 			if (xs_has_arg(argc, argv)) {
    814 				p->rulenum = parse_rulenumber(argv[optind++]);
    815 				p->command = CMD_ZERO_NUM;
    816 			}
    817 			break;
    818 
    819 		case 'N':
    820 			if (optarg && (*optarg == '-' || *optarg == '!'))
    821 				xtables_error(PARAMETER_PROBLEM,
    822 					   "chain name not allowed to start "
    823 					   "with `%c'\n", *optarg);
    824 			if (xtables_find_target(optarg, XTF_TRY_LOAD))
    825 				xtables_error(PARAMETER_PROBLEM,
    826 					   "chain name may not clash "
    827 					   "with target name\n");
    828 			add_command(&p->command, CMD_NEW_CHAIN, CMD_NONE,
    829 				    cs->invert);
    830 			p->chain = optarg;
    831 			break;
    832 
    833 		case 'X':
    834 			add_command(&p->command, CMD_DELETE_CHAIN, CMD_NONE,
    835 				    cs->invert);
    836 			if (optarg)
    837 				p->chain = optarg;
    838 			else if (xs_has_arg(argc, argv))
    839 				p->chain = argv[optind++];
    840 			break;
    841 
    842 		case 'E':
    843 			add_command(&p->command, CMD_RENAME_CHAIN, CMD_NONE,
    844 				    cs->invert);
    845 			p->chain = optarg;
    846 			if (xs_has_arg(argc, argv))
    847 				p->newname = argv[optind++];
    848 			else
    849 				xtables_error(PARAMETER_PROBLEM,
    850 					   "-%c requires old-chain-name and "
    851 					   "new-chain-name",
    852 					    cmd2char(CMD_RENAME_CHAIN));
    853 			break;
    854 
    855 		case 'P':
    856 			add_command(&p->command, CMD_SET_POLICY, CMD_NONE,
    857 				    cs->invert);
    858 			p->chain = optarg;
    859 			if (xs_has_arg(argc, argv))
    860 				p->policy = argv[optind++];
    861 			else
    862 				xtables_error(PARAMETER_PROBLEM,
    863 					   "-%c requires a chain and a policy",
    864 					   cmd2char(CMD_SET_POLICY));
    865 			break;
    866 
    867 		case 'h':
    868 			if (!optarg)
    869 				optarg = argv[optind];
    870 
    871 			/* iptables -p icmp -h */
    872 			if (!cs->matches && cs->protocol)
    873 				xtables_find_match(cs->protocol,
    874 					XTF_TRY_LOAD, &cs->matches);
    875 
    876 			exit_printhelp(cs->matches);
    877 
    878 			/*
    879 			 * Option selection
    880 			 */
    881 		case 'p':
    882 			set_option(&cs->options, OPT_PROTOCOL,
    883 				   &args->invflags, cs->invert);
    884 
    885 			/* Canonicalize into lower case */
    886 			for (cs->protocol = optarg; *cs->protocol; cs->protocol++)
    887 				*cs->protocol = tolower(*cs->protocol);
    888 
    889 			cs->protocol = optarg;
    890 			args->proto = xtables_parse_protocol(cs->protocol);
    891 
    892 			if (args->proto == 0 &&
    893 			    (args->invflags & XT_INV_PROTO))
    894 				xtables_error(PARAMETER_PROBLEM,
    895 					   "rule would never match protocol");
    896 
    897 			/* This needs to happen here to parse extensions */
    898 			h->ops->proto_parse(cs, args);
    899 			break;
    900 
    901 		case 's':
    902 			set_option(&cs->options, OPT_SOURCE,
    903 				   &args->invflags, cs->invert);
    904 			args->shostnetworkmask = optarg;
    905 			break;
    906 
    907 		case 'd':
    908 			set_option(&cs->options, OPT_DESTINATION,
    909 				   &args->invflags, cs->invert);
    910 			args->dhostnetworkmask = optarg;
    911 			break;
    912 
    913 #ifdef IPT_F_GOTO
    914 		case 'g':
    915 			set_option(&cs->options, OPT_JUMP, &args->invflags,
    916 				   cs->invert);
    917 			args->goto_set = true;
    918 			cs->jumpto = parse_target(optarg);
    919 			break;
    920 #endif
    921 
    922 		case 'j':
    923 			command_jump(cs);
    924 			break;
    925 
    926 
    927 		case 'i':
    928 			if (*optarg == '\0')
    929 				xtables_error(PARAMETER_PROBLEM,
    930 					"Empty interface is likely to be "
    931 					"undesired");
    932 			set_option(&cs->options, OPT_VIANAMEIN,
    933 				   &args->invflags, cs->invert);
    934 			xtables_parse_interface(optarg,
    935 						args->iniface,
    936 						args->iniface_mask);
    937 			break;
    938 
    939 		case 'o':
    940 			if (*optarg == '\0')
    941 				xtables_error(PARAMETER_PROBLEM,
    942 					"Empty interface is likely to be "
    943 					"undesired");
    944 			set_option(&cs->options, OPT_VIANAMEOUT,
    945 				   &args->invflags, cs->invert);
    946 			xtables_parse_interface(optarg,
    947 						args->outiface,
    948 						args->outiface_mask);
    949 			break;
    950 
    951 		case 'f':
    952 			if (args->family == AF_INET6) {
    953 				xtables_error(PARAMETER_PROBLEM,
    954 					"`-f' is not supported in IPv6, "
    955 					"use -m frag instead");
    956 			}
    957 			set_option(&cs->options, OPT_FRAGMENT, &args->invflags,
    958 				   cs->invert);
    959 			args->flags |= IPT_F_FRAG;
    960 			break;
    961 
    962 		case 'v':
    963 			if (!p->verbose)
    964 				set_option(&cs->options, OPT_VERBOSE,
    965 					   &args->invflags, cs->invert);
    966 			p->verbose++;
    967 			break;
    968 
    969 		case 'm':
    970 			command_match(cs);
    971 			break;
    972 
    973 		case 'n':
    974 			set_option(&cs->options, OPT_NUMERIC, &args->invflags,
    975 				   cs->invert);
    976 			break;
    977 
    978 		case 't':
    979 			if (cs->invert)
    980 				xtables_error(PARAMETER_PROBLEM,
    981 					   "unexpected ! flag before --table");
    982 			p->table = optarg;
    983 			break;
    984 
    985 		case 'x':
    986 			set_option(&cs->options, OPT_EXPANDED, &args->invflags,
    987 				   cs->invert);
    988 			break;
    989 
    990 		case 'V':
    991 			if (cs->invert)
    992 				printf("Not %s ;-)\n", prog_vers);
    993 			else
    994 				printf("%s v%s\n",
    995 				       prog_name, prog_vers);
    996 			exit(0);
    997 
    998 		case 'w':
    999 			if (p->restore) {
   1000 				xtables_error(PARAMETER_PROBLEM,
   1001 					      "You cannot use `-w' from "
   1002 					      "iptables-restore");
   1003 			}
   1004 
   1005 			wait = parse_wait_time(argc, argv);
   1006 			break;
   1007 
   1008 		case 'W':
   1009 			if (p->restore) {
   1010 				xtables_error(PARAMETER_PROBLEM,
   1011 					      "You cannot use `-W' from "
   1012 					      "iptables-restore");
   1013 			}
   1014 
   1015 			parse_wait_interval(argc, argv, &wait_interval);
   1016 			wait_interval_set = true;
   1017 			break;
   1018 
   1019 		case '0':
   1020 			set_option(&cs->options, OPT_LINENUMBERS,
   1021 				   &args->invflags, cs->invert);
   1022 			break;
   1023 
   1024 		case 'M':
   1025 			xtables_modprobe_program = optarg;
   1026 			break;
   1027 
   1028 		case 'c':
   1029 			set_option(&cs->options, OPT_COUNTERS, &args->invflags,
   1030 				   cs->invert);
   1031 			args->pcnt = optarg;
   1032 			args->bcnt = strchr(args->pcnt + 1, ',');
   1033 			if (args->bcnt)
   1034 			    args->bcnt++;
   1035 			if (!args->bcnt && xs_has_arg(argc, argv))
   1036 				args->bcnt = argv[optind++];
   1037 			if (!args->bcnt)
   1038 				xtables_error(PARAMETER_PROBLEM,
   1039 					"-%c requires packet and byte counter",
   1040 					opt2char(OPT_COUNTERS));
   1041 
   1042 			if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1)
   1043 				xtables_error(PARAMETER_PROBLEM,
   1044 					"-%c packet counter not numeric",
   1045 					opt2char(OPT_COUNTERS));
   1046 
   1047 			if (sscanf(args->bcnt, "%llu", &args->bcnt_cnt) != 1)
   1048 				xtables_error(PARAMETER_PROBLEM,
   1049 					"-%c byte counter not numeric",
   1050 					opt2char(OPT_COUNTERS));
   1051 			break;
   1052 
   1053 		case '4':
   1054 			if (args->family != AF_INET)
   1055 				exit_tryhelp(2);
   1056 
   1057 			h->ops = nft_family_ops_lookup(args->family);
   1058 			break;
   1059 
   1060 		case '6':
   1061 			args->family = AF_INET6;
   1062 			xtables_set_nfproto(AF_INET6);
   1063 
   1064 			h->ops = nft_family_ops_lookup(args->family);
   1065 			if (h->ops == NULL)
   1066 				xtables_error(PARAMETER_PROBLEM,
   1067 					      "Unknown family");
   1068 			break;
   1069 
   1070 		case 1: /* non option */
   1071 			if (optarg[0] == '!' && optarg[1] == '\0') {
   1072 				if (cs->invert)
   1073 					xtables_error(PARAMETER_PROBLEM,
   1074 						   "multiple consecutive ! not"
   1075 						   " allowed");
   1076 				cs->invert = TRUE;
   1077 				optarg[0] = '\0';
   1078 				continue;
   1079 			}
   1080 			fprintf(stderr, "Bad argument `%s'\n", optarg);
   1081 			exit_tryhelp(2);
   1082 
   1083 		default:
   1084 			if (command_default(cs, &xtables_globals) == 1)
   1085 				/* cf. ip6tables.c */
   1086 				continue;
   1087 			break;
   1088 		}
   1089 		cs->invert = FALSE;
   1090 	}
   1091 
   1092 	if (strcmp(p->table, "nat") == 0 &&
   1093 	    ((p->policy != NULL && strcmp(p->policy, "DROP") == 0) ||
   1094 	    (cs->jumpto != NULL && strcmp(cs->jumpto, "DROP") == 0)))
   1095 		xtables_error(PARAMETER_PROBLEM,
   1096 			"\nThe \"nat\" table is not intended for filtering, "
   1097 			"the use of DROP is therefore inhibited.\n\n");
   1098 
   1099 	if (!wait && wait_interval_set)
   1100 		xtables_error(PARAMETER_PROBLEM,
   1101 			      "--wait-interval only makes sense with --wait\n");
   1102 
   1103 	for (matchp = cs->matches; matchp; matchp = matchp->next)
   1104 		xtables_option_mfcall(matchp->match);
   1105 	if (cs->target != NULL)
   1106 		xtables_option_tfcall(cs->target);
   1107 
   1108 	/* Fix me: must put inverse options checking here --MN */
   1109 
   1110 	if (optind < argc)
   1111 		xtables_error(PARAMETER_PROBLEM,
   1112 			   "unknown arguments found on commandline");
   1113 	if (!p->command)
   1114 		xtables_error(PARAMETER_PROBLEM, "no command specified");
   1115 	if (cs->invert)
   1116 		xtables_error(PARAMETER_PROBLEM,
   1117 			   "nothing appropriate following !");
   1118 
   1119 	/* Set only if required, needed by xtables-restore */
   1120 	if (h->family == AF_UNSPEC)
   1121 		h->family = args->family;
   1122 
   1123 	h->ops->post_parse(p->command, cs, args);
   1124 
   1125 	if (p->command == CMD_REPLACE &&
   1126 	    (args->s.naddrs != 1 || args->d.naddrs != 1))
   1127 		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
   1128 			   "specify a unique address");
   1129 
   1130 	generic_opt_check(p->command, cs->options);
   1131 
   1132 	if (p->chain != NULL && strlen(p->chain) >= XT_EXTENSION_MAXNAMELEN)
   1133 		xtables_error(PARAMETER_PROBLEM,
   1134 			   "chain name `%s' too long (must be under %u chars)",
   1135 			   p->chain, XT_EXTENSION_MAXNAMELEN);
   1136 
   1137 	if (p->command == CMD_APPEND ||
   1138 	    p->command == CMD_DELETE ||
   1139 	    p->command == CMD_CHECK ||
   1140 	    p->command == CMD_INSERT ||
   1141 	    p->command == CMD_REPLACE) {
   1142 		if (strcmp(p->chain, "PREROUTING") == 0
   1143 		    || strcmp(p->chain, "INPUT") == 0) {
   1144 			/* -o not valid with incoming packets. */
   1145 			if (cs->options & OPT_VIANAMEOUT)
   1146 				xtables_error(PARAMETER_PROBLEM,
   1147 					   "Can't use -%c with %s\n",
   1148 					   opt2char(OPT_VIANAMEOUT),
   1149 					   p->chain);
   1150 		}
   1151 
   1152 		if (strcmp(p->chain, "POSTROUTING") == 0
   1153 		    || strcmp(p->chain, "OUTPUT") == 0) {
   1154 			/* -i not valid with outgoing packets */
   1155 			if (cs->options & OPT_VIANAMEIN)
   1156 				xtables_error(PARAMETER_PROBLEM,
   1157 					   "Can't use -%c with %s\n",
   1158 					   opt2char(OPT_VIANAMEIN),
   1159 					   p->chain);
   1160 		}
   1161 
   1162 		/*
   1163 		 * Contrary to what iptables does, we assume that any jumpto
   1164 		 * is a custom chain jumps (if no target is found). Later on,
   1165 		 * nf_table will spot the error if the chain does not exists.
   1166 		 */
   1167 	}
   1168 }
   1169 
   1170 int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
   1171 		bool restore)
   1172 {
   1173 	int ret = 1;
   1174 	struct nft_xt_cmd_parse p = {
   1175 		.table		= *table,
   1176 		.restore	= restore,
   1177 	};
   1178 	struct iptables_command_state cs;
   1179 	struct xtables_args args = {
   1180 		.family = h->family,
   1181 	};
   1182 
   1183 	do_parse(h, argc, argv, &p, &cs, &args);
   1184 
   1185 	switch (p.command) {
   1186 	case CMD_APPEND:
   1187 		ret = add_entry(p.chain, p.table, &cs, 0, h->family,
   1188 				args.s, args.d,
   1189 				cs.options & OPT_VERBOSE, h, true);
   1190 		break;
   1191 	case CMD_DELETE:
   1192 		ret = delete_entry(p.chain, p.table, &cs, h->family,
   1193 				   args.s, args.d,
   1194 				   cs.options & OPT_VERBOSE, h);
   1195 		break;
   1196 	case CMD_DELETE_NUM:
   1197 		ret = nft_rule_delete_num(h, p.chain, p.table,
   1198 					  p.rulenum - 1, p.verbose);
   1199 		break;
   1200 	case CMD_CHECK:
   1201 		ret = check_entry(p.chain, p.table, &cs, h->family,
   1202 				  args.s, args.d,
   1203 				  cs.options & OPT_VERBOSE, h);
   1204 		break;
   1205 	case CMD_REPLACE:
   1206 		ret = replace_entry(p.chain, p.table, &cs, p.rulenum - 1,
   1207 				    h->family, args.s, args.d,
   1208 				    cs.options & OPT_VERBOSE, h);
   1209 		break;
   1210 	case CMD_INSERT:
   1211 		ret = add_entry(p.chain, p.table, &cs, p.rulenum - 1,
   1212 				h->family, args.s, args.d,
   1213 				cs.options&OPT_VERBOSE, h, false);
   1214 		break;
   1215 	case CMD_FLUSH:
   1216 		ret = nft_rule_flush(h, p.chain, p.table);
   1217 		break;
   1218 	case CMD_ZERO:
   1219 		ret = nft_chain_zero_counters(h, p.chain, p.table);
   1220 		break;
   1221 	case CMD_ZERO_NUM:
   1222 		ret = nft_rule_zero_counters(h, p.chain, p.table,
   1223 					     p.rulenum - 1);
   1224 		break;
   1225 	case CMD_LIST:
   1226 	case CMD_LIST|CMD_ZERO:
   1227 	case CMD_LIST|CMD_ZERO_NUM:
   1228 		if (nft_is_ruleset_compatible(h) == 1) {
   1229 			printf("ERROR: You're using nft features that cannot be mapped to iptables, please keep using nft.\n");
   1230 			exit(EXIT_FAILURE);
   1231 		}
   1232 
   1233 		ret = list_entries(h, p.chain, p.table, p.rulenum,
   1234 				   cs.options & OPT_VERBOSE,
   1235 				   cs.options & OPT_NUMERIC,
   1236 				   cs.options & OPT_EXPANDED,
   1237 				   cs.options & OPT_LINENUMBERS);
   1238 		if (ret && (p.command & CMD_ZERO)) {
   1239 			ret = nft_chain_zero_counters(h, p.chain,
   1240 						      p.table);
   1241 		}
   1242 		if (ret && (p.command & CMD_ZERO_NUM)) {
   1243 			ret = nft_rule_zero_counters(h, p.chain, p.table,
   1244 						     p.rulenum - 1);
   1245 		}
   1246 		break;
   1247 	case CMD_LIST_RULES:
   1248 	case CMD_LIST_RULES|CMD_ZERO:
   1249 	case CMD_LIST_RULES|CMD_ZERO_NUM:
   1250 		ret = list_rules(h, p.chain, p.table, p.rulenum,
   1251 				 cs.options & OPT_VERBOSE);
   1252 		if (ret && (p.command & CMD_ZERO)) {
   1253 			ret = nft_chain_zero_counters(h, p.chain,
   1254 						      p.table);
   1255 		}
   1256 		if (ret && (p.command & CMD_ZERO_NUM)) {
   1257 			ret = nft_rule_zero_counters(h, p.chain, p.table,
   1258 						     p.rulenum - 1);
   1259 		}
   1260 		break;
   1261 	case CMD_NEW_CHAIN:
   1262 		ret = nft_chain_user_add(h, p.chain, p.table);
   1263 		break;
   1264 	case CMD_DELETE_CHAIN:
   1265 		ret = nft_chain_user_del(h, p.chain, p.table);
   1266 		break;
   1267 	case CMD_RENAME_CHAIN:
   1268 		ret = nft_chain_user_rename(h, p.chain, p.table, p.newname);
   1269 		break;
   1270 	case CMD_SET_POLICY:
   1271 		ret = nft_chain_set(h, p.table, p.chain, p.policy, NULL);
   1272 		if (ret < 0)
   1273 			xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
   1274 				      p.policy);
   1275 		break;
   1276 	default:
   1277 		/* We should never reach this... */
   1278 		exit_tryhelp(2);
   1279 	}
   1280 
   1281 	*table = p.table;
   1282 
   1283 	xtables_rule_matches_free(&cs.matches);
   1284 
   1285 	if (h->family == AF_INET) {
   1286 		free(args.s.addr.v4);
   1287 		free(args.s.mask.v4);
   1288 		free(args.d.addr.v4);
   1289 		free(args.d.mask.v4);
   1290 	} else if (h->family == AF_INET6) {
   1291 		free(args.s.addr.v6);
   1292 		free(args.s.mask.v6);
   1293 		free(args.d.addr.v6);
   1294 		free(args.d.mask.v6);
   1295 	}
   1296 	xtables_free_opts(1);
   1297 
   1298 	return ret;
   1299 }
   1300