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