diff options
Diffstat (limited to 'iptables/nft.c')
-rw-r--r-- | iptables/nft.c | 208 |
1 files changed, 168 insertions, 40 deletions
diff --git a/iptables/nft.c b/iptables/nft.c index 63468cf3..884cc77e 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -109,7 +109,9 @@ static struct nftnl_batch *mnl_batch_init(void) static void mnl_nft_batch_continue(struct nftnl_batch *batch) { - assert(nftnl_batch_update(batch) >= 0); + int ret = nftnl_batch_update(batch); + + assert(ret >= 0); } static uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t genid, uint32_t seqnum) @@ -335,6 +337,7 @@ static int mnl_append_error(const struct nft_handle *h, case NFT_COMPAT_RULE_REPLACE: case NFT_COMPAT_RULE_DELETE: case NFT_COMPAT_RULE_FLUSH: + case NFT_COMPAT_RULE_CHANGE_COUNTERS: snprintf(tcr, sizeof(tcr), "rule in chain %s", nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN)); #if 0 @@ -434,7 +437,7 @@ static void batch_chain_flush(struct nft_handle *h, } } -const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = { +static const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = { [NFT_TABLE_RAW] = { .name = "raw", .type = NFT_TABLE_RAW, @@ -571,7 +574,7 @@ const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = { #include <linux/netfilter_arp.h> -const struct builtin_table xtables_arp[NFT_TABLE_MAX] = { +static const struct builtin_table xtables_arp[NFT_TABLE_MAX] = { [NFT_TABLE_FILTER] = { .name = "filter", .type = NFT_TABLE_FILTER, @@ -594,7 +597,7 @@ const struct builtin_table xtables_arp[NFT_TABLE_MAX] = { #include <linux/netfilter_bridge.h> -const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = { +static const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = { [NFT_TABLE_FILTER] = { .name = "filter", .type = NFT_TABLE_FILTER, @@ -643,6 +646,19 @@ const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = { }, }, }, + [NFT_TABLE_BROUTE] = { + .name = "broute", + .type = NFT_TABLE_BROUTE, + .chains = { + { + .name = "BROUTING", + .type = "filter", + .prio = NF_BR_PRI_FIRST, + .hook = NF_BR_PRE_ROUTING, + }, + }, + }, + }; static int nft_table_builtin_add(struct nft_handle *h, @@ -686,6 +702,9 @@ nft_chain_builtin_alloc(int family, const char *tname, nftnl_chain_set_str(c, NFTNL_CHAIN_TYPE, chain->type); + nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0); + nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0); + return c; } @@ -946,6 +965,7 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h, int policy, const struct xt_counters *counters) { + static const struct xt_counters zero = {}; struct nftnl_chain *c; const struct builtin_table *_t; const struct builtin_chain *_c; @@ -970,12 +990,10 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h, return NULL; } - if (counters) { - nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, - counters->bcnt); - nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, - counters->pcnt); - } + if (!counters) + counters = &zero; + nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, counters->bcnt); + nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, counters->pcnt); return c; } @@ -1289,14 +1307,12 @@ static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r, uint8_t reg; int ret; - if (src[0] && src[0] == src[1] && + if (!invert_src && + src[0] && src[0] == src[1] && dst[0] && dst[0] == dst[1] && invert_src == invert_dst) { uint32_t combined = dst[0] | (src[0] << 16); - if (invert_src) - op = NFT_CMP_NEQ; - expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 4, ®); if (!expr) return -ENOMEM; @@ -1306,7 +1322,7 @@ static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r, return 0; } - if (src[0] || src[1] < 0xffff) { + if (src[0] || src[1] < UINT16_MAX || invert_src) { expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, ®); if (!expr) return -ENOMEM; @@ -1317,7 +1333,7 @@ static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r, return ret; } - if (dst[0] || dst[1] < 0xffff) { + if (dst[0] || dst[1] < UINT16_MAX || invert_dst) { expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, ®); if (!expr) return -ENOMEM; @@ -1449,22 +1465,30 @@ static int add_nft_mark(struct nft_handle *h, struct nftnl_rule *r, return 0; } -int add_match(struct nft_handle *h, +int add_match(struct nft_handle *h, struct nft_rule_ctx *ctx, struct nftnl_rule *r, struct xt_entry_match *m) { struct nftnl_expr *expr; int ret; - if (!strcmp(m->u.user.name, "limit")) - return add_nft_limit(r, m); - else if (!strcmp(m->u.user.name, "among")) - return add_nft_among(h, r, m); - else if (!strcmp(m->u.user.name, "udp")) - return add_nft_udp(h, r, m); - else if (!strcmp(m->u.user.name, "tcp")) - return add_nft_tcp(h, r, m); - else if (!strcmp(m->u.user.name, "mark")) - return add_nft_mark(h, r, m); + switch (ctx->command) { + case NFT_COMPAT_RULE_APPEND: + case NFT_COMPAT_RULE_INSERT: + case NFT_COMPAT_RULE_REPLACE: + if (!strcmp(m->u.user.name, "limit")) + return add_nft_limit(r, m); + else if (!strcmp(m->u.user.name, "among")) + return add_nft_among(h, r, m); + else if (!strcmp(m->u.user.name, "udp")) + return add_nft_udp(h, r, m); + else if (!strcmp(m->u.user.name, "tcp")) + return add_nft_tcp(h, r, m); + else if (!strcmp(m->u.user.name, "mark")) + return add_nft_mark(h, r, m); + break; + default: + break; + } expr = nftnl_expr_alloc("match"); if (expr == NULL) @@ -1692,7 +1716,8 @@ void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv) } struct nftnl_rule * -nft_rule_new(struct nft_handle *h, const char *chain, const char *table, +nft_rule_new(struct nft_handle *h, struct nft_rule_ctx *ctx, + const char *chain, const char *table, struct iptables_command_state *cs) { struct nftnl_rule *r; @@ -1705,7 +1730,7 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table, nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); - if (h->ops->add(h, r, cs) < 0) + if (h->ops->add(h, ctx, r, cs) < 0) goto err; return r; @@ -2343,7 +2368,8 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r) nftnl_rule_list_del(r); - if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE)) + if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE) && + !nftnl_rule_get_u32(r, NFTNL_RULE_ID)) nftnl_rule_set_u32(r, NFTNL_RULE_ID, ++h->rule_id); obj = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r); @@ -2614,6 +2640,58 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, return ret; } +static int nft_rule_change_counters(struct nft_handle *h, const char *table, + const char *chain, struct nftnl_rule *rule, + int rulenum, struct xt_counters *counters, + uint8_t counter_op, bool verbose) +{ + struct iptables_command_state cs = {}; + struct nftnl_rule *r, *new_rule; + struct nft_rule_ctx ctx = { + .command = NFT_COMPAT_RULE_APPEND, + }; + struct nft_chain *c; + + nft_fn = nft_rule_change_counters; + + c = nft_chain_find(h, table, chain); + if (!c) { + errno = ENOENT; + return 0; + } + + r = nft_rule_find(h, c, rule, rulenum); + if (!r) { + errno = E2BIG; + return 0; + } + + DEBUGP("changing counters of rule with handle=%llu\n", + (unsigned long long) + nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE)); + + h->ops->rule_to_cs(h, r, &cs); + + if (counter_op & CTR_OP_INC_PKTS) + cs.counters.pcnt += counters->pcnt; + else if (counter_op & CTR_OP_DEC_PKTS) + cs.counters.pcnt -= counters->pcnt; + else + cs.counters.pcnt = counters->pcnt; + + if (counter_op & CTR_OP_INC_BYTES) + cs.counters.bcnt += counters->bcnt; + else if (counter_op & CTR_OP_DEC_BYTES) + cs.counters.bcnt -= counters->bcnt; + else + cs.counters.bcnt = counters->bcnt; + + new_rule = nft_rule_new(h, &ctx, chain, table, &cs); + h->ops->clear_cs(&cs); + + return nft_rule_append(h, chain, table, new_rule, r, verbose); +} + static int __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c, int rulenum, unsigned int format, @@ -2732,8 +2810,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (chain) { c = nft_chain_find(h, table, chain); - if (!c) + if (!c) { + errno = ENOENT; return 0; + } if (rulenum) d.save_fmt = true; /* skip header printing */ @@ -2840,8 +2920,10 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, if (chain) { c = nft_chain_find(h, table, chain); - if (!c) + if (!c) { + errno = ENOENT; return 0; + } if (!rulenum) nft_rule_list_chain_save(c, &counters); @@ -2864,14 +2946,19 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain, { struct iptables_command_state cs = {}; struct nftnl_rule *r, *new_rule; + struct nft_rule_ctx ctx = { + .command = NFT_COMPAT_RULE_APPEND, + }; struct nft_chain *c; int ret = 0; nft_fn = nft_rule_delete; c = nft_chain_find(h, table, chain); - if (!c) + if (!c) { + errno = ENOENT; return 0; + } r = nft_rule_find(h, c, NULL, rulenum); if (r == NULL) { @@ -2882,7 +2969,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain, h->ops->rule_to_cs(h, r, &cs); cs.counters.pcnt = cs.counters.bcnt = 0; - new_rule = nft_rule_new(h, chain, table, &cs); + new_rule = nft_rule_new(h, &ctx, chain, table, &cs); h->ops->clear_cs(&cs); if (!new_rule) @@ -2949,6 +3036,12 @@ static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type, break; } nftnl_set_elems_iter_destroy(iter); + + if (h->verbose > 1) { + fprintf(stdout, "set "); + nftnl_set_fprintf(stdout, set, 0, 0); + fprintf(stdout, "\n"); + } } static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type, @@ -2995,6 +3088,7 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o) case NFT_COMPAT_RULE_APPEND: case NFT_COMPAT_RULE_INSERT: case NFT_COMPAT_RULE_REPLACE: + case NFT_COMPAT_RULE_CHANGE_COUNTERS: break; case NFT_COMPAT_RULE_DELETE: case NFT_COMPAT_RULE_FLUSH: @@ -3082,6 +3176,7 @@ static void nft_refresh_transaction(struct nft_handle *h) case NFT_COMPAT_RULE_APPEND: case NFT_COMPAT_RULE_INSERT: case NFT_COMPAT_RULE_REPLACE: + case NFT_COMPAT_RULE_CHANGE_COUNTERS: case NFT_COMPAT_RULE_DELETE: case NFT_COMPAT_SET_ADD: case NFT_COMPAT_RULE_LIST: @@ -3172,6 +3267,7 @@ retry: n->rule); break; case NFT_COMPAT_RULE_REPLACE: + case NFT_COMPAT_RULE_CHANGE_COUNTERS: nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE, NLM_F_CREATE | NLM_F_REPLACE, n->seq, n->rule); @@ -3195,6 +3291,7 @@ retry: case NFT_COMPAT_RULE_ZERO: case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE: assert(0); + return 0; } mnl_nft_batch_continue(h->batch); @@ -3260,6 +3357,9 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data) .eb.bitmask = EBT_NOPROTO, }; struct nftnl_udata_buf *udata; + struct nft_rule_ctx ctx = { + .command = NFT_COMPAT_RULE_APPEND, + }; struct nft_handle *h = data; struct nftnl_rule *r; const char *pname; @@ -3287,7 +3387,7 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data) command_jump(&cs, pname); - r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME), + r = nft_rule_new(h, &ctx, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME), nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs); ebt_cs_clean(&cs); @@ -3469,6 +3569,15 @@ static int nft_prepare(struct nft_handle *h) case NFT_COMPAT_TABLE_ADD: case NFT_COMPAT_CHAIN_ADD: assert(0); + return 0; + case NFT_COMPAT_RULE_CHANGE_COUNTERS: + ret = nft_rule_change_counters(h, cmd->table, + cmd->chain, + cmd->obj.rule, + cmd->rulenum, + &cmd->counters, + cmd->counter_op, + cmd->verbose); break; } @@ -3639,6 +3748,27 @@ const char *nft_strerror(int err) return strerror(err); } +static int l4proto_expr_get_dreg(struct nftnl_expr *e, uint32_t *dregp) +{ + const char *name = nftnl_expr_get_str(e, NFTNL_EXPR_NAME); + uint32_t poff = offsetof(struct iphdr, protocol); + uint32_t pbase = NFT_PAYLOAD_NETWORK_HEADER; + + if (!strcmp(name, "payload") && + nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE) == pbase && + nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET) == poff && + nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN) == sizeof(uint8_t)) { + *dregp = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG); + return 0; + } + if (!strcmp(name, "meta") && + nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) == NFT_META_L4PROTO) { + *dregp = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG); + return 0; + } + return -1; +} + static int recover_rule_compat(struct nftnl_rule *r) { struct nftnl_expr_iter *iter; @@ -3655,12 +3785,10 @@ next_expr: if (!e) goto out; - if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) || - nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO) + /* may be 'ip protocol' or 'meta l4proto' with identical RHS */ + if (l4proto_expr_get_dreg(e, ®) < 0) goto next_expr; - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG); - e = nftnl_expr_iter_next(iter); if (!e) goto out; @@ -3846,7 +3974,7 @@ bool nft_is_table_compatible(struct nft_handle *h, if (chain) { struct nft_chain *c = nft_chain_find(h, table, chain); - return c && !nft_is_chain_compatible(c, h); + return !c || !nft_is_chain_compatible(c, h); } return !nft_chain_foreach(h, table, nft_is_chain_compatible, h); |