/* Code to take an iptables-style command line and do it. */ /* * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au * * (C) 2000-2002 by the netfilter coreteam : * Paul 'Rusty' Russell * Marc Boucher * James Morris * Harald Welte * Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xshared.h" static const char unsupported_rev[] = " [unsupported revision]"; static struct option original_opts[] = { {.name = "append", .has_arg = 1, .val = 'A'}, {.name = "delete", .has_arg = 1, .val = 'D'}, {.name = "check", .has_arg = 1, .val = 'C'}, {.name = "insert", .has_arg = 1, .val = 'I'}, {.name = "replace", .has_arg = 1, .val = 'R'}, {.name = "list", .has_arg = 2, .val = 'L'}, {.name = "list-rules", .has_arg = 2, .val = 'S'}, {.name = "flush", .has_arg = 2, .val = 'F'}, {.name = "zero", .has_arg = 2, .val = 'Z'}, {.name = "new-chain", .has_arg = 1, .val = 'N'}, {.name = "delete-chain", .has_arg = 2, .val = 'X'}, {.name = "rename-chain", .has_arg = 1, .val = 'E'}, {.name = "policy", .has_arg = 1, .val = 'P'}, {.name = "source", .has_arg = 1, .val = 's'}, {.name = "destination", .has_arg = 1, .val = 'd'}, {.name = "src", .has_arg = 1, .val = 's'}, /* synonym */ {.name = "dst", .has_arg = 1, .val = 'd'}, /* synonym */ {.name = "protocol", .has_arg = 1, .val = 'p'}, {.name = "in-interface", .has_arg = 1, .val = 'i'}, {.name = "jump", .has_arg = 1, .val = 'j'}, {.name = "table", .has_arg = 1, .val = 't'}, {.name = "match", .has_arg = 1, .val = 'm'}, {.name = "numeric", .has_arg = 0, .val = 'n'}, {.name = "out-interface", .has_arg = 1, .val = 'o'}, {.name = "verbose", .has_arg = 0, .val = 'v'}, {.name = "wait", .has_arg = 2, .val = 'w'}, {.name = "wait-interval", .has_arg = 2, .val = 'W'}, {.name = "exact", .has_arg = 0, .val = 'x'}, {.name = "fragments", .has_arg = 0, .val = 'f'}, {.name = "version", .has_arg = 0, .val = 'V'}, {.name = "help", .has_arg = 2, .val = 'h'}, {.name = "line-numbers", .has_arg = 0, .val = '0'}, {.name = "modprobe", .has_arg = 1, .val = 'M'}, {.name = "set-counters", .has_arg = 1, .val = 'c'}, {.name = "goto", .has_arg = 1, .val = 'g'}, {.name = "ipv4", .has_arg = 0, .val = '4'}, {.name = "ipv6", .has_arg = 0, .val = '6'}, {NULL}, }; struct xtables_globals iptables_globals = { .option_offset = 0, .program_version = PACKAGE_VERSION " (legacy)", .orig_opts = original_opts, .compat_rev = xtables_compatible_revision, }; /* * All functions starting with "parse" should succeed, otherwise * the program fails. * Most routines return pointers to static data that may change * between calls to the same or other routines with a few exceptions: * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" * return global static data. */ /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ static int print_match(const struct xt_entry_match *m, const struct ipt_ip *ip, int numeric) { const char *name = m->u.user.name; const int revision = m->u.user.revision; struct xtables_match *match, *mt; match = xtables_find_match(name, XTF_TRY_LOAD, NULL); if (match) { mt = xtables_find_match_revision(name, XTF_TRY_LOAD, match, revision); if (mt && mt->print) mt->print(ip, m, numeric); else if (match->print) printf("%s%s ", match->name, unsupported_rev); else printf("%s ", match->name); if (match->next == match) free(match); } else { if (name[0]) printf("UNKNOWN match `%s' ", name); } /* Don't stop iterating. */ return 0; } /* e is called `fw' here for historical reasons */ static void print_firewall(const struct ipt_entry *fw, const char *targname, unsigned int num, unsigned int format, struct xtc_handle *const handle) { struct xtables_target *target, *tg; const struct xt_entry_target *t; if (!iptc_is_chain(targname, handle)) target = xtables_find_target(targname, XTF_TRY_LOAD); else target = xtables_find_target(XT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED); t = ipt_get_target((struct ipt_entry *)fw); print_rule_details(num, &fw->counters, targname, fw->ip.proto, fw->ip.flags, fw->ip.invflags, format); print_fragment(fw->ip.flags, fw->ip.invflags, format, false); print_ifaces(fw->ip.iniface, fw->ip.outiface, fw->ip.invflags, format); print_ipv4_addresses(fw, format); if (format & FMT_NOTABLE) fputs(" ", stdout); #ifdef IPT_F_GOTO if(fw->ip.flags & IPT_F_GOTO) printf("[goto] "); #endif IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC); if (target) { const int revision = t->u.user.revision; tg = xtables_find_target_revision(targname, XTF_TRY_LOAD, target, revision); if (tg && tg->print) /* Print the target information. */ tg->print(&fw->ip, t, format & FMT_NUMERIC); else if (target->print) printf(" %s%s", target->name, unsupported_rev); if (target->next == target) free(target); } else if (t->u.target_size != sizeof(*t)) printf("[%u bytes of unknown target data] ", (unsigned int)(t->u.target_size - sizeof(*t))); if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); } static void print_firewall_line(const struct ipt_entry *fw, struct xtc_handle *const h) { struct xt_entry_target *t; t = ipt_get_target((struct ipt_entry *)fw); print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); } static int append_entry(const xt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], const struct in_addr dmasks[], int verbose, struct xtc_handle *handle) { unsigned int i, j; int ret = 1; for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_append_entry(chain, fw, handle); } } return ret; } static int replace_entry(const xt_chainlabel chain, struct ipt_entry *fw, unsigned int rulenum, const struct in_addr *saddr, const struct in_addr *smask, const struct in_addr *daddr, const struct in_addr *dmask, int verbose, struct xtc_handle *handle) { fw->ip.src.s_addr = saddr->s_addr; fw->ip.dst.s_addr = daddr->s_addr; fw->ip.smsk.s_addr = smask->s_addr; fw->ip.dmsk.s_addr = dmask->s_addr; if (verbose) print_firewall_line(fw, handle); return iptc_replace_entry(chain, fw, rulenum, handle); } static int insert_entry(const xt_chainlabel chain, struct ipt_entry *fw, unsigned int rulenum, unsigned int nsaddrs, const struct in_addr saddrs[], const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], const struct in_addr dmasks[], int verbose, struct xtc_handle *handle) { unsigned int i, j; int ret = 1; for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_insert_entry(chain, fw, rulenum, handle); } } return ret; } static int delete_entry(const xt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], const struct in_addr dmasks[], int verbose, struct xtc_handle *handle, struct xtables_rule_match *matches, const struct xtables_target *target) { unsigned int i, j; int ret = 1; unsigned char *mask; mask = make_delete_mask(matches, target, sizeof(*fw)); for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_delete_entry(chain, fw, mask, handle); } } free(mask); return ret; } static int check_entry(const xt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr *saddrs, const struct in_addr *smasks, unsigned int ndaddrs, const struct in_addr *daddrs, const struct in_addr *dmasks, bool verbose, struct xtc_handle *handle, struct xtables_rule_match *matches, const struct xtables_target *target) { unsigned int i, j; int ret = 1; unsigned char *mask; mask = make_delete_mask(matches, target, sizeof(*fw)); for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_check_entry(chain, fw, mask, handle); } } free(mask); return ret; } int for_each_chain4(int (*fn)(const xt_chainlabel, int, struct xtc_handle *), int verbose, int builtinstoo, struct xtc_handle *handle) { int ret = 1; const char *chain; char *chains; unsigned int i, chaincount = 0; chain = iptc_first_chain(handle); while (chain) { chaincount++; chain = iptc_next_chain(handle); } chains = xtables_malloc(sizeof(xt_chainlabel) * chaincount); i = 0; chain = iptc_first_chain(handle); while (chain) { strcpy(chains + i*sizeof(xt_chainlabel), chain); i++; chain = iptc_next_chain(handle); } for (i = 0; i < chaincount; i++) { if (!builtinstoo && iptc_builtin(chains + i*sizeof(xt_chainlabel), handle) == 1) continue; ret &= fn(chains + i*sizeof(xt_chainlabel), verbose, handle); } free(chains); return ret; } int flush_entries4(const xt_chainlabel chain, int verbose, struct xtc_handle *handle) { if (!chain) return for_each_chain4(flush_entries4, verbose, 1, handle); if (verbose) fprintf(stdout, "Flushing chain `%s'\n", chain); return iptc_flush_entries(chain, handle); } static int zero_entries(const xt_chainlabel chain, int verbose, struct xtc_handle *handle) { if (!chain) return for_each_chain4(zero_entries, verbose, 1, handle); if (verbose) fprintf(stdout, "Zeroing chain `%s'\n", chain); return iptc_zero_entries(chain, handle); } int delete_chain4(const xt_chainlabel chain, int verbose, struct xtc_handle *handle) { if (!chain) return for_each_chain4(delete_chain4, verbose, 0, handle); if (verbose) fprintf(stdout, "Deleting chain `%s'\n", chain); return iptc_delete_chain(chain, handle); } static int list_entries(const xt_chainlabel chain, int rulenum, int verbose, int numeric, int expanded, int linenumbers, struct xtc_handle *handle) { int found = 0; unsigned int format; const char *this; format = FMT_OPTIONS; if (!verbose) format |= FMT_NOCOUNTS; else format |= FMT_VIA; if (numeric) format |= FMT_NUMERIC; if (!expanded) format |= FMT_KILOMEGAGIGA; if (linenumbers) format |= FMT_LINENUMBERS; for (this = iptc_first_chain(handle); this; this = iptc_next_chain(handle)) { const struct ipt_entry *i; unsigned int num; if (chain && strcmp(chain, this) != 0) continue; if (found) printf("\n"); if (!rulenum) { struct xt_counters counters; unsigned int urefs; const char *pol; int refs = -1; pol = iptc_get_policy(this, &counters, handle); if (!pol && iptc_get_references(&urefs, this, handle)) refs = urefs; print_header(format, this, pol, &counters, refs, 0); } i = iptc_first_rule(this, handle); num = 0; while (i) { num++; if (!rulenum || num == rulenum) print_firewall(i, iptc_get_target(i, handle), num, format, handle); i = iptc_next_rule(i, handle); } found = 1; } errno = ENOENT; return found; } #define IP_PARTS_NATIVE(n) \ (unsigned int)((n)>>24)&0xFF, \ (unsigned int)((n)>>16)&0xFF, \ (unsigned int)((n)>>8)&0xFF, \ (unsigned int)((n)&0xFF) #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n)) /* We want this to be readable, so only print out necessary fields. * Because that's the kind of world I want to live in. */ void print_rule4(const struct ipt_entry *e, struct xtc_handle *h, const char *chain, int counters) { const struct xt_entry_target *t; const char *target_name; /* print counters for iptables-save */ if (counters > 0) printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt); /* print chain name */ printf("-A %s", chain); /* Print IP part. */ save_ipv4_addr('s', &e->ip.src, &e->ip.smsk, e->ip.invflags & IPT_INV_SRCIP); save_ipv4_addr('d', &e->ip.dst, &e->ip.dmsk, e->ip.invflags & IPT_INV_DSTIP); save_rule_details(e->ip.iniface, e->ip.outiface, e->ip.proto, e->ip.flags & IPT_F_FRAG, e->ip.invflags); /* Print matchinfo part */ if (e->target_offset) IPT_MATCH_ITERATE(e, print_match_save, &e->ip); /* print counters for iptables -R */ if (counters < 0) printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt); /* Print target name and targinfo part */ target_name = iptc_get_target(e, h); t = ipt_get_target((struct ipt_entry *)e); if (t->u.user.name[0]) { const char *name = t->u.user.name; const int revision = t->u.user.revision; struct xtables_target *target, *tg, *tg2; target = xtables_find_target(name, XTF_TRY_LOAD); if (!target) { fprintf(stderr, "Can't find library for target `%s'\n", name); exit(1); } tg = tg2 = xtables_find_target_revision(name, XTF_TRY_LOAD, target, revision); if (!tg2) tg2 = target; printf(" -j %s", tg2->alias ? tg2->alias(t) : target_name); if (tg && tg->save) tg->save(&e->ip, t); else if (target->save) printf(unsupported_rev); else { /* If the target size is greater than xt_entry_target * there is something to be saved, we just don't know * how to print it */ if (t->u.target_size != sizeof(struct xt_entry_target)) { fprintf(stderr, "Target `%s' is missing " "save function\n", name); exit(1); } } } else if (target_name && (*target_name != '\0')) #ifdef IPT_F_GOTO printf(" -%c %s", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name); #else printf(" -j %s", target_name); #endif printf("\n"); } static int list_rules(const xt_chainlabel chain, int rulenum, int counters, struct xtc_handle *handle) { const char *this = NULL; int found = 0; if (counters) counters = -1; /* iptables -c format */ /* Dump out chain names first, * thereby preventing dependency conflicts */ if (!rulenum) for (this = iptc_first_chain(handle); this; this = iptc_next_chain(handle)) { if (chain && strcmp(this, chain) != 0) continue; if (iptc_builtin(this, handle)) { struct xt_counters count; printf("-P %s %s", this, iptc_get_policy(this, &count, handle)); if (counters) printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt); printf("\n"); } else { printf("-N %s\n", this); } } for (this = iptc_first_chain(handle); this; this = iptc_next_chain(handle)) { const struct ipt_entry *e; int num = 0; if (chain && strcmp(this, chain) != 0) continue; /* Dump out rules */ e = iptc_first_rule(this, handle); while(e) { num++; if (!rulenum || num == rulenum) print_rule4(e, handle, this, counters); e = iptc_next_rule(e, handle); } found = 1; } errno = ENOENT; return found; } static struct ipt_entry * generate_entry(const struct ipt_entry *fw, struct xtables_rule_match *matches, struct xt_entry_target *target) { unsigned int size; struct xtables_rule_match *matchp; struct ipt_entry *e; size = sizeof(struct ipt_entry); for (matchp = matches; matchp; matchp = matchp->next) size += matchp->match->m->u.match_size; e = xtables_malloc(size + target->u.target_size); *e = *fw; e->target_offset = size; e->next_offset = size + target->u.target_size; size = 0; for (matchp = matches; matchp; matchp = matchp->next) { memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size); size += matchp->match->m->u.match_size; } memcpy(e->elems + size, target, target->u.target_size); return e; } int do_command4(int argc, char *argv[], char **table, struct xtc_handle **handle, bool restore) { struct xt_cmd_parse_ops cmd_parse_ops = { .proto_parse = ipv4_proto_parse, .post_parse = ipv4_post_parse, .option_name = ip46t_option_name, .option_invert = ip46t_option_invert, .command_default = command_default, .print_help = xtables_printhelp, }; struct xt_cmd_parse p = { .table = *table, .restore = restore, .line = line, .ops = &cmd_parse_ops, }; struct iptables_command_state cs = { .jumpto = "", .argv = argv, }; struct xtables_args args = { .family = AF_INET, }; struct ipt_entry *e = NULL; unsigned int nsaddrs = 0, ndaddrs = 0; struct in_addr *saddrs = NULL, *smasks = NULL; struct in_addr *daddrs = NULL, *dmasks = NULL; int verbose = 0; int wait = 0; const char *chain = NULL; const char *policy = NULL, *newname = NULL; unsigned int rulenum = 0, command = 0; int ret = 1; do_parse(argc, argv, &p, &cs, &args); command = p.command; chain = p.chain; *table = p.table; rulenum = p.rulenum; policy = p.policy; newname = p.newname; verbose = p.verbose; wait = args.wait; nsaddrs = args.s.naddrs; ndaddrs = args.d.naddrs; saddrs = args.s.addr.v4; daddrs = args.d.addr.v4; smasks = args.s.mask.v4; dmasks = args.d.mask.v4; iface_to_mask(cs.fw.ip.iniface, cs.fw.ip.iniface_mask); iface_to_mask(cs.fw.ip.outiface, cs.fw.ip.outiface_mask); /* Attempt to acquire the xtables lock */ if (!restore) xtables_lock_or_exit(wait); /* only allocate handle if we weren't called with a handle */ if (!*handle) *handle = iptc_init(*table); /* try to insmod the module if iptc_init failed */ if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1) *handle = iptc_init(*table); if (!*handle) xtables_error(VERSION_PROBLEM, "can't initialize iptables table `%s': %s", *table, iptc_strerror(errno)); if (command == CMD_APPEND || command == CMD_DELETE || command == CMD_CHECK || command == CMD_INSERT || command == CMD_REPLACE) { if (cs.target && iptc_is_chain(cs.jumpto, *handle)) { fprintf(stderr, "Warning: using chain %s, not extension\n", cs.jumpto); if (cs.target->t) free(cs.target->t); cs.target = NULL; } /* If they didn't specify a target, or it's a chain name, use standard. */ if (!cs.target && (strlen(cs.jumpto) == 0 || iptc_is_chain(cs.jumpto, *handle))) { size_t size; cs.target = xtables_find_target(XT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED); size = sizeof(struct xt_entry_target) + cs.target->size; cs.target->t = xtables_calloc(1, size); cs.target->t->u.target_size = size; strcpy(cs.target->t->u.user.name, cs.jumpto); if (!iptc_is_chain(cs.jumpto, *handle)) cs.target->t->u.user.revision = cs.target->revision; xs_init_target(cs.target); } if (!cs.target) { /* It is no chain, and we can't load a plugin. * We cannot know if the plugin is corrupt, non * existent OR if the user just misspelled a * chain. */ #ifdef IPT_F_GOTO if (cs.fw.ip.flags & IPT_F_GOTO) xtables_error(PARAMETER_PROBLEM, "goto '%s' is not a chain", cs.jumpto); #endif xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED); } else { e = generate_entry(&cs.fw, cs.matches, cs.target->t); } } switch (command) { case CMD_APPEND: ret = append_entry(chain, e, nsaddrs, saddrs, smasks, ndaddrs, daddrs, dmasks, cs.options&OPT_VERBOSE, *handle); break; case CMD_DELETE: ret = delete_entry(chain, e, nsaddrs, saddrs, smasks, ndaddrs, daddrs, dmasks, cs.options&OPT_VERBOSE, *handle, cs.matches, cs.target); break; case CMD_DELETE_NUM: ret = iptc_delete_num_entry(chain, rulenum - 1, *handle); break; case CMD_CHECK: ret = check_entry(chain, e, nsaddrs, saddrs, smasks, ndaddrs, daddrs, dmasks, cs.options&OPT_VERBOSE, *handle, cs.matches, cs.target); break; case CMD_REPLACE: ret = replace_entry(chain, e, rulenum - 1, saddrs, smasks, daddrs, dmasks, cs.options&OPT_VERBOSE, *handle); break; case CMD_INSERT: ret = insert_entry(chain, e, rulenum - 1, nsaddrs, saddrs, smasks, ndaddrs, daddrs, dmasks, cs.options&OPT_VERBOSE, *handle); break; case CMD_FLUSH: ret = flush_entries4(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_ZERO: ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_ZERO_NUM: ret = iptc_zero_counter(chain, rulenum, *handle); break; case CMD_LIST: case CMD_LIST|CMD_ZERO: case CMD_LIST|CMD_ZERO_NUM: ret = list_entries(chain, rulenum, cs.options&OPT_VERBOSE, cs.options&OPT_NUMERIC, cs.options&OPT_EXPANDED, cs.options&OPT_LINENUMBERS, *handle); if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); if (ret && (command & CMD_ZERO_NUM)) ret = iptc_zero_counter(chain, rulenum, *handle); break; case CMD_LIST_RULES: case CMD_LIST_RULES|CMD_ZERO: case CMD_LIST_RULES|CMD_ZERO_NUM: ret = list_rules(chain, rulenum, cs.options&OPT_VERBOSE, *handle); if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); if (ret && (command & CMD_ZERO_NUM)) ret = iptc_zero_counter(chain, rulenum, *handle); break; case CMD_NEW_CHAIN: ret = iptc_create_chain(chain, *handle); break; case CMD_DELETE_CHAIN: ret = delete_chain4(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_RENAME_CHAIN: ret = iptc_rename_chain(chain, newname, *handle); break; case CMD_SET_POLICY: ret = iptc_set_policy(chain, policy, cs.options&OPT_COUNTERS ? &cs.fw.counters : NULL, *handle); break; case CMD_NONE: /* do_parse ignored the line (eg: -4 with ip6tables-restore) */ break; default: /* We should never reach this... */ exit_tryhelp(2, line); } if (verbose > 1) dump_entries(*handle); xtables_clear_iptables_command_state(&cs); if (e != NULL) { free(e); e = NULL; } xtables_clear_args(&args); xtables_free_opts(1); return ret; }