From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH nft] tests: shell: add conntrack template attachment test
Date: Wed, 7 Oct 2026 01:19:53 +0200 [thread overview]
Message-ID: <20261006231953.6338-1-fw@strlen.de> (raw)
Try to attach ct templates at various priorities and check that
attachmet only works at the acceptable positions: prerouting + output,
before nf_conntrack_in() on the kernel side.
Relax the test for now to not fail on kernels that lack the required
validation and SKIP if all attachment request work.
Assisted-by: LLM
Signed-off-by: Florian Westphal <fw@strlen.de>
---
Companion nftables.git shell test for the
'netfilter: nft_ct: validate hook and priority for ct zone set' kernel
patch.
It could be added to nftables.git already, its expected to PASS or
SKIP only.
...te_ct_zone_set_template_attach.sh.json-nft | 918 ++++++++++++++++++
...alidate_ct_zone_set_template_attach.sh.nft | 206 ++++
.../validate_ct_zone_set_template_attach.sh | 377 +++++++
3 files changed, 1501 insertions(+)
create mode 100644 tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.json-nft
create mode 100644 tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.nft
create mode 100755 tests/shell/testcases/transactions/validate_ct_zone_set_template_attach.sh
diff --git a/tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.json-nft b/tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.json-nft
new file mode 100644
index 000000000000..9dd84f95cad9
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.json-nft
@@ -0,0 +1,918 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "ctz",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "prerouting_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "prerouting_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "prerouting_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "output_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "output_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "output_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "postrouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "ctz",
+ "name": "nonbase",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "ctz",
+ "chain": "output_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "ctz",
+ "chain": "output_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "ctz",
+ "chain": "nonbase",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "ctz",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "prerouting_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "prerouting_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "prerouting_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "output_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "output_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "output_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "postrouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "ctz",
+ "name": "nonbase",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "ctz",
+ "chain": "output_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "ctz",
+ "chain": "output_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "ctz",
+ "chain": "nonbase",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "ctz",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "prerouting_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "prerouting_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "prerouting_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "output_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "output_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "output_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "postrouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "ctz",
+ "name": "nonbase",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "ctz",
+ "chain": "nonbase",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "ctz",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "prerouting_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "prerouting_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "prerouting_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "output_before",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -201,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "output_at",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "output_after",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -199,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "postrouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "nonbase",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "ctz",
+ "name": "ingress",
+ "handle": 0,
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "ctz",
+ "chain": "prerouting_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "ctz",
+ "chain": "output_before",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "ctz",
+ "chain": "output_before",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nonbase"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "ctz",
+ "chain": "nonbase",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "zone"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "ctz",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "ctz",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "ctz",
+ "name": "output",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "ctz",
+ "name": "nonbase",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "ctz",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "ctz",
+ "name": "ingress",
+ "handle": 0,
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "ctz",
+ "name": "nonbase",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "ctz",
+ "name": "egress",
+ "handle": 0,
+ "type": "filter",
+ "hook": "egress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.nft b/tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.nft
new file mode 100644
index 000000000000..dee5c6259e0e
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/validate_ct_zone_set_template_attach.sh.nft
@@ -0,0 +1,206 @@
+table ip ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain prerouting_at {
+ type filter hook prerouting priority -200; policy accept;
+ }
+
+ chain prerouting_after {
+ type filter hook prerouting priority -199; policy accept;
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ }
+
+ chain forward {
+ type filter hook forward priority filter; policy accept;
+ }
+
+ chain output_before {
+ type filter hook output priority -201; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain output_at {
+ type filter hook output priority -200; policy accept;
+ }
+
+ chain output_after {
+ type filter hook output priority -199; policy accept;
+ }
+
+ chain postrouting {
+ type filter hook postrouting priority filter; policy accept;
+ }
+
+ chain nonbase {
+ ct zone set 1
+ }
+}
+table ip6 ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain prerouting_at {
+ type filter hook prerouting priority -200; policy accept;
+ }
+
+ chain prerouting_after {
+ type filter hook prerouting priority -199; policy accept;
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ }
+
+ chain forward {
+ type filter hook forward priority filter; policy accept;
+ }
+
+ chain output_before {
+ type filter hook output priority -201; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain output_at {
+ type filter hook output priority -200; policy accept;
+ }
+
+ chain output_after {
+ type filter hook output priority -199; policy accept;
+ }
+
+ chain postrouting {
+ type filter hook postrouting priority filter; policy accept;
+ }
+
+ chain nonbase {
+ ct zone set 1
+ }
+}
+table bridge ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority filter - 1; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain prerouting_at {
+ type filter hook prerouting priority filter; policy accept;
+ }
+
+ chain prerouting_after {
+ type filter hook prerouting priority filter + 1; policy accept;
+ }
+
+ chain input {
+ type filter hook input priority 0; policy accept;
+ }
+
+ chain forward {
+ type filter hook forward priority 0; policy accept;
+ }
+
+ chain output_before {
+ type filter hook output priority filter - 1; policy accept;
+ }
+
+ chain output_at {
+ type filter hook output priority filter; policy accept;
+ }
+
+ chain output_after {
+ type filter hook output priority filter + 1; policy accept;
+ }
+
+ chain postrouting {
+ type filter hook postrouting priority 0; policy accept;
+ }
+
+ chain nonbase {
+ ct zone set 1
+ }
+}
+table inet ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain prerouting_at {
+ type filter hook prerouting priority -200; policy accept;
+ }
+
+ chain prerouting_after {
+ type filter hook prerouting priority -199; policy accept;
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ }
+
+ chain forward {
+ type filter hook forward priority filter; policy accept;
+ }
+
+ chain output_before {
+ type filter hook output priority -201; policy accept;
+ ct zone set 1
+ jump nonbase
+ }
+
+ chain output_at {
+ type filter hook output priority -200; policy accept;
+ }
+
+ chain output_after {
+ type filter hook output priority -199; policy accept;
+ }
+
+ chain postrouting {
+ type filter hook postrouting priority filter; policy accept;
+ }
+
+ chain nonbase {
+ ct zone set 1
+ }
+
+ chain ingress {
+ type filter hook ingress priority filter; policy accept;
+ }
+}
+table arp ctz {
+ chain input {
+ type filter hook input priority filter; policy accept;
+ }
+
+ chain output {
+ type filter hook output priority filter; policy accept;
+ }
+
+ chain nonbase {
+ }
+}
+table netdev ctz {
+ chain ingress {
+ type filter hook ingress priority filter; policy accept;
+ }
+
+ chain nonbase {
+ }
+
+ chain egress {
+ type filter hook egress priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/transactions/validate_ct_zone_set_template_attach.sh b/tests/shell/testcases/transactions/validate_ct_zone_set_template_attach.sh
new file mode 100755
index 000000000000..c2dab6ac6b25
--- /dev/null
+++ b/tests/shell/testcases/transactions/validate_ct_zone_set_template_attach.sh
@@ -0,0 +1,377 @@
+#!/bin/bash
+#
+# Test ct zone set placement in base chains across all families and hooks.
+#
+# ct zone set is only valid in:
+# - PREROUTING hook with priority < -200 (before conntrack)
+# - OUTPUT hook in ip/ip6/inet families (bridge OUTPUT does not see traffic)
+#
+# For every other base chain the kernel must reject both
+# 'ct zone set <n>'
+# and
+# 'jump <non-base-chain containing ct zone set>'
+#
+# same for the unsupported families and hooks.
+
+set -e
+
+expected_fail=0
+actual_fail=0
+total=0
+expected_ok=0
+actual_ok=0
+bad=0
+
+errlog() {
+ local fam=$1 chain=$2 what=$3 err=$4
+
+ echo "Unxpected $err: add rule $fam ctz $chain $what"
+ $NFT list table $fam ctz
+}
+
+test_chain() {
+ local fam=$1 chain=$2 expected=$3
+ local pass="n"
+
+ total=$((total + 1))
+
+ if [ "$expected" = "y" ]; then
+ expected_ok=$((expected_ok + 1))
+ fi
+
+ if $NFT add rule "$fam" ctz "$chain" ct zone set 1; then
+ # accepted -- jump to non-base chain must also be accepted
+ if ! $NFT add rule "$fam" ctz "$chain" jump nonbase; then
+ errlog $fam $chain "jump" "failure"
+ bad=$((bad + 1))
+ else
+ pass="y"
+ actual_ok=$((actual_ok + 1))
+ fi
+ else
+ if [ "$expected" = "y" ]; then
+ errlog $fam $chain "ct zone set 1" "failure"
+ bad=$((bad + 1))
+ return
+ fi
+
+ # rejected -- jump to non-base chain must also be rejected
+ if [ "$fam" = "netdev" ] || [ "$fam" = "arp" ] ; then
+ # non-basechain doesn't have a ct zone set rule.
+ if $NFT add rule "$fam" ctz "nonbase" ct zone set 1; then
+ errlog $fam $chain "ct zone set 1" "success"
+ bad=$((bad + 1))
+ else
+ expected_fail=$((expected_fail + 1))
+ fi
+
+ return
+ fi
+
+ if $NFT add rule "$fam" ctz "$chain" jump nonbase; then
+ errlog $fam $chain "jump" "success"
+ bad=$((bad + 1))
+ fi
+ fi
+
+ if [ "$pass" = "y" ] && [ "$expected" = "n" ]; then
+ errlog $fam "$chain" "both" "success"
+ fi
+}
+
+# ---------------------------------------------------------------------------
+# Create tables with base chains in every family / hook.
+# PREROUTING and OUTPUT each get three chains:
+# -201 (before conntrack), -200 (at conntrack), -201 (after conntrack).
+#
+# Each table also carries a non-base chain "nonbase" holding
+# 'ct zone set 1'; no base chain jumps to it at this point.
+# ---------------------------------------------------------------------------
+
+$NFT -f - <<EOF
+table ip ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201;
+ policy accept;
+ }
+ chain prerouting_at {
+ type filter hook prerouting priority -200;
+ policy accept;
+ }
+ chain prerouting_after {
+ type filter hook prerouting priority -199;
+ policy accept;
+ }
+ chain input {
+ type filter hook input priority 0;
+ policy accept;
+ }
+ chain forward {
+ type filter hook forward priority 0;
+ policy accept;
+ }
+ chain output_before {
+ type filter hook output priority -201;
+ policy accept;
+ }
+ chain output_at {
+ type filter hook output priority -200;
+ policy accept;
+ }
+ chain output_after {
+ type filter hook output priority -199;
+ policy accept;
+ }
+ chain postrouting {
+ type filter hook postrouting priority 0;
+ policy accept;
+ }
+ chain nonbase {
+ ct zone set 1;
+ }
+}
+
+table ip6 ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201;
+ policy accept;
+ }
+ chain prerouting_at {
+ type filter hook prerouting priority -200;
+ policy accept;
+ }
+ chain prerouting_after {
+ type filter hook prerouting priority -199;
+ policy accept;
+ }
+ chain input {
+ type filter hook input priority 0;
+ policy accept;
+ }
+ chain forward {
+ type filter hook forward priority 0;
+ policy accept;
+ }
+ chain output_before {
+ type filter hook output priority -201;
+ policy accept;
+ }
+ chain output_at {
+ type filter hook output priority -200;
+ policy accept;
+ }
+ chain output_after {
+ type filter hook output priority -199;
+ policy accept;
+ }
+ chain postrouting {
+ type filter hook postrouting priority 0;
+ policy accept;
+ }
+ chain nonbase {
+ ct zone set 1;
+ }
+}
+
+table bridge ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201;
+ policy accept;
+ }
+ chain prerouting_at {
+ type filter hook prerouting priority -200;
+ policy accept;
+ }
+ chain prerouting_after {
+ type filter hook prerouting priority -199;
+ policy accept;
+ }
+ chain input {
+ type filter hook input priority 0;
+ policy accept;
+ }
+ chain forward {
+ type filter hook forward priority 0;
+ policy accept;
+ }
+ chain output_before {
+ type filter hook output priority -201;
+ policy accept;
+ }
+ chain output_at {
+ type filter hook output priority -200;
+ policy accept;
+ }
+ chain output_after {
+ type filter hook output priority -199;
+ policy accept;
+ }
+ chain postrouting {
+ type filter hook postrouting priority 0;
+ policy accept;
+ }
+ chain nonbase {
+ ct zone set 1;
+ }
+}
+
+table inet ctz {
+ chain prerouting_before {
+ type filter hook prerouting priority -201;
+ policy accept;
+ }
+ chain prerouting_at {
+ type filter hook prerouting priority -200;
+ policy accept;
+ }
+ chain prerouting_after {
+ type filter hook prerouting priority -199;
+ policy accept;
+ }
+ chain input {
+ type filter hook input priority 0;
+ policy accept;
+ }
+ chain forward {
+ type filter hook forward priority 0;
+ policy accept;
+ }
+ chain output_before {
+ type filter hook output priority -201;
+ policy accept;
+ }
+ chain output_at {
+ type filter hook output priority -200;
+ policy accept;
+ }
+ chain output_after {
+ type filter hook output priority -199;
+ policy accept;
+ }
+ chain postrouting {
+ type filter hook postrouting priority 0;
+ policy accept;
+ }
+ chain nonbase {
+ ct zone set 1;
+ }
+}
+
+table arp ctz {
+ chain input {
+ type filter hook input priority 0;
+ policy accept;
+ }
+ chain output {
+ type filter hook output priority 0;
+ policy accept;
+ }
+ chain nonbase {
+ }
+}
+
+table netdev ctz {
+ chain ingress {
+ type filter hook ingress priority 0;
+ policy accept;
+ }
+ chain nonbase {
+ }
+}
+EOF
+
+# Conditionally add netdev egress and inet ingress when supported.
+if [ "${NFT_TEST_HAVE_netdev_egress}" = "y" ]; then
+$NFT -f - <<EOF
+ table netdev ctz {
+ chain egress {
+ type filter hook egress priority 0;
+ policy accept;
+ }
+ }
+EOF
+fi
+
+if [ "${NFT_TEST_HAVE_inet_ingress}" = "y" ]; then
+$NFT -f - <<EOF
+ table inet ctz {
+ chain ingress {
+ type filter hook ingress priority 0;
+ policy accept;
+ }
+ }
+EOF
+fi
+
+# ---------------------------------------------------------------------------
+# Exercise every base chain.
+# Expected: "y" = ct zone set must be accepted, "n" = must be rejected.
+# ---------------------------------------------------------------------------
+
+# --- inet ---
+for family in ip ip6 inet; do
+ test_chain $family prerouting_before y
+ test_chain $family prerouting_at n
+ test_chain $family prerouting_after n
+ test_chain $family input n
+ test_chain $family forward n
+ test_chain $family output_before y
+ test_chain $family output_at n
+ test_chain $family output_after n
+ test_chain $family postrouting n
+done
+
+# --- bridge (OUTPUT is NOT expected to work) ---
+test_chain bridge prerouting_before y
+test_chain bridge prerouting_at n
+test_chain bridge prerouting_after n
+test_chain bridge input n
+test_chain bridge forward n
+test_chain bridge output_before n
+test_chain bridge output_at n
+test_chain bridge output_after n
+test_chain bridge postrouting n
+
+# --- arp ---
+test_chain arp input n
+test_chain arp output n
+
+# --- netdev ---
+test_chain netdev ingress n
+
+# ingress, arp in + out.
+if [ "${NFT_TEST_HAVE_netdev_egress}" = "y" ]; then
+ test_chain netdev egress n
+fi
+
+# --- inet ingress (if supported) ---
+if [ "${NFT_TEST_HAVE_inet_ingress}" = "y" ]; then
+ test_chain inet ingress n
+fi
+
+# ---------------------------------------------------------------------------
+# Evaluate results.
+# ---------------------------------------------------------------------------
+echo "Bad $bad, actual_ok $actual_ok, expected_ok $expected_ok, total $total"
+
+if [ "$bad" -ne 0 ]; then
+ echo "FAIL: $bad unexpected result(s) out of $total"
+ exit 1
+fi
+
+# Expected successes and failures matched exactly.
+if [ "$actual_ok" -eq "$expected_ok" ]; then
+ exit 0
+fi
+
+actual_ok=$((actual_ok+expected_fail))
+if [ "$actual_ok" -eq "$total" ]; then
+ # Every chain accepted ct zone set -- kernel does not enforce the
+ # restriction, so the test has nothing to verify.
+ # This can hopefully be removed in the future.
+ exit 77
+fi
+
+# Unexpected
+echo "Error: no rules failed, but inconsistent number of passed/failed tests"
+exit 1
--
2.55.0
next reply other threads:[~2026-10-06 23:20 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 23:19 Florian Westphal [this message]
2026-10-07 11:40 ` [PATCH nft] tests: shell: add conntrack template attachment test Florian Westphal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261006231953.6338-1-fw@strlen.de \
--to=fw@strlen.de \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox