]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
tests: shell: bad_rule_graphs: add chain linked from different hooks
authorFlorian Westphal <fw@strlen.de>
Tue, 25 Nov 2025 13:03:33 +0000 (14:03 +0100)
committerFlorian Westphal <fw@strlen.de>
Sat, 6 Dec 2025 10:24:01 +0000 (11:24 +0100)
On a kernel with broken (never upstreamed) patch this fails with:

Accepted bad ruleset with jump from filter type to masquerade (3)
and
Accepted bad ruleset with jump from prerouting to masquerade

... because bogus optimisation suppresses re-validation of 'n2', even
though it becomes reachable from an invalid base chain (filter, but n2
has nat-only masquerade expression).

Another broken corner-case is validation of the different hook types:
When it becomes reachable from nat:prerouting in addition to the allowed
nat:postrouting the validation step must fail.

Improve test coverage to ensure future optimisations catch this.

Signed-off-by: Florian Westphal <fw@strlen.de>
tests/shell/testcases/transactions/bad_rule_graphs
tests/shell/testcases/transactions/dumps/bad_rule_graphs.json-nft
tests/shell/testcases/transactions/dumps/bad_rule_graphs.nft

index 53047c3c229faf5768629391e2d9fffd15d2c3dd..1f36bad80792ffa3cb8b00f4f51cee9ed377bcc2 100755 (executable)
@@ -259,4 +259,72 @@ $NFT "add rule t c9 tcp dport 80 tproxy to :20000 meta mark set 1 accept"
 good_ruleset $? "add tproxy expression to c9"
 check_bad_expr
 
+$NFT -f - <<EOF
+table t {
+       chain n2 {
+               meta oifname "ppp*" masquerade
+       }
+
+       chain n1 {
+               ip saddr 192.168.0.0/16 jump n2
+       }
+
+       chain n0 { type nat hook postrouting priority 0;
+               jump n1
+       }
+}
+EOF
+good_ruleset $? "add nat skeleton"
+
+$NFT -f - <<EOF
+table t {
+       chain c2 {
+               jump n2
+       }
+}
+EOF
+bad_ruleset $? "jump from filter type to masquerade"
+
+$NFT flush chain t n2
+$NFT -f - <<EOF
+table t {
+       chain c2 {
+               jump n2
+       }
+
+       chain n2 { meta oifname "ppp0" masquerade; }
+}
+EOF
+bad_ruleset $? "jump from filter type to masquerade (2)"
+
+$NFT -f - <<EOF
+delete chain t c0
+delete chain t c1
+EOF
+good_ruleset $? "delete c1/c0"
+
+$NFT -f - <<EOF
+table t {
+       chain c1 { }
+
+       chain n2 { masquerade; }
+       chain n1 { goto n2; }
+
+       chain n0 { type nat hook postrouting priority 0;
+               goto n1
+       }
+
+       chain c0 { type filter hook input priority 0;
+               jump c1
+       }
+}
+EOF
+good_ruleset $? "add nat skeleton (2)"
+
+$NFT add rule t c1 goto n2
+bad_ruleset $? "jump from filter type to masquerade (3)"
+
+$NFT add chain 't invalid { type nat hook prerouting priority 0; goto n2; }'
+bad_ruleset $? "jump from prerouting to masquerade"
+
 exit $?
index 30789211ff4aac7bd71e399eb06e9250ef51625e..7cc731488f163d1c301753ba614040ddc2659d3d 100644 (file)
         "handle": 0
       }
     },
+    {
+      "chain": {
+        "family": "ip",
+        "table": "t",
+        "name": "n2",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "ip",
+        "table": "t",
+        "name": "n1",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "ip",
+        "table": "t",
+        "name": "n0",
+        "handle": 0,
+        "type": "nat",
+        "hook": "postrouting",
+        "prio": 0,
+        "policy": "accept"
+      }
+    },
     {
       "chain": {
         "family": "ip",
       "rule": {
         "family": "ip",
         "table": "t",
-        "chain": "c1",
+        "chain": "n2",
         "handle": 0,
         "expr": [
           {
-            "jump": {
-              "target": "c2"
-            }
+            "masquerade": null
           }
         ]
       }
       "rule": {
         "family": "ip",
         "table": "t",
-        "chain": "c1",
+        "chain": "n1",
         "handle": 0,
         "expr": [
           {
-            "vmap": {
-              "key": {
+            "match": {
+              "op": "==",
+              "left": {
                 "payload": {
                   "protocol": "ip",
                   "field": "saddr"
                 }
               },
-              "data": "@m"
+              "right": {
+                "prefix": {
+                  "addr": "192.168.0.0",
+                  "len": 16
+                }
+              }
+            }
+          },
+          {
+            "jump": {
+              "target": "n2"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "ip",
+        "table": "t",
+        "chain": "n1",
+        "handle": 0,
+        "expr": [
+          {
+            "goto": {
+              "target": "n2"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "ip",
+        "table": "t",
+        "chain": "n0",
+        "handle": 0,
+        "expr": [
+          {
+            "jump": {
+              "target": "n1"
+            }
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "ip",
+        "table": "t",
+        "chain": "n0",
+        "handle": 0,
+        "expr": [
+          {
+            "goto": {
+              "target": "n1"
             }
           }
         ]
index 3a5936500c6ef51ad0c5aa4f256991fdbfbd8787..314809c3ddab7cb3fc582c0d18fa560f7548819a 100644 (file)
@@ -18,9 +18,22 @@ table ip t {
        chain c2 {
        }
 
+       chain n2 {
+               masquerade
+       }
+
+       chain n1 {
+               ip saddr 192.168.0.0/16 jump n2
+               goto n2
+       }
+
+       chain n0 {
+               type nat hook postrouting priority filter; policy accept;
+               jump n1
+               goto n1
+       }
+
        chain c1 {
-               jump c2
-               ip saddr vmap @m
        }
 
        chain c0 {