• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ebtables.c, v2.0 July 2002
3  *
4  * Author: Bart De Schuymer
5  *
6  *  This code was stongly inspired on the iptables code which is
7  *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 #include "config.h"
24 #include <ctype.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <inttypes.h>
32 #include <signal.h>
33 #include <net/if.h>
34 #include <netinet/ether.h>
35 #include <iptables.h>
36 #include <xtables.h>
37 
38 #include <linux/netfilter_bridge.h>
39 #include <linux/netfilter/nf_tables.h>
40 #include <libiptc/libxtc.h>
41 #include "xshared.h"
42 #include "nft.h"
43 #include "nft-bridge.h"
44 
45 static int
delete_entry(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,int rule_nr,int rule_nr_end,bool verbose)46 delete_entry(struct nft_handle *h,
47 	     const char *chain,
48 	     const char *table,
49 	     struct iptables_command_state *cs,
50 	     int rule_nr,
51 	     int rule_nr_end,
52 	     bool verbose)
53 {
54 	int ret = 1;
55 
56 	if (rule_nr == -1)
57 		ret = nft_cmd_rule_delete(h, chain, table, cs, verbose);
58 	else {
59 		do {
60 			ret = nft_cmd_rule_delete_num(h, chain, table,
61 						  rule_nr, verbose);
62 			rule_nr++;
63 		} while (rule_nr < rule_nr_end);
64 	}
65 
66 	return ret;
67 }
68 
69 static int
change_entry_counters(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,int rule_nr,int rule_nr_end,uint8_t counter_op,bool verbose)70 change_entry_counters(struct nft_handle *h,
71 		      const char *chain, const char *table,
72 		      struct iptables_command_state *cs,
73 		      int rule_nr, int rule_nr_end, uint8_t counter_op,
74 		      bool verbose)
75 {
76 	int ret = 1;
77 
78 	if (rule_nr == -1)
79 		return nft_cmd_rule_change_counters(h, chain, table, cs,
80 						    rule_nr, counter_op,
81 						    verbose);
82 	do {
83 		ret = nft_cmd_rule_change_counters(h, chain, table, cs,
84 						   rule_nr, counter_op,
85 						   verbose);
86 		rule_nr++;
87 	} while (rule_nr < rule_nr_end);
88 
89 	return ret;
90 }
91 
92 /* Default command line options. Do not mess around with the already
93  * assigned numbers unless you know what you are doing */
94 struct option ebt_original_options[] =
95 {
96 	{ "append"         , required_argument, 0, 'A' },
97 	{ "insert"         , required_argument, 0, 'I' },
98 	{ "delete"         , required_argument, 0, 'D' },
99 	{ "list"           , optional_argument, 0, 'L' },
100 	{ "Lc"             , no_argument      , 0, 17  },
101 	{ "Ln"             , no_argument      , 0, 18  },
102 	{ "Lx"             , no_argument      , 0, 19  },
103 	{ "Lmac2"          , no_argument      , 0, 12  },
104 	{ "zero"           , optional_argument, 0, 'Z' },
105 	{ "flush"          , optional_argument, 0, 'F' },
106 	{ "policy"         , required_argument, 0, 'P' },
107 	{ "in-interface"   , required_argument, 0, 'i' },
108 	{ "in-if"          , required_argument, 0, 'i' },
109 	{ "logical-in"     , required_argument, 0, 15  },
110 	{ "logical-out"    , required_argument, 0, 16  },
111 	{ "out-interface"  , required_argument, 0, 'o' },
112 	{ "out-if"         , required_argument, 0, 'o' },
113 	{ "version"        , no_argument      , 0, 'V' },
114 	{ "verbose"        , no_argument      , 0, 'v' },
115 	{ "help"           , no_argument      , 0, 'h' },
116 	{ "jump"           , required_argument, 0, 'j' },
117 	{ "set-counters"   , required_argument, 0, 'c' },
118 	{ "change-counters", required_argument, 0, 'C' },
119 	{ "proto"          , required_argument, 0, 'p' },
120 	{ "protocol"       , required_argument, 0, 'p' },
121 	{ "db"             , required_argument, 0, 'b' },
122 	{ "source"         , required_argument, 0, 's' },
123 	{ "src"            , required_argument, 0, 's' },
124 	{ "destination"    , required_argument, 0, 'd' },
125 	{ "dst"            , required_argument, 0, 'd' },
126 	{ "table"          , required_argument, 0, 't' },
127 	{ "modprobe"       , required_argument, 0, 'M' },
128 	{ "new-chain"      , required_argument, 0, 'N' },
129 	{ "rename-chain"   , required_argument, 0, 'E' },
130 	{ "delete-chain"   , optional_argument, 0, 'X' },
131 	{ "init-table"     , no_argument      , 0, 11  },
132 	{ "concurrent"     , no_argument      , 0, 13  },
133 	{ "check"          , required_argument, 0, 14  },
134 	{ 0 }
135 };
136 
137 struct xtables_globals ebtables_globals = {
138 	.option_offset 		= 0,
139 	.program_version	= PACKAGE_VERSION " (nf_tables)",
140 	.orig_opts		= ebt_original_options,
141 	.compat_rev		= nft_compatible_revision,
142 };
143 
144 #define prog_name ebtables_globals.program_name
145 #define prog_vers ebtables_globals.program_version
146 
147 /* Prints all registered extensions */
ebt_list_extensions(const struct xtables_target * t,const struct xtables_rule_match * m)148 static void ebt_list_extensions(const struct xtables_target *t,
149 				const struct xtables_rule_match *m)
150 {
151 	printf("%s v%s\n", prog_name, prog_vers);
152 	printf("Loaded userspace extensions:\n");
153 	/*printf("\nLoaded tables:\n");
154         while (tbl) {
155 		printf("%s\n", tbl->name);
156                 tbl = tbl->next;
157 	}*/
158 	printf("\nLoaded targets:\n");
159         for (t = xtables_targets; t; t = t->next) {
160 		printf("%s\n", t->name);
161 	}
162 	printf("\nLoaded matches:\n");
163         for (; m != NULL; m = m->next)
164 		printf("%s\n", m->match->name);
165 	/*printf("\nLoaded watchers:\n");
166         while (w) {
167 		printf("%s\n", w->name);
168                 w = w->next;
169 	}*/
170 }
171 
nft_bridge_print_help(struct iptables_command_state * cs)172 void nft_bridge_print_help(struct iptables_command_state *cs)
173 {
174 	const struct xtables_rule_match *m = cs->matches;
175 	struct xtables_target *t = cs->target;
176 
177 	while (optind < cs->argc) {
178 		/*struct ebt_u_match *m;
179 		struct ebt_u_watcher *w;*/
180 
181 		if (!strcasecmp("list_extensions", cs->argv[optind])) {
182 			ebt_list_extensions(xtables_targets, cs->matches);
183 			exit(0);
184 		}
185 		/*if ((m = ebt_find_match(cs->argv[optind])))
186 			ebt_add_match(new_entry, m);
187 		else if ((w = ebt_find_watcher(cs->argv[optind])))
188 			ebt_add_watcher(new_entry, w);
189 		else {*/
190 			if (!(t = xtables_find_target(cs->argv[optind],
191 						      XTF_TRY_LOAD)))
192 				xtables_error(PARAMETER_PROBLEM,
193 					      "Extension '%s' not found",
194 					      cs->argv[optind]);
195 			if (cs->options & OPT_JUMP)
196 				xtables_error(PARAMETER_PROBLEM,
197 					      "Sorry, you can only see help for one target extension at a time");
198 			cs->options |= OPT_JUMP;
199 			cs->target = t;
200 		//}
201 		optind++;
202 	}
203 
204 	printf("%s %s\n", prog_name, prog_vers);
205 	printf(
206 "Usage:\n"
207 "ebtables -[ADI] chain rule-specification [options]\n"
208 "ebtables -P chain target\n"
209 "ebtables -[LFZ] [chain]\n"
210 "ebtables -[NX] [chain]\n"
211 "ebtables -E old-chain-name new-chain-name\n\n"
212 "Commands:\n"
213 "--append -A chain             : append to chain\n"
214 "--delete -D chain             : delete matching rule from chain\n"
215 "--delete -D chain rulenum     : delete rule at position rulenum from chain\n"
216 "--change-counters -C chain\n"
217 "          [rulenum] pcnt bcnt : change counters of existing rule\n"
218 "--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
219 "--list   -L [chain]           : list the rules in a chain or in all chains\n"
220 "--flush  -F [chain]           : delete all rules in chain or in all chains\n"
221 "--init-table                  : replace the kernel table with the initial table\n"
222 "--zero   -Z [chain]           : put counters on zero in chain or in all chains\n"
223 "--policy -P chain target      : change policy on chain to target\n"
224 "--new-chain -N chain          : create a user defined chain\n"
225 "--rename-chain -E old new     : rename a chain\n"
226 "--delete-chain -X [chain]     : delete a user defined chain\n"
227 "Options:\n"
228 "[!] --proto  -p proto         : protocol hexadecimal, by name or LENGTH\n"
229 "[!] --src    -s address[/mask]: source mac address\n"
230 "[!] --dst    -d address[/mask]: destination mac address\n"
231 "[!] --in-if  -i name[+]       : network input interface name\n"
232 "[!] --out-if -o name[+]       : network output interface name\n"
233 "[!] --logical-in  name[+]     : logical bridge input interface name\n"
234 "[!] --logical-out name[+]     : logical bridge output interface name\n"
235 "--set-counters -c chain\n"
236 "          pcnt bcnt           : set the counters of the to be added rule\n"
237 "--modprobe -M program         : try to insert modules using this program\n"
238 "--concurrent                  : use a file lock to support concurrent scripts\n"
239 "--verbose -v                  : verbose mode\n"
240 "--version -V                  : print package version\n\n"
241 "Environment variable:\n"
242 /*ATOMIC_ENV_VARIABLE "          : if set <FILE> (see above) will equal its value"*/
243 "\n\n");
244 	for (; m != NULL; m = m->next) {
245 		printf("\n");
246 		m->match->help();
247 	}
248 	if (t != NULL) {
249 		printf("\n");
250 		t->help();
251 	}
252 }
253 
254 /* Execute command L */
list_rules(struct nft_handle * h,const char * chain,const char * table,int rule_nr,int verbose,int numeric,int expanded,int linenumbers,int counters)255 static int list_rules(struct nft_handle *h, const char *chain, const char *table,
256 		      int rule_nr, int verbose, int numeric, int expanded,
257 		      int linenumbers, int counters)
258 {
259 	unsigned int format;
260 
261 	format = FMT_OPTIONS | FMT_C_COUNTS;
262 	if (verbose)
263 		format |= FMT_VIA;
264 
265 	if (numeric)
266 		format |= FMT_NUMERIC;
267 
268 	if (!expanded)
269 		format |= FMT_KILOMEGAGIGA;
270 
271 	if (linenumbers)
272 		format |= FMT_LINENUMBERS;
273 
274 	if (!counters)
275 		format |= FMT_NOCOUNTS;
276 
277 	return nft_cmd_rule_list(h, chain, table, rule_nr, format);
278 }
279 
280 /* This code is very similar to iptables/xtables.c:command_match() */
ebt_load_match(const char * name)281 static void ebt_load_match(const char *name)
282 {
283 	struct option *opts = xt_params->opts;
284 	struct xtables_match *m;
285 	size_t size;
286 
287 	m = xtables_find_match(name, XTF_TRY_LOAD, NULL);
288 	if (m == NULL) {
289 		fprintf(stderr, "Unable to load %s match\n", name);
290 		return;
291 	}
292 
293 	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
294 	m->m = xtables_calloc(1, size);
295 	m->m->u.match_size = size;
296 	strcpy(m->m->u.user.name, m->name);
297 	m->m->u.user.revision = m->revision;
298 	xs_init_match(m);
299 
300 	if (m->x6_options != NULL)
301 		opts = xtables_options_xfrm(xt_params->orig_opts, opts,
302 					    m->x6_options, &m->option_offset);
303 	else if (m->extra_opts != NULL)
304 		opts = xtables_merge_options(xt_params->orig_opts, opts,
305 					     m->extra_opts, &m->option_offset);
306 	else
307 		return;
308 
309 	if (opts == NULL)
310 		xtables_error(OTHER_PROBLEM, "Can't alloc memory");
311 	xt_params->opts = opts;
312 }
313 
ebt_load_watcher(const char * name)314 static void ebt_load_watcher(const char *name)
315 {
316 	struct option *opts = xt_params->opts;
317 	struct xtables_target *watcher;
318 	size_t size;
319 
320 	watcher = xtables_find_target(name, XTF_TRY_LOAD);
321 	if (!watcher) {
322 		fprintf(stderr, "Unable to load %s watcher\n", name);
323 		return;
324 	}
325 
326 	size = XT_ALIGN(sizeof(struct xt_entry_target)) + watcher->size;
327 
328 	watcher->t = xtables_calloc(1, size);
329 	watcher->t->u.target_size = size;
330 	snprintf(watcher->t->u.user.name,
331 		sizeof(watcher->t->u.user.name), "%s", name);
332 	watcher->t->u.user.name[sizeof(watcher->t->u.user.name)-1] = '\0';
333 	watcher->t->u.user.revision = watcher->revision;
334 
335 	xs_init_target(watcher);
336 
337 	if (watcher->x6_options != NULL)
338 		opts = xtables_options_xfrm(xt_params->orig_opts, opts,
339 					    watcher->x6_options,
340 					    &watcher->option_offset);
341 	else if (watcher->extra_opts != NULL)
342 		opts = xtables_merge_options(xt_params->orig_opts, opts,
343 					     watcher->extra_opts,
344 					     &watcher->option_offset);
345 	else
346 		return;
347 
348 	if (opts == NULL)
349 		xtables_error(OTHER_PROBLEM, "Can't alloc memory");
350 	xt_params->opts = opts;
351 }
352 
ebt_load_match_extensions(void)353 static void ebt_load_match_extensions(void)
354 {
355 	ebt_load_match("802_3");
356 	ebt_load_match("arp");
357 	ebt_load_match("ip");
358 	ebt_load_match("ip6");
359 	ebt_load_match("mark_m");
360 	ebt_load_match("limit");
361 	ebt_load_match("pkttype");
362 	ebt_load_match("vlan");
363 	ebt_load_match("stp");
364 	ebt_load_match("among");
365 
366 	ebt_load_watcher("log");
367 	ebt_load_watcher("nflog");
368 }
369 
ebt_add_match(struct xtables_match * m,struct iptables_command_state * cs)370 struct xtables_match *ebt_add_match(struct xtables_match *m,
371 				    struct iptables_command_state *cs)
372 {
373 	struct xtables_rule_match **rule_matches = &cs->matches;
374 	struct ebt_match *newnode, **matchp;
375 	struct xtables_match *newm;
376 
377 	newm = xtables_find_match(m->name, XTF_LOAD_MUST_SUCCEED, rule_matches);
378 	if (newm == NULL)
379 		xtables_error(OTHER_PROBLEM,
380 			      "Unable to add match %s", m->name);
381 
382 	newm->m = xtables_calloc(1, m->m->u.match_size);
383 	memcpy(newm->m, m->m, m->m->u.match_size);
384 	xs_init_match(newm);
385 
386 	/* glue code for watchers */
387 	newnode = xtables_calloc(1, sizeof(struct ebt_match));
388 	newnode->ismatch = true;
389 	newnode->u.match = newm;
390 
391 	for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
392 		;
393 	*matchp = newnode;
394 
395 	return newm;
396 }
397 
ebt_add_watcher(struct xtables_target * watcher,struct iptables_command_state * cs)398 struct xtables_target *ebt_add_watcher(struct xtables_target *watcher,
399 				       struct iptables_command_state *cs)
400 {
401 	struct ebt_match *newnode, **matchp;
402 	struct xtables_target *clone;
403 
404 	clone = xtables_malloc(sizeof(struct xtables_target));
405 	memcpy(clone, watcher, sizeof(struct xtables_target));
406 	clone->next = clone;
407 	clone->udata = NULL;
408 	xs_init_target(clone);
409 
410 	clone->t = xtables_calloc(1, watcher->t->u.target_size);
411 	memcpy(clone->t, watcher->t, watcher->t->u.target_size);
412 
413 
414 	newnode = xtables_calloc(1, sizeof(struct ebt_match));
415 	newnode->u.watcher = clone;
416 
417 	for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
418 		;
419 	*matchp = newnode;
420 
421 	return clone;
422 }
423 
ebt_command_default(struct iptables_command_state * cs,struct xtables_globals * unused,bool ebt_invert)424 int ebt_command_default(struct iptables_command_state *cs,
425 			struct xtables_globals *unused, bool ebt_invert)
426 {
427 	struct xtables_target *t = cs->target;
428 	struct xtables_match *m;
429 	struct ebt_match *matchp;
430 
431 	/* Is it a target option? */
432 	if (cs->target != NULL &&
433 	    (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
434 	    cs->c >= cs->target->option_offset &&
435 	    cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
436 		xtables_option_tpcall(cs->c, cs->argv, ebt_invert,
437 				      cs->target, &cs->eb);
438 		return 0;
439 	}
440 
441 	/* check previously added matches/watchers to this rule first */
442 	for (matchp = cs->match_list; matchp; matchp = matchp->next) {
443 		if (matchp->ismatch) {
444 			m = matchp->u.match;
445 			if (!m->parse && !m->x6_parse)
446 				continue;
447 			if (cs->c < m->option_offset ||
448 			    cs->c >= m->option_offset + XT_OPTION_OFFSET_SCALE)
449 				continue;
450 			xtables_option_mpcall(cs->c, cs->argv, ebt_invert,
451 					      m, &cs->eb);
452 			return 0;
453 		} else {
454 			t = matchp->u.watcher;
455 			if (!t->parse && !t->x6_parse)
456 				continue;
457 			if (cs->c < t->option_offset ||
458 			    cs->c >= t->option_offset + XT_OPTION_OFFSET_SCALE)
459 				continue;
460 			xtables_option_tpcall(cs->c, cs->argv, ebt_invert,
461 					      t, &cs->eb);
462 			return 0;
463 		}
464 	}
465 
466 	/* Is it a match_option? */
467 	for (m = xtables_matches; m; m = m->next) {
468 		if (!m->parse && !m->x6_parse)
469 			continue;
470 		if (cs->c < m->option_offset ||
471 		    cs->c >= m->option_offset + XT_OPTION_OFFSET_SCALE)
472 			continue;
473 		m = ebt_add_match(m, cs);
474 		xtables_option_mpcall(cs->c, cs->argv, ebt_invert, m, &cs->eb);
475 		return 0;
476 	}
477 
478 	/* Is it a watcher option? */
479 	for (t = xtables_targets; t; t = t->next) {
480 		if (!(t->ext_flags & XTABLES_EXT_WATCHER))
481 			continue;
482 
483 		if (!t->parse && !t->x6_parse)
484 			continue;
485 		if (cs->c < t->option_offset ||
486 		    cs->c >= t->option_offset + XT_OPTION_OFFSET_SCALE)
487 			continue;
488 		t = ebt_add_watcher(t, cs);
489 		xtables_option_tpcall(cs->c, cs->argv, ebt_invert, t, &cs->eb);
490 		return 0;
491 	}
492 	if (cs->c == ':')
493 		xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
494 		              "requires an argument", cs->argv[optind - 1]);
495 	if (cs->c == '?') {
496 		char optoptstr[3] = {'-', optopt, '\0'};
497 
498 		xtables_error(PARAMETER_PROBLEM, "unknown option \"%s\"",
499 			      optopt ? optoptstr : cs->argv[optind - 1]);
500 	}
501 	xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
502 }
503 
nft_init_eb(struct nft_handle * h,const char * pname)504 int nft_init_eb(struct nft_handle *h, const char *pname)
505 {
506 	ebtables_globals.program_name = pname;
507 	if (xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE) < 0) {
508 		fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
509 			ebtables_globals.program_name,
510 			ebtables_globals.program_version);
511 		exit(1);
512 	}
513 	init_extensions();
514 	init_extensionsb();
515 
516 	if (nft_init(h, NFPROTO_BRIDGE) < 0)
517 		xtables_error(OTHER_PROBLEM,
518 			      "Could not initialize nftables layer.");
519 
520 	/* manually registering ebt matches, given the original ebtables parser
521 	 * don't use '-m matchname' and the match can't be loaded dynamically when
522 	 * the user calls it.
523 	 */
524 	ebt_load_match_extensions();
525 
526 	return 0;
527 }
528 
nft_fini_eb(struct nft_handle * h)529 void nft_fini_eb(struct nft_handle *h)
530 {
531 	struct xtables_match *match;
532 	struct xtables_target *target;
533 
534 	for (match = xtables_matches; match; match = match->next) {
535 		free(match->m);
536 	}
537 	for (target = xtables_targets; target; target = target->next) {
538 		free(target->t);
539 	}
540 
541 	free(xt_params->opts);
542 
543 	nft_fini(h);
544 	xtables_fini();
545 }
546 
do_commandeb(struct nft_handle * h,int argc,char * argv[],char ** table,bool restore)547 int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
548 		 bool restore)
549 {
550 	struct iptables_command_state cs = {
551 		.argc = argc,
552 		.argv = argv,
553 		.jumpto	= "",
554 	};
555 	const struct builtin_table *t;
556 	struct xtables_args args = {
557 		.family	= h->family,
558 	};
559 	struct xt_cmd_parse p = {
560 		.table		= *table,
561 		.restore	= restore,
562 		.line		= line,
563 		.rule_ranges	= true,
564 		.ops		= &h->ops->cmd_parse,
565 	};
566 	int ret = 0;
567 
568 	if (h->ops->init_cs)
569 		h->ops->init_cs(&cs);
570 
571 	do_parse(argc, argv, &p, &cs, &args);
572 
573 	h->verbose	= p.verbose;
574 
575 	t = nft_table_builtin_find(h, p.table);
576 	if (!t)
577 		xtables_error(VERSION_PROBLEM,
578 			      "table '%s' does not exist", p.table);
579 
580 	switch (p.command) {
581 	case CMD_NEW_CHAIN:
582 	case CMD_NEW_CHAIN | CMD_SET_POLICY:
583 		ret = nft_cmd_chain_user_add(h, p.chain, p.table);
584 		if (!ret || !(p.command & CMD_SET_POLICY))
585 			break;
586 		/* fall through */
587 	case CMD_SET_POLICY:
588 		if (!nft_chain_builtin_find(t, p.chain)) {
589 			ret = ebt_cmd_user_chain_policy(h, p.table, p.chain,
590 							p.policy);
591 			break;
592 		}
593 		if (strcmp(p.policy, "RETURN") == 0) {
594 			xtables_error(PARAMETER_PROBLEM,
595 				      "Policy RETURN only allowed for user defined chains");
596 		}
597 		ret = nft_cmd_chain_set(h, p.table, p.chain, p.policy, NULL);
598 		if (ret < 0)
599 			xtables_error(PARAMETER_PROBLEM, "Wrong policy");
600 		break;
601 	case CMD_LIST:
602 	case CMD_LIST | CMD_ZERO:
603 	case CMD_LIST | CMD_ZERO_NUM:
604 	case CMD_LIST_RULES:
605 	case CMD_LIST_RULES | CMD_ZERO:
606 	case CMD_LIST_RULES | CMD_ZERO_NUM:
607 		if (p.command & CMD_LIST)
608 			ret = list_rules(h, p.chain, p.table, p.rulenum,
609 					 cs.options & OPT_VERBOSE,
610 					 0,
611 					 /*cs.options&OPT_EXPANDED*/0,
612 					 cs.options&OPT_LINENUMBERS,
613 					 cs.options&OPT_LIST_C);
614 		else if (p.command & CMD_LIST_RULES)
615 			ret = nft_cmd_rule_list_save(h, p.chain, p.table,
616 						     p.rulenum,
617 						     cs.options & OPT_VERBOSE);
618 		if (ret && (p.command & CMD_ZERO))
619 			ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
620 							  cs.options & OPT_VERBOSE);
621 		if (ret && (p.command & CMD_ZERO_NUM))
622 			ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
623 							 p.rulenum - 1);
624 		break;
625 	case CMD_ZERO:
626 		ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
627 						  cs.options & OPT_VERBOSE);
628 		break;
629 	case CMD_ZERO_NUM:
630 		ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
631 						 p.rulenum - 1);
632 		break;
633 	case CMD_FLUSH:
634 		ret = nft_cmd_rule_flush(h, p.chain, p.table,
635 					 cs.options & OPT_VERBOSE);
636 		break;
637 	case CMD_APPEND:
638 		ret = nft_cmd_rule_append(h, p.chain, p.table, &cs,
639 					  cs.options & OPT_VERBOSE);
640 		break;
641 	case CMD_INSERT:
642 		ret = nft_cmd_rule_insert(h, p.chain, p.table, &cs,
643 					  p.rulenum - 1,
644 					  cs.options & OPT_VERBOSE);
645 		break;
646 	case CMD_DELETE:
647 	case CMD_DELETE_NUM:
648 		ret = delete_entry(h, p.chain, p.table, &cs, p.rulenum - 1,
649 				   p.rulenum_end, cs.options & OPT_VERBOSE);
650 		break;
651 	case CMD_DELETE_CHAIN:
652 		ret = nft_cmd_chain_del(h, p.chain, p.table, 0);
653 		break;
654 	case CMD_RENAME_CHAIN:
655 		ret = nft_cmd_chain_user_rename(h, p.chain, p.table, p.newname);
656 		break;
657 	case CMD_INIT_TABLE:
658 		ret = nft_cmd_table_flush(h, p.table, false);
659 		break;
660 	case CMD_CHECK:
661 		ret = nft_cmd_rule_check(h, p.chain, p.table,
662 					 &cs, cs.options & OPT_VERBOSE);
663 		break;
664 	case CMD_CHANGE_COUNTERS:
665 		ret = change_entry_counters(h, p.chain, p.table, &cs,
666 					    p.rulenum - 1, p.rulenum_end,
667 					    args.counter_op,
668 					    cs.options & OPT_VERBOSE);
669 		break;
670 	case CMD_REPLACE:
671 		ret = nft_cmd_rule_replace(h, p.chain, p.table, &cs,
672 					   p.rulenum - 1,
673 					   cs.options & OPT_VERBOSE);
674 		break;
675 	default:
676 		/* We should never reach this... */
677 		exit_tryhelp(2, line);
678 	}
679 
680 	ebt_cs_clean(&cs);
681 	return ret;
682 }
683