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