]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft: Fix for broken recover_rule_compat()
authorPhil Sutter <phil@nwl.cc>
Tue, 27 Feb 2024 17:47:39 +0000 (18:47 +0100)
committerPhil Sutter <phil@nwl.cc>
Tue, 27 Feb 2024 18:41:02 +0000 (19:41 +0100)
When IPv4 rule generator was changed to emit payload instead of
meta expressions for l4proto matches, the code reinserting
NFTNL_RULE_COMPAT_* attributes into rules being reused for counter
zeroing was broken by accident.

Make rule compat recovery aware of the alternative match, basically
reinstating the effect of commit 7a373f6683afb ("nft: Fix -Z for rules
with NFTA_RULE_COMPAT") but add a test case this time to make sure
things stay intact.

Fixes: 69278f9602b43 ("nft: use payload matching for layer 4 protocol")
Signed-off-by: Phil Sutter <phil@nwl.cc>
iptables/nft.c
iptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_0 [new file with mode: 0755]

index dae6698d3234a202f1902366cc055823f9a0790f..ee63c3dc42ed400ef86e954cbca0bce9dc0cb012 100644 (file)
@@ -3750,6 +3750,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;
@@ -3766,12 +3787,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, &reg) < 0)
                goto next_expr;
 
-       reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
-
        e = nftnl_expr_iter_next(iter);
        if (!e)
                goto out;
diff --git a/iptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_0 b/iptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_0
new file mode 100755 (executable)
index 0000000..e276a95
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+
+set -e
+
+rule="-p tcp -m tcp --dport 27374 -c 23 42 -j TPROXY --on-port 50080"
+for cmd in iptables ip6tables; do
+       $XT_MULTI $cmd -t mangle -A PREROUTING $rule
+       $XT_MULTI $cmd -t mangle -Z
+       $XT_MULTI $cmd -t mangle -v -S | grep -q -- "${rule/23 42/0 0}"
+done