Linux Netfilter development
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH nft] tests: shell: add packetpath test for nat on loopback
Date: Wed, 16 Sep 2026 09:18:47 +0200	[thread overview]
Message-ID: <20260916071853.23560-1-fw@strlen.de> (raw)

Esoteric use case, but legal: NAT port rewrite over loopback.
Also add drop rule to ensure that the packet is still tracked
in input.

Assisted-by: LLM
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 .../packetpath/dumps/nat_lo.json-nft          | 152 ++++++++++++++++++
 .../testcases/packetpath/dumps/nat_lo.nft     |  14 ++
 tests/shell/testcases/packetpath/nat_lo       |  42 +++++
 3 files changed, 208 insertions(+)
 create mode 100644 tests/shell/testcases/packetpath/dumps/nat_lo.json-nft
 create mode 100644 tests/shell/testcases/packetpath/dumps/nat_lo.nft
 create mode 100755 tests/shell/testcases/packetpath/nat_lo

diff --git a/tests/shell/testcases/packetpath/dumps/nat_lo.json-nft b/tests/shell/testcases/packetpath/dumps/nat_lo.json-nft
new file mode 100644
index 000000000000..1b8dca338ee0
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/nat_lo.json-nft
@@ -0,0 +1,152 @@
+{
+  "nftables": [
+    {
+      "metainfo": {
+        "version": "VERSION",
+        "release_name": "RELEASE_NAME",
+        "json_schema_version": 1
+      }
+    },
+    {
+      "table": {
+        "family": "inet",
+        "name": "nat",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "inet",
+        "table": "nat",
+        "name": "output",
+        "handle": 0,
+        "type": "nat",
+        "hook": "output",
+        "prio": -100,
+        "policy": "accept"
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "nat",
+        "chain": "output",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "payload": {
+                  "protocol": "tcp",
+                  "field": "dport"
+                }
+              },
+              "right": 1234
+            }
+          },
+          {
+            "redirect": {
+              "port": 12345
+            }
+          }
+        ]
+      }
+    },
+    {
+      "table": {
+        "family": "inet",
+        "name": "filter",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "inet",
+        "table": "filter",
+        "name": "input",
+        "handle": 0,
+        "type": "filter",
+        "hook": "input",
+        "prio": 0,
+        "policy": "drop"
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "filter",
+        "chain": "input",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "in",
+              "left": {
+                "ct": {
+                  "key": "state"
+                }
+              },
+              "right": [
+                "established",
+                "related",
+                "new"
+              ]
+            }
+          },
+          {
+            "accept": null
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "filter",
+        "chain": "input",
+        "handle": 0,
+        "expr": [
+          {
+            "match": {
+              "op": "==",
+              "left": {
+                "meta": {
+                  "key": "l4proto"
+                }
+              },
+              "right": {
+                "set": [
+                  1,
+                  58
+                ]
+              }
+            }
+          },
+          {
+            "accept": null
+          }
+        ]
+      }
+    },
+    {
+      "rule": {
+        "family": "inet",
+        "table": "filter",
+        "chain": "input",
+        "handle": 0,
+        "expr": [
+          {
+            "counter": {
+              "packets": 0,
+              "bytes": 0
+            }
+          },
+          {
+            "drop": null
+          }
+        ]
+      }
+    }
+  ]
+}
diff --git a/tests/shell/testcases/packetpath/dumps/nat_lo.nft b/tests/shell/testcases/packetpath/dumps/nat_lo.nft
new file mode 100644
index 000000000000..65259ec80242
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/nat_lo.nft
@@ -0,0 +1,14 @@
+table inet nat {
+	chain output {
+		type nat hook output priority dstnat; policy accept;
+		tcp dport 1234 redirect to :12345
+	}
+}
+table inet filter {
+	chain input {
+		type filter hook input priority filter; policy drop;
+		ct state established,related,new accept
+		meta l4proto { 1, 58 } accept
+		counter packets 0 bytes 0 drop
+	}
+}
diff --git a/tests/shell/testcases/packetpath/nat_lo b/tests/shell/testcases/packetpath/nat_lo
new file mode 100755
index 000000000000..862aadc82ef1
--- /dev/null
+++ b/tests/shell/testcases/packetpath/nat_lo
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+set -e
+
+$NFT -f - <<EOF
+table inet nat {
+	chain output {
+		type nat hook output priority dstnat; policy accept;
+		tcp dport 1234 redirect to :12345
+	}
+}
+
+table inet filter {
+	chain input {
+		type filter hook input priority filter; policy drop;
+		ct state new,established,related accept
+		meta l4proto { icmp, icmpv6 } accept
+		counter drop
+	}
+}
+EOF
+
+ip link set lo up
+
+# Start listener on the target port
+timeout 5 socat TCP6-LISTEN:12345,ipv6only=1,fork STDOUT &
+PID1=$!
+timeout 5 socat TCP4-LISTEN:12345,fork STDOUT &
+PID2=$!
+
+# Give socat a moment to bind
+sleep 1
+
+# Test IPv4 translation
+echo "Testing IPv4..."
+echo "hello ipv4" | socat -u STDIN TCP:127.0.0.1:1234
+
+# Test IPv6 translation
+echo "Testing IPv6..."
+echo "hello ipv6" | socat -u STDIN TCP:[::1]:1234
+
+kill $PID1 $PID2
-- 
2.55.0


                 reply	other threads:[~2026-09-16  7:19 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260916071853.23560-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