|
@@ -0,0 +1,1054 @@
|
|
|
+From f315af1cf88714702dcc51dc00b109df3d52e9e9 Mon Sep 17 00:00:00 2001
|
|
|
+From: Florian Westphal <[email protected]>
|
|
|
+Date: Fri, 23 Sep 2022 14:17:08 +0200
|
|
|
+Subject: nft: track each register individually
|
|
|
+
|
|
|
+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/[email protected]/T/#t
|
|
|
+Signed-off-by: Florian Westphal <[email protected]>
|
|
|
+Reviewed-by: Phil Sutter <[email protected]>
|
|
|
+---
|
|
|
+ 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(-)
|
|
|
+
|
|
|
+--- a/iptables/nft-arp.c
|
|
|
++++ b/iptables/nft-arp.c
|
|
|
+@@ -160,25 +160,27 @@ static int nft_arp_add(struct nft_handle
|
|
|
+ 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
|
|
|
+
|
|
|
+ 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
|
|
|
+ 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
|
|
|
+ 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;
|
|
|
+--- a/iptables/nft-bridge.c
|
|
|
++++ b/iptables/nft-bridge.c
|
|
|
+@@ -170,6 +170,7 @@ static int nft_bridge_add(struct nft_han
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+ 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
|
|
|
+ }
|
|
|
+
|
|
|
+ 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(str
|
|
|
+ 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(str
|
|
|
+ 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(str
|
|
|
+ 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
|
|
|
+ /* 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
|
|
|
+ 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(stru
|
|
|
+ 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(stru
|
|
|
+ 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,
|
|
|
+--- a/iptables/nft-ipv4.c
|
|
|
++++ b/iptables/nft-ipv4.c
|
|
|
+@@ -115,28 +115,28 @@ static bool nft_ipv4_is_same(const struc
|
|
|
+ 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 n
|
|
|
+ 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(struc
|
|
|
+ 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(struc
|
|
|
+ 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,13 +199,12 @@ static void nft_ipv4_parse_payload(struc
|
|
|
+ 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;
|
|
|
+ default:
|
|
|
+- DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
|
|
|
++ DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+--- a/iptables/nft-ipv6.c
|
|
|
++++ b/iptables/nft-ipv6.c
|
|
|
+@@ -104,10 +104,12 @@ static bool nft_ipv6_is_same(const struc
|
|
|
+ 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 n
|
|
|
+ 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(struc
|
|
|
+ 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(struc
|
|
|
+ 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;
|
|
|
+--- a/iptables/nft-shared.c
|
|
|
++++ b/iptables/nft-shared.c
|
|
|
+@@ -295,6 +295,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;
|
|
|
+@@ -312,12 +322,7 @@ static int parse_meta_mark(struct nft_xt
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+@@ -451,20 +456,40 @@ void get_cmp_data(struct nftnl_expr *e,
|
|
|
+ *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;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -486,51 +511,74 @@ static void nft_meta_set_to_target(struc
|
|
|
+
|
|
|
+ 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 *
|
|
|
+@@ -835,6 +883,8 @@ static void nft_parse_transport(struct n
|
|
|
+ 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;
|
|
|
+@@ -855,7 +905,17 @@ static void nft_parse_transport(struct n
|
|
|
+ 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 */
|
|
|
+@@ -881,10 +941,9 @@ static void nft_parse_transport(struct n
|
|
|
+ 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;
|
|
|
+@@ -892,6 +951,7 @@ static void nft_parse_transport(struct n
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+@@ -921,7 +981,7 @@ static void nft_parse_transport_range(st
|
|
|
+ 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;
|
|
|
+@@ -934,30 +994,40 @@ static void nft_parse_transport_range(st
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -976,18 +1046,22 @@ static void nft_parse_immediate(struct n
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1124,20 +1198,29 @@ static void nft_parse_lookup(struct nft_
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+--- 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);
|