diff options
-rw-r--r-- | iptables/nft-bridge.c | 2 | ||||
-rw-r--r-- | iptables/nft.c | 228 | ||||
-rw-r--r-- | iptables/nft.h | 4 | ||||
-rwxr-xr-x | iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 | 7 | ||||
-rw-r--r-- | iptables/xtables-eb.c | 20 | ||||
-rw-r--r-- | iptables/xtables-restore.c | 23 |
6 files changed, 265 insertions, 19 deletions
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c index 848ca793..ddfbee16 100644 --- a/iptables/nft-bridge.c +++ b/iptables/nft-bridge.c @@ -358,7 +358,7 @@ static void nft_bridge_print_header(unsigned int format, const char *chain, bool basechain, uint32_t refs, uint32_t entries) { printf("Bridge chain: %s, entries: %u, policy: %s\n", - chain, entries, basechain ? pol : "RETURN"); + chain, entries, pol ?: "RETURN"); } static void print_matches_and_watchers(const struct iptables_command_state *cs, diff --git a/iptables/nft.c b/iptables/nft.c index 4010ccd5..2d527358 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -55,6 +55,7 @@ #include "nft.h" #include "xshared.h" /* proto_to_name */ #include "nft-shared.h" +#include "nft-bridge.h" /* EBT_NOPROTO */ #include "xtables-config-parser.h" static void *nft_fn; @@ -1325,6 +1326,87 @@ retry: return ret; } +static bool nft_rule_is_policy_rule(struct nftnl_rule *r) +{ + const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {}; + const void *data; + uint32_t len; + + if (!nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) + return false; + + data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len); + if (nftnl_udata_parse(data, len, parse_udata_cb, tb) < 0) + return NULL; + + if (!tb[UDATA_TYPE_EBTABLES_POLICY] || + nftnl_udata_get_u32(tb[UDATA_TYPE_EBTABLES_POLICY]) != 1) + return false; + + return true; +} + +static struct nftnl_rule *nft_chain_last_rule(struct nftnl_chain *c) +{ + struct nftnl_rule *r = NULL, *last; + struct nftnl_rule_iter *iter; + + iter = nftnl_rule_iter_create(c); + if (!iter) + return NULL; + + do { + last = r; + r = nftnl_rule_iter_next(iter); + } while (r); + nftnl_rule_iter_destroy(iter); + + return last; +} + +static void nft_bridge_chain_postprocess(struct nft_handle *h, + struct nftnl_chain *c) +{ + struct nftnl_rule *last = nft_chain_last_rule(c); + struct nftnl_expr_iter *iter; + struct nftnl_expr *expr; + int verdict; + + if (!last || !nft_rule_is_policy_rule(last)) + return; + + iter = nftnl_expr_iter_create(last); + if (!iter) + return; + + expr = nftnl_expr_iter_next(iter); + if (!expr || + strcmp("counter", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME))) + goto out_iter; + + expr = nftnl_expr_iter_next(iter); + if (!expr || + strcmp("immediate", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)) || + !nftnl_expr_is_set(expr, NFTNL_EXPR_IMM_VERDICT)) + goto out_iter; + + verdict = nftnl_expr_get_u32(expr, NFTNL_EXPR_IMM_VERDICT); + switch (verdict) { + case NF_ACCEPT: + case NF_DROP: + break; + default: + goto out_iter; + } + + nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, verdict); + if (batch_rule_add(h, NFT_COMPAT_RULE_DELETE, last) < 0) + fprintf(stderr, "Failed to delete old policy rule\n"); + nftnl_chain_rule_del(last); +out_iter: + nftnl_expr_iter_destroy(iter); +} + static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data) { struct nftnl_chain *c = data; @@ -1378,6 +1460,10 @@ retry: } nftnl_rule_free(rule); + + if (h->family == NFPROTO_BRIDGE) + nft_bridge_chain_postprocess(h, c); + return 0; } @@ -1443,6 +1529,15 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list) if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY)) pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY); policy = policy_name[pol]; + } else if (h->family == NFPROTO_BRIDGE) { + if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) { + uint32_t pol; + + pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY); + policy = policy_name[pol]; + } else { + policy = "RETURN"; + } } if (ops->save_chain) @@ -1637,6 +1732,8 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table); nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain); + if (h->family == NFPROTO_BRIDGE) + nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT); ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); @@ -2278,7 +2375,6 @@ static void __nft_print_header(struct nft_handle *h, struct nftnl_chain *c, unsigned int format) { const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME); - uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY); bool basechain = !!nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM); uint32_t refs = nftnl_chain_get_u32(c, NFTNL_CHAIN_USE); uint32_t entries = nft_rule_count(h, c); @@ -2286,8 +2382,12 @@ static void __nft_print_header(struct nft_handle *h, .pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS), .bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES), }; + const char *pname = NULL; - ops->print_header(format, chain_name, policy_name[policy], + if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) + pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)]; + + ops->print_header(format, chain_name, pname, &ctrs, basechain, refs - entries, entries); } @@ -2671,8 +2771,111 @@ static int nft_action(struct nft_handle *h, int action) return ret == 0 ? 1 : 0; } +static int ebt_add_policy_rule(struct nftnl_chain *c, void *data) +{ + uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY); + struct iptables_command_state cs = { + .eb.bitmask = EBT_NOPROTO, + }; + struct nftnl_udata_buf *udata; + struct nft_handle *h = data; + struct nftnl_rule *r; + const char *pname; + + if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM)) + return 0; /* ignore base chains */ + + if (!nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) + return 0; + + nftnl_chain_unset(c, NFTNL_CHAIN_POLICY); + + switch (policy) { + case NFT_RETURN: + return 0; /* return policy is default for nft chains */ + case NF_ACCEPT: + pname = "ACCEPT"; + break; + case NF_DROP: + pname = "DROP"; + break; + default: + return -1; + } + + command_jump(&cs, pname); + + r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME), + nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs); + if (!r) + return -1; + + udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udata) + return -1; + + if (!nftnl_udata_put_u32(udata, UDATA_TYPE_EBTABLES_POLICY, 1)) + return -1; + + nftnl_rule_set_data(r, NFTNL_RULE_USERDATA, + nftnl_udata_buf_data(udata), + nftnl_udata_buf_len(udata)); + nftnl_udata_buf_free(udata); + + if (batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r) < 0) { + nftnl_rule_free(r); + return -1; + } + + return 0; +} + +int ebt_set_user_chain_policy(struct nft_handle *h, const char *table, + const char *chain, const char *policy) +{ + struct nftnl_chain *c = nft_chain_find(h, table, chain); + int pval; + + if (!c) + return 0; + + if (!strcmp(policy, "DROP")) + pval = NF_DROP; + else if (!strcmp(policy, "ACCEPT")) + pval = NF_ACCEPT; + else if (!strcmp(policy, "RETURN")) + pval = NFT_RETURN; + else + return 0; + + nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval); + return 1; +} + +static void nft_bridge_commit_prepare(struct nft_handle *h) +{ + const struct builtin_table *t; + struct nftnl_chain_list *list; + int i; + + for (i = 0; i < NFT_TABLE_MAX; i++) { + t = &h->tables[i]; + + if (!t->name) + continue; + + list = h->table[t->type].chain_cache; + if (!list) + continue; + + nftnl_chain_list_foreach(list, ebt_add_policy_rule, h); + } +} + int nft_commit(struct nft_handle *h) { + if (h->family == NFPROTO_BRIDGE) + nft_bridge_commit_prepare(h); return nft_action(h, NFT_COMPAT_COMMIT); } @@ -2681,6 +2884,27 @@ int nft_abort(struct nft_handle *h) return nft_action(h, NFT_COMPAT_ABORT); } +int nft_abort_policy_rule(struct nft_handle *h, const char *table) +{ + struct obj_update *n, *tmp; + + list_for_each_entry_safe(n, tmp, &h->obj_list, head) { + if (n->type != NFT_COMPAT_RULE_APPEND && + n->type != NFT_COMPAT_RULE_DELETE) + continue; + + if (strcmp(table, + nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE))) + continue; + + if (!nft_rule_is_policy_rule(n->rule)) + continue; + + batch_obj_del(h, n); + } + return 0; +} + int nft_compatible_revision(const char *name, uint8_t rev, int opt) { struct mnl_socket *nl; diff --git a/iptables/nft.h b/iptables/nft.h index 0726923a..56dc2076 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -137,6 +137,7 @@ uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag); */ int nft_commit(struct nft_handle *h); int nft_abort(struct nft_handle *h); +int nft_abort_policy_rule(struct nft_handle *h, const char *table); /* * revision compatibility. @@ -203,4 +204,7 @@ void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw); bool nft_is_table_compatible(struct nft_handle *h, const char *name); +int ebt_set_user_chain_policy(struct nft_handle *h, const char *table, + const char *chain, const char *policy); + #endif diff --git a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 index b23c1ee1..080ba49a 100755 --- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 +++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 @@ -50,6 +50,9 @@ $XT_MULTI ebtables -A foo --pkttype-type multicast --limit 100 -j ACCEPT $XT_MULTI ebtables -A FORWARD -j foo +$XT_MULTI ebtables -N bar +$XT_MULTI ebtables -P bar RETURN + $XT_MULTI ebtables -t nat -A PREROUTING --redirect-target ACCEPT #$XT_MULTI ebtables -t nat -A PREROUTING --to-src fe:ed:ba:be:00:01 @@ -59,6 +62,8 @@ $XT_MULTI ebtables -t nat -P OUTPUT DROP $XT_MULTI ebtables -t nat -A POSTROUTING -j ACCEPT #$XT_MULTI ebtables -t nat -A POSTROUTING --to-dst fe:ed:ba:be:00:01 --dnat-target ACCEPT +$XT_MULTI ebtables -t nat -N nat_foo -P DROP + # compare against stored ebtables dump DUMP='*filter @@ -66,6 +71,7 @@ DUMP='*filter :FORWARD DROP :OUTPUT ACCEPT :foo ACCEPT +:bar RETURN -A INPUT -p IPv4 -i lo -j ACCEPT -A FORWARD -j foo -A OUTPUT -s Broadcast -j DROP @@ -98,6 +104,7 @@ DUMP='*filter :PREROUTING ACCEPT :OUTPUT DROP :POSTROUTING ACCEPT +:nat_foo DROP -A PREROUTING -j redirect -A OUTPUT -j ACCEPT -A POSTROUTING -j ACCEPT diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c index 4d2e6f68..bc71e122 100644 --- a/iptables/xtables-eb.c +++ b/iptables/xtables-eb.c @@ -800,7 +800,6 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table, case 'E': /* Rename chain */ case 'X': /* Delete chain */ /* We allow -N chainname -P policy */ - /* XXX: Not in ebtables-compat */ if (command == 'N' && c == 'P') { command = c; optind--; /* No table specified */ @@ -1225,17 +1224,16 @@ print_zero: if (command == 'P') { if (selected_chain < 0) { - xtables_error(PARAMETER_PROBLEM, - "Policy %s not allowed for user defined chains", - policy); - } - if (strcmp(policy, "RETURN") == 0) { - xtables_error(PARAMETER_PROBLEM, - "Policy RETURN only allowed for user defined chains"); + ret = ebt_set_user_chain_policy(h, *table, chain, policy); + } else { + if (strcmp(policy, "RETURN") == 0) { + xtables_error(PARAMETER_PROBLEM, + "Policy RETURN only allowed for user defined chains"); + } + ret = nft_chain_set(h, *table, chain, policy, NULL); + if (ret < 0) + xtables_error(PARAMETER_PROBLEM, "Wrong policy"); } - ret = nft_chain_set(h, *table, chain, policy, NULL); - if (ret < 0) - xtables_error(PARAMETER_PROBLEM, "Wrong policy"); } else if (command == 'L') { ret = list_rules(h, chain, *table, rule_nr, 0, diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 4e00ed86..6e6daffc 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -226,14 +226,20 @@ void xtables_restore_parse(struct nft_handle *h, curtable->name, chain); } else if (cb->chain_user_add && cb->chain_user_add(h, chain, - curtable->name) < 0) { - if (errno == EEXIST) - continue; - + curtable->name) < 0 && + errno != EEXIST) { xtables_error(PARAMETER_PROBLEM, "cannot create chain " "'%s' (%s)\n", chain, strerror(errno)); + } else if (h->family == NFPROTO_BRIDGE && + !ebt_set_user_chain_policy(h, curtable->name, + chain, policy)) { + xtables_error(OTHER_PROBLEM, + "Can't set policy `%s'" + " on `%s' line %u: %s\n", + policy, chain, line, + ops->strerror(errno)); } ret = 1; } else if (in_table) { @@ -462,11 +468,18 @@ int xtables_ip6_restore_main(int argc, char *argv[]) argc, argv); } +static int ebt_table_flush(struct nft_handle *h, const char *table) +{ + /* drop any pending policy rule add/removal jobs */ + nft_abort_policy_rule(h, table); + return nft_table_flush(h, table); +} + struct nft_xt_restore_cb ebt_restore_cb = { .chain_list = get_chain_list, .commit = nft_commit, .table_new = nft_table_new, - .table_flush = nft_table_flush, + .table_flush = ebt_table_flush, .chain_user_flush = nft_chain_user_flush, .do_command = do_commandeb, .chain_set = nft_chain_set, |