Instead of assuming only one register is used, track all 16 regs individually. This avoids need for the 'PREV_PAYLOAD' hack and also avoids the need to clear out old flags: When we see that register 'x' will be written to, that register state is reset automatically. Existing dissector decodes ip saddr 1.2.3.4 meta l4proto tcp ... as -s 6.0.0.0 -p tcp iptables-nft -s 1.2.3.4 -p tcp is decoded correctly because the expressions are ordered like: meta l4proto tcp ip saddr 1.2.3.4 | ... and 'meta l4proto' did clear the PAYLOAD flag. The simpler fix is: ctx->flags &= ~NFT_XT_CTX_PAYLOAD; in nft_parse_cmp(), but that breaks dissection of '1-42', because the second compare ('cmp lte 42') will not find the payload expression anymore. Link: https://lore.kernel.org/netfilter-devel/20220922143544.GA22541@xxxxxxxxxxxxx/T/#t Signed-off-by: Florian Westphal <fw@xxxxxxxxx> --- iptables/nft-arp.c | 57 ++++++------ iptables/nft-bridge.c | 102 +++++++++++++-------- iptables/nft-ipv4.c | 49 +++++----- iptables/nft-ipv6.c | 36 ++++---- iptables/nft-shared.c | 205 +++++++++++++++++++++++++++++------------- iptables/nft-shared.h | 110 +++++++++++++++++------ 6 files changed, 360 insertions(+), 199 deletions(-) diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index e6e4d2d81e52..e9e111416d79 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -160,25 +160,27 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, return ret; } -static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, +static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, + struct nftnl_expr *e, struct iptables_command_state *cs) { struct arpt_entry *fw = &cs->arp; uint8_t flags = 0; - parse_meta(ctx, e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask, + parse_meta(ctx, e, reg->meta_dreg.key, fw->arp.iniface, fw->arp.iniface_mask, fw->arp.outiface, fw->arp.outiface_mask, &flags); fw->arp.invflags |= flags; } -static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask) +static void parse_mask_ipv4(const struct nft_xt_ctx_reg *reg, struct in_addr *mask) { - mask->s_addr = ctx->bitwise.mask[0]; + mask->s_addr = reg->bitwise.mask[0]; } -static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx, +static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e, struct arpt_devaddr_info *info) { @@ -192,18 +194,17 @@ static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx, get_cmp_data(e, info->addr, ETH_ALEN, &inv); - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(info->mask, ctx->bitwise.mask, ETH_ALEN); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + memcpy(info->mask, reg->bitwise.mask, ETH_ALEN); + else memset(info->mask, 0xff, - min(ctx->payload.len, ETH_ALEN)); - } + min(reg->payload.len, ETH_ALEN)); return inv; } static void nft_arp_parse_payload(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e, struct iptables_command_state *cs) { @@ -213,7 +214,7 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx, uint8_t ar_hln; bool inv; - switch (ctx->payload.offset) { + switch (reg->payload.offset) { case offsetof(struct arphdr, ar_hrd): get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv); fw->arp.arhrd = ar_hrd; @@ -243,43 +244,39 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx, fw->arp.invflags |= IPT_INV_ARPOP; break; default: - if (ctx->payload.offset == sizeof(struct arphdr)) { - if (nft_arp_parse_devaddr(ctx, e, &fw->arp.src_devaddr)) + if (reg->payload.offset == sizeof(struct arphdr)) { + if (nft_arp_parse_devaddr(reg, e, &fw->arp.src_devaddr)) fw->arp.invflags |= IPT_INV_SRCDEVADDR; - } else if (ctx->payload.offset == sizeof(struct arphdr) + + } else if (reg->payload.offset == sizeof(struct arphdr) + fw->arp.arhln) { get_cmp_data(e, &addr, sizeof(addr), &inv); fw->arp.src.s_addr = addr.s_addr; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv4(ctx, &fw->arp.smsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + parse_mask_ipv4(reg, &fw->arp.smsk); + else memset(&fw->arp.smsk, 0xff, - min(ctx->payload.len, + min(reg->payload.len, sizeof(struct in_addr))); - } if (inv) fw->arp.invflags |= IPT_INV_SRCIP; - } else if (ctx->payload.offset == sizeof(struct arphdr) + + } else if (reg->payload.offset == sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr)) { - if (nft_arp_parse_devaddr(ctx, e, &fw->arp.tgt_devaddr)) + if (nft_arp_parse_devaddr(reg, e, &fw->arp.tgt_devaddr)) fw->arp.invflags |= IPT_INV_TGTDEVADDR; - } else if (ctx->payload.offset == sizeof(struct arphdr) + + } else if (reg->payload.offset == sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln) { get_cmp_data(e, &addr, sizeof(addr), &inv); fw->arp.tgt.s_addr = addr.s_addr; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv4(ctx, &fw->arp.tmsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + parse_mask_ipv4(reg, &fw->arp.tmsk); + else memset(&fw->arp.tmsk, 0xff, - min(ctx->payload.len, + min(reg->payload.len, sizeof(struct in_addr))); - } if (inv) fw->arp.invflags |= IPT_INV_DSTIP; diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c index 106bcc72889f..1121e8644a4e 100644 --- a/iptables/nft-bridge.c +++ b/iptables/nft-bridge.c @@ -170,6 +170,7 @@ static int nft_bridge_add(struct nft_handle *h, } static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e, struct iptables_command_state *cs) { @@ -177,9 +178,9 @@ static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx, uint8_t invflags = 0; char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {}; - parse_meta(ctx, e, ctx->meta.key, iifname, NULL, oifname, NULL, &invflags); + parse_meta(ctx, e, reg->meta_dreg.key, iifname, NULL, oifname, NULL, &invflags); - switch (ctx->meta.key) { + switch (reg->meta_dreg.key) { case NFT_META_BRI_IIFNAME: if (invflags & IPT_INV_VIA_IN) cs->eb.invflags |= EBT_ILOGICALIN; @@ -206,6 +207,7 @@ static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx, } static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e, struct iptables_command_state *cs) { @@ -215,7 +217,7 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, bool inv; int i; - switch (ctx->payload.offset) { + switch (reg->payload.offset) { case offsetof(struct ethhdr, h_dest): get_cmp_data(e, addr, sizeof(addr), &inv); for (i = 0; i < ETH_ALEN; i++) @@ -223,13 +225,11 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, if (inv) fw->invflags |= EBT_IDEST; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(fw->destmsk, ctx->bitwise.mask, ETH_ALEN); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + memcpy(fw->destmsk, reg->bitwise.mask, ETH_ALEN); + else memset(&fw->destmsk, 0xff, - min(ctx->payload.len, ETH_ALEN)); - } + min(reg->payload.len, ETH_ALEN)); fw->bitmask |= EBT_IDEST; break; case offsetof(struct ethhdr, h_source): @@ -238,13 +238,11 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, fw->sourcemac[i] = addr[i]; if (inv) fw->invflags |= EBT_ISOURCE; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(fw->sourcemsk, ctx->bitwise.mask, ETH_ALEN); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + memcpy(fw->sourcemsk, reg->bitwise.mask, ETH_ALEN); + else memset(&fw->sourcemsk, 0xff, - min(ctx->payload.len, ETH_ALEN)); - } + min(reg->payload.len, ETH_ALEN)); fw->bitmask |= EBT_ISOURCE; break; case offsetof(struct ethhdr, h_proto): @@ -294,28 +292,53 @@ lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len) /* Make sure previous payload expression(s) is/are consistent and extract if * matching on source or destination address and if matching on MAC and IP or * only MAC address. */ -static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx, +static int lookup_analyze_payloads(struct nft_xt_ctx *ctx, + enum nft_registers sreg, + uint32_t key_len, bool *dst, bool *ip) { + const struct nft_xt_ctx_reg *reg; + uint32_t sreg_count; int val, val2 = -1; - if (ctx->flags & NFT_XT_CTX_PREV_PAYLOAD) { - val = lookup_check_ether_payload(ctx->prev_payload.base, - ctx->prev_payload.offset, - ctx->prev_payload.len); + reg = nft_xt_ctx_get_sreg(ctx, sreg); + if (!reg) + return -1; + + if (reg->type != NFT_XT_REG_PAYLOAD) { + ctx->errmsg = "lookup reg is not payload type"; + return -1; + } + + sreg_count = sreg; + switch (key_len) { + case 12: /* ether + ipv4addr */ + val = lookup_check_ether_payload(reg->payload.base, + reg->payload.offset, + reg->payload.len); if (val < 0) { DEBUGP("unknown payload base/offset/len %d/%d/%d\n", - ctx->prev_payload.base, ctx->prev_payload.offset, - ctx->prev_payload.len); + reg->payload.base, reg->payload.offset, + reg->payload.len); return -1; } - if (!(ctx->flags & NFT_XT_CTX_PAYLOAD)) { - DEBUGP("Previous but no current payload?\n"); + + sreg_count += 2; + + reg = nft_xt_ctx_get_sreg(ctx, sreg_count); + if (!reg) { + ctx->errmsg = "next lookup register is invalid"; + return -1; + } + + if (reg->type != NFT_XT_REG_PAYLOAD) { + ctx->errmsg = "next lookup reg is not payload type"; return -1; } - val2 = lookup_check_iphdr_payload(ctx->payload.base, - ctx->payload.offset, - ctx->payload.len); + + val2 = lookup_check_iphdr_payload(reg->payload.base, + reg->payload.offset, + reg->payload.len); if (val2 < 0) { DEBUGP("unknown payload base/offset/len %d/%d/%d\n", ctx->payload.base, ctx->payload.offset, @@ -325,18 +348,20 @@ static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx, DEBUGP("mismatching payload match offsets\n"); return -1; } - } else if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - val = lookup_check_ether_payload(ctx->payload.base, - ctx->payload.offset, - ctx->payload.len); + break; + case 4: /* ipv4addr */ + val = lookup_check_ether_payload(reg->payload.base, + reg->payload.offset, + reg->payload.len); if (val < 0) { DEBUGP("unknown payload base/offset/len %d/%d/%d\n", ctx->payload.base, ctx->payload.offset, ctx->payload.len); return -1; } - } else { - DEBUGP("unknown LHS of lookup expression\n"); + break; + default: + ctx->errmsg = "unsupported lookup key length"; return -1; } @@ -413,14 +438,17 @@ static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx, size_t poff, size; uint32_t cnt; - if (lookup_analyze_payloads(ctx, &is_dst, &have_ip)) - return; - s = set_from_lookup_expr(ctx, e); if (!s) xtables_error(OTHER_PROBLEM, "BUG: lookup expression references unknown set"); + if (lookup_analyze_payloads(ctx, + nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SREG), + nftnl_set_get_u32(s, NFTNL_SET_KEY_LEN), + &is_dst, &have_ip)) + return; + cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE); for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) { @@ -468,8 +496,6 @@ static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx, if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt)) xtables_error(OTHER_PROBLEM, "ebtables among pair parsing failed"); - - ctx->flags &= ~(NFT_XT_CTX_PAYLOAD | NFT_XT_CTX_PREV_PAYLOAD); } static void parse_watcher(void *object, struct ebt_match **match_list, diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index 1865d1515296..92a914f1a4a4 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -115,28 +115,28 @@ static bool nft_ipv4_is_same(const struct iptables_command_state *a, b->fw.ip.iniface_mask, b->fw.ip.outiface_mask); } -static void get_frag(struct nft_xt_ctx *ctx, struct nftnl_expr *e, bool *inv) +static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e) { uint8_t op; /* we assume correct mask and xor */ - if (!(ctx->flags & NFT_XT_CTX_BITWISE)) - return; + if (!reg->bitwise.set) + return false; /* we assume correct data */ op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); if (op == NFT_CMP_EQ) - *inv = true; - else - *inv = false; + return true; - ctx->flags &= ~NFT_XT_CTX_BITWISE; + return false; } -static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, +static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, + struct nftnl_expr *e, struct iptables_command_state *cs) { - switch (ctx->meta.key) { + switch (reg->meta_dreg.key) { case NFT_META_L4PROTO: cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA); if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) @@ -146,17 +146,18 @@ static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, break; } - parse_meta(ctx, e, ctx->meta.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, + parse_meta(ctx, e, reg->meta_dreg.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, cs->fw.ip.outiface, cs->fw.ip.outiface_mask, &cs->fw.ip.invflags); } -static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask) +static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask) { - mask->s_addr = ctx->bitwise.mask[0]; + mask->s_addr = sreg->bitwise.mask[0]; } static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *sreg, struct nftnl_expr *e, struct iptables_command_state *cs) { @@ -164,16 +165,15 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, uint8_t proto; bool inv; - switch(ctx->payload.offset) { + switch (sreg->payload.offset) { case offsetof(struct iphdr, saddr): get_cmp_data(e, &addr, sizeof(addr), &inv); cs->fw.ip.src.s_addr = addr.s_addr; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv4(ctx, &cs->fw.ip.smsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; + if (sreg->bitwise.set) { + parse_mask_ipv4(sreg, &cs->fw.ip.smsk); } else { memset(&cs->fw.ip.smsk, 0xff, - min(ctx->payload.len, sizeof(struct in_addr))); + min(sreg->payload.len, sizeof(struct in_addr))); } if (inv) @@ -182,13 +182,11 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, case offsetof(struct iphdr, daddr): get_cmp_data(e, &addr, sizeof(addr), &inv); cs->fw.ip.dst.s_addr = addr.s_addr; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv4(ctx, &cs->fw.ip.dmsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (sreg->bitwise.set) + parse_mask_ipv4(sreg, &cs->fw.ip.dmsk); + else memset(&cs->fw.ip.dmsk, 0xff, - min(ctx->payload.len, sizeof(struct in_addr))); - } + min(sreg->payload.len, sizeof(struct in_addr))); if (inv) cs->fw.ip.invflags |= IPT_INV_DSTIP; @@ -201,8 +199,7 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, break; case offsetof(struct iphdr, frag_off): cs->fw.ip.flags |= IPT_F_FRAG; - inv = false; - get_frag(ctx, e, &inv); + inv = get_frag(sreg, e); if (inv) cs->fw.ip.invflags |= IPT_INV_FRAG; break; @@ -210,7 +207,7 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, nft_parse_hl(ctx, e, cs); break; default: - DEBUGP("unknown payload offset %d\n", ctx->payload.offset); + DEBUGP("unknown payload offset %d\n", sreg->payload.offset); break; } } diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c index 0ab1f9719344..05d65fbb4624 100644 --- a/iptables/nft-ipv6.c +++ b/iptables/nft-ipv6.c @@ -104,10 +104,12 @@ static bool nft_ipv6_is_same(const struct iptables_command_state *a, b->fw6.ipv6.outiface_mask); } -static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, +static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, + struct nftnl_expr *e, struct iptables_command_state *cs) { - switch (ctx->meta.key) { + switch (reg->meta_dreg.key) { case NFT_META_L4PROTO: cs->fw6.ipv6.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA); if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) @@ -117,17 +119,19 @@ static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, break; } - parse_meta(ctx, e, ctx->meta.key, cs->fw6.ipv6.iniface, + parse_meta(ctx, e, reg->meta_dreg.key, cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface, cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags); } -static void parse_mask_ipv6(struct nft_xt_ctx *ctx, struct in6_addr *mask) +static void parse_mask_ipv6(const struct nft_xt_ctx_reg *reg, + struct in6_addr *mask) { - memcpy(mask, ctx->bitwise.mask, sizeof(struct in6_addr)); + memcpy(mask, reg->bitwise.mask, sizeof(struct in6_addr)); } static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e, struct iptables_command_state *cs) { @@ -135,17 +139,15 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx, uint8_t proto; bool inv; - switch (ctx->payload.offset) { + switch (reg->payload.offset) { case offsetof(struct ip6_hdr, ip6_src): get_cmp_data(e, &addr, sizeof(addr), &inv); memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr)); - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv6(ctx, &cs->fw6.ipv6.smsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + parse_mask_ipv6(reg, &cs->fw6.ipv6.smsk); + else memset(&cs->fw6.ipv6.smsk, 0xff, - min(ctx->payload.len, sizeof(struct in6_addr))); - } + min(reg->payload.len, sizeof(struct in6_addr))); if (inv) cs->fw6.ipv6.invflags |= IP6T_INV_SRCIP; @@ -153,13 +155,11 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx, case offsetof(struct ip6_hdr, ip6_dst): get_cmp_data(e, &addr, sizeof(addr), &inv); memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr)); - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv6(ctx, &cs->fw6.ipv6.dmsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { + if (reg->bitwise.set) + parse_mask_ipv6(reg, &cs->fw6.ipv6.dmsk); + else memset(&cs->fw6.ipv6.dmsk, 0xff, - min(ctx->payload.len, sizeof(struct in6_addr))); - } + min(reg->payload.len, sizeof(struct in6_addr))); if (inv) cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP; diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 71e2f18dab92..555f74f03530 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -299,6 +299,16 @@ nft_create_match(struct nft_xt_ctx *ctx, struct iptables_command_state *cs, const char *name); +static uint32_t get_meta_mask(struct nft_xt_ctx *ctx, enum nft_registers sreg) +{ + struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_sreg(ctx, sreg); + + if (reg->bitwise.set) + return reg->bitwise.mask[0]; + + return ~0u; +} + static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e) { struct xt_mark_mtinfo1 *mark; @@ -316,12 +326,7 @@ static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e) value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA); mark->mark = value; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(&mark->mask, &ctx->bitwise.mask, sizeof(mark->mask)); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { - mark->mask = 0xffffffff; - } + mark->mask = get_meta_mask(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG)); return 0; } @@ -479,20 +484,40 @@ void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv) *inv = false; } -static void nft_meta_set_to_target(struct nft_xt_ctx *ctx) +static void nft_meta_set_to_target(struct nft_xt_ctx *ctx, + struct nftnl_expr *e) { struct xtables_target *target; + struct nft_xt_ctx_reg *sreg; + enum nft_registers sregnum; struct xt_entry_target *t; unsigned int size; const char *targname; - switch (ctx->meta.key) { + sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG); + sreg = nft_xt_ctx_get_sreg(ctx, sregnum); + if (!sreg) + return; + + if (sreg->meta_sreg.set == 0) + return; + + switch (sreg->meta_sreg.key) { case NFT_META_NFTRACE: - if (ctx->immediate.data[0] == 0) + if ((sreg->type != NFT_XT_REG_IMMEDIATE)) { + ctx->errmsg = "meta nftrace but reg not immediate"; return; + } + + if (sreg->immediate.data[0] == 0) { + ctx->errmsg = "trace is cleared"; + return; + } + targname = "TRACE"; break; default: + ctx->errmsg = "meta sreg key not supported"; return; } @@ -514,51 +539,74 @@ static void nft_meta_set_to_target(struct nft_xt_ctx *ctx) static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e) { - ctx->meta.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY); + struct nft_xt_ctx_reg *reg; - if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG) && - (ctx->flags & NFT_XT_CTX_IMMEDIATE) && - nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG) == ctx->immediate.reg) { - ctx->flags &= ~NFT_XT_CTX_IMMEDIATE; - nft_meta_set_to_target(ctx); + if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) { + nft_meta_set_to_target(ctx, e); return; } - ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG); - ctx->flags |= NFT_XT_CTX_META; + reg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG)); + if (!reg) + return; + + reg->meta_dreg.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY); + reg->type = NFT_XT_REG_META_DREG; } static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e) { - if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - memcpy(&ctx->prev_payload, &ctx->payload, - sizeof(ctx->prev_payload)); - ctx->flags |= NFT_XT_CTX_PREV_PAYLOAD; - } + enum nft_registers regnum = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG); + struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_dreg(ctx, regnum); - ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG); - ctx->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE); - ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET); - ctx->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN); - ctx->flags |= NFT_XT_CTX_PAYLOAD; + if (!reg) + return; + + reg->type = NFT_XT_REG_PAYLOAD; + reg->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE); + reg->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET); + reg->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN); } static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e) { - uint32_t reg, len; + enum nft_registers sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG); + enum nft_registers dregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG); + struct nft_xt_ctx_reg *sreg = nft_xt_ctx_get_sreg(ctx, sregnum); + struct nft_xt_ctx_reg *dreg = sreg; const void *data; + uint32_t len; - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG); - if (ctx->reg && reg != ctx->reg) + if (!sreg) return; - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG); - ctx->reg = reg; + if (sregnum != dregnum) { + dreg = nft_xt_ctx_get_sreg(ctx, dregnum); /* sreg, do NOT clear ... */ + if (!dreg) + return; + + *dreg = *sreg; /* .. and copy content instead */ + } + data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len); - memcpy(ctx->bitwise.xor, data, len); + + if (len > sizeof(dreg->bitwise.xor)) { + ctx->errmsg = "bitwise xor too large"; + return; + } + + memcpy(dreg->bitwise.xor, data, len); + data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len); - memcpy(ctx->bitwise.mask, data, len); - ctx->flags |= NFT_XT_CTX_BITWISE; + + if (len > sizeof(dreg->bitwise.mask)) { + ctx->errmsg = "bitwise mask too large"; + return; + } + + memcpy(dreg->bitwise.mask, data, len); + + dreg->bitwise.set = true; } static struct xtables_match * @@ -863,6 +911,8 @@ static void nft_parse_transport(struct nft_xt_ctx *ctx, struct nftnl_expr *e, struct iptables_command_state *cs) { + struct nft_xt_ctx_reg *sreg; + enum nft_registers reg; uint32_t sdport; uint16_t port; uint8_t proto, op; @@ -883,7 +933,17 @@ static void nft_parse_transport(struct nft_xt_ctx *ctx, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len); op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); - switch(ctx->payload.offset) { + reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG); + sreg = nft_xt_ctx_get_sreg(ctx, reg); + if (!sreg) + return; + + if (sreg->type != NFT_XT_REG_PAYLOAD) { + ctx->errmsg = "sgreg not payload"; + return; + } + + switch(sreg->payload.offset) { case 0: /* th->sport */ switch (len) { case 2: /* load sport only */ @@ -909,10 +969,9 @@ static void nft_parse_transport(struct nft_xt_ctx *ctx, uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA); uint8_t mask = ~0; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(&mask, &ctx->bitwise.mask, sizeof(mask)); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } + if (sreg->bitwise.set) + memcpy(&mask, &sreg->bitwise.mask, sizeof(mask)); + nft_parse_tcp_flags(ctx, cs, op, flags, mask); } return; @@ -920,6 +979,7 @@ static void nft_parse_transport(struct nft_xt_ctx *ctx, } static void nft_parse_transport_range(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *sreg, struct nftnl_expr *e, struct iptables_command_state *cs) { @@ -949,7 +1009,7 @@ static void nft_parse_transport_range(struct nft_xt_ctx *ctx, from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA)); to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA)); - switch(ctx->payload.offset) { + switch (sreg->payload.offset) { case 0: nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op); return; @@ -962,30 +1022,40 @@ static void nft_parse_transport_range(struct nft_xt_ctx *ctx, static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e) { + struct nft_xt_ctx_reg *sreg; uint32_t reg; reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG); - if (ctx->reg && reg != ctx->reg) + + sreg = nft_xt_ctx_get_sreg(ctx, reg); + if (!sreg) return; - if (ctx->flags & NFT_XT_CTX_META) { - ctx->h->ops->parse_meta(ctx, e, ctx->cs); - ctx->flags &= ~NFT_XT_CTX_META; - } - /* bitwise context is interpreted from payload */ - if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - switch (ctx->payload.base) { + switch (sreg->type) { + case NFT_XT_REG_UNDEF: + ctx->errmsg = "cmp sreg undef"; + break; + case NFT_XT_REG_META_DREG: + ctx->h->ops->parse_meta(ctx, sreg, e, ctx->cs); + break; + case NFT_XT_REG_PAYLOAD: + switch (sreg->payload.base) { case NFT_PAYLOAD_LL_HEADER: if (ctx->h->family == NFPROTO_BRIDGE) - ctx->h->ops->parse_payload(ctx, e, ctx->cs); + ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs); break; case NFT_PAYLOAD_NETWORK_HEADER: - ctx->h->ops->parse_payload(ctx, e, ctx->cs); + ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs); break; case NFT_PAYLOAD_TRANSPORT_HEADER: nft_parse_transport(ctx, e, ctx->cs); break; } + + break; + default: + ctx->errmsg = "cmp sreg has unknown type"; + break; } } @@ -1004,18 +1074,22 @@ static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e) int verdict; if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) { + struct nft_xt_ctx_reg *dreg; const void *imm_data; uint32_t len; imm_data = nftnl_expr_get_data(e, NFTNL_EXPR_IMM_DATA, &len); + dreg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG)); + if (!dreg) + return; - if (len > sizeof(ctx->immediate.data)) + if (len > sizeof(dreg->immediate.data)) return; - memcpy(ctx->immediate.data, imm_data, len); - ctx->immediate.len = len; - ctx->immediate.reg = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG); - ctx->flags |= NFT_XT_CTX_IMMEDIATE; + memcpy(dreg->immediate.data, imm_data, len); + dreg->immediate.len = len; + dreg->type = NFT_XT_REG_IMMEDIATE; + return; } @@ -1152,20 +1226,29 @@ static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h, static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e) { + struct nft_xt_ctx_reg *sreg; uint32_t reg; reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG); - if (reg != ctx->reg) - return; + sreg = nft_xt_ctx_get_sreg(ctx, reg); - if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - switch (ctx->payload.base) { + switch (sreg->type) { + case NFT_XT_REG_UNDEF: + ctx->errmsg = "range sreg undef"; + break; + case NFT_XT_REG_PAYLOAD: + switch (sreg->payload.base) { case NFT_PAYLOAD_TRANSPORT_HEADER: - nft_parse_transport_range(ctx, e, ctx->cs); + nft_parse_transport_range(ctx, sreg, e, ctx->cs); break; default: + ctx->errmsg = "range with unknown payload base"; break; } + break; + default: + ctx->errmsg = "range sreg type unsupported"; + break; } } diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h index 0718dc23e8b7..421706389e55 100644 --- a/iptables/nft-shared.h +++ b/iptables/nft-shared.h @@ -38,13 +38,41 @@ struct xtables_args; struct nft_handle; struct xt_xlate; -enum { - NFT_XT_CTX_PAYLOAD = (1 << 0), - NFT_XT_CTX_META = (1 << 1), - NFT_XT_CTX_BITWISE = (1 << 2), - NFT_XT_CTX_IMMEDIATE = (1 << 3), - NFT_XT_CTX_PREV_PAYLOAD = (1 << 4), - NFT_XT_CTX_RANGE = (1 << 5), +enum nft_ctx_reg_type { + NFT_XT_REG_UNDEF, + NFT_XT_REG_PAYLOAD, + NFT_XT_REG_IMMEDIATE, + NFT_XT_REG_META_DREG, +}; + +struct nft_xt_ctx_reg { + enum nft_ctx_reg_type type:8; + + union { + struct { + uint32_t base; + uint32_t offset; + uint32_t len; + } payload; + struct { + uint32_t data[4]; + uint8_t len; + } immediate; + struct { + uint32_t key; + } meta_dreg; + }; + + struct { + uint32_t mask[4]; + uint32_t xor[4]; + bool set; + } bitwise; + + struct { + uint32_t key; + bool set; + } meta_sreg; }; struct nft_xt_ctx { @@ -58,25 +86,51 @@ struct nft_xt_ctx { struct xt_udp *udp; } tcpudp; - uint32_t reg; - struct { - uint32_t base; - uint32_t offset; - uint32_t len; - } payload, prev_payload; - struct { - uint32_t key; - } meta; - struct { - uint32_t data[4]; - uint32_t len, reg; - } immediate; - struct { - uint32_t mask[4]; - uint32_t xor[4]; - } bitwise; + struct nft_xt_ctx_reg regs[1 + 16]; + + const char *errmsg; }; +static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_sreg(struct nft_xt_ctx *ctx, enum nft_registers reg) +{ + switch (reg) { + case NFT_REG_VERDICT: + return &ctx->regs[0]; + case NFT_REG_1: + return &ctx->regs[1]; + case NFT_REG_2: + return &ctx->regs[5]; + case NFT_REG_3: + return &ctx->regs[9]; + case NFT_REG_4: + return &ctx->regs[13]; + case NFT_REG32_00...NFT_REG32_15: + return &ctx->regs[reg - NFT_REG32_00]; + default: + ctx->errmsg = "Unknown register requested"; + break; + } + + return NULL; +} + +static inline void nft_xt_reg_clear(struct nft_xt_ctx_reg *r) +{ + r->type = 0; + r->bitwise.set = false; + r->meta_sreg.set = false; +} + +static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_dreg(struct nft_xt_ctx *ctx, enum nft_registers reg) +{ + struct nft_xt_ctx_reg *r = nft_xt_ctx_get_sreg(ctx, reg); + + if (r) + nft_xt_reg_clear(r); + + return r; +} + struct nft_family_ops { int (*add)(struct nft_handle *h, struct nftnl_rule *r, struct iptables_command_state *cs); @@ -84,9 +138,13 @@ struct nft_family_ops { const struct iptables_command_state *cs_b); void (*print_payload)(struct nftnl_expr *e, struct nftnl_expr_iter *iter); - void (*parse_meta)(struct nft_xt_ctx *ctx, struct nftnl_expr *e, + void (*parse_meta)(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *sreg, + struct nftnl_expr *e, struct iptables_command_state *cs); - void (*parse_payload)(struct nft_xt_ctx *ctx, struct nftnl_expr *e, + void (*parse_payload)(struct nft_xt_ctx *ctx, + const struct nft_xt_ctx_reg *sreg, + struct nftnl_expr *e, struct iptables_command_state *cs); void (*parse_lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e); void (*set_goto_flag)(struct iptables_command_state *cs); -- 2.35.1