• 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 
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))
exit_tryhelp(int status)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
exit_printhelp(const struct xtables_rule_match * matches)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
xtables_exit_error(enum xtables_exittype status,const char * msg,...)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
generic_opt_check(int command,int options)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
opt2char(int option)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
cmd2char(int option)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
add_command(unsigned int * cmd,const int newcmd,const int othercmds,int invert)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
parse_rulenumber(const char * rule)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 *
parse_target(const char * targetname)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
set_option(unsigned int * options,unsigned int option,uint8_t * invflg,int invert)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
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)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
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)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
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)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
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)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
list_entries(struct nft_handle * h,const char * chain,const char * table,int rulenum,int verbose,int numeric,int expanded,int linenumbers)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
list_rules(struct nft_handle * h,const char * chain,const char * table,int rulenum,int counters)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 
command_jump(struct iptables_command_state * cs)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 
command_match(struct iptables_command_state * cs)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 
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)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 
do_commandx(struct nft_handle * h,int argc,char * argv[],char ** table,bool restore)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