From: Florian Westphal Date: Wed, 6 May 2026 12:22:40 +0000 (+0200) Subject: tests: shell: add a test case for dup expression X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c12dad604a2b17f6deaf9fbded32314c68dc7ba6;p=thirdparty%2Fnftables.git tests: shell: add a test case for dup expression Signed-off-by: Florian Westphal --- diff --git a/tests/shell/testcases/packetpath/dumps/dup.nodump b/tests/shell/testcases/packetpath/dumps/dup.nodump new file mode 100644 index 00000000..e69de29b diff --git a/tests/shell/testcases/packetpath/dumps/dup_ip.nodump b/tests/shell/testcases/packetpath/dumps/dup_ip.nodump new file mode 100644 index 00000000..e69de29b diff --git a/tests/shell/testcases/packetpath/dup b/tests/shell/testcases/packetpath/dup new file mode 100755 index 00000000..a58fd1e8 --- /dev/null +++ b/tests/shell/testcases/packetpath/dup @@ -0,0 +1,160 @@ +#!/bin/bash + +# Test case for nftables dup expression with bridge +# Tests that packets are forwarded to the correct device using "dup to" +# Setup: ns1 (sender) -> ns2 (bridge) -> ns3/ns4 (receivers) + +rnd=$(mktemp -u XXXXXXXX) +ns1="nft1dupbr-$rnd" +ns2="nft2dupbr-$rnd" +ns3="nft3dupbr-$rnd" +ns4="nft4dupbr-$rnd" + +cleanup() { + ip netns del "$ns1" 2>/dev/null + ip netns del "$ns2" 2>/dev/null + ip netns del "$ns3" 2>/dev/null + ip netns del "$ns4" 2>/dev/null +} + +die() { + local n="$1" + + ip netns exec "$n" $NFT list ruleset + + echo "ns2 (router)" + ip netns exec "$ns2" $NFT list ruleset + exit 1 +} + +ping_and_check() { + ip netns exec "$ns1" ping -c 1 -W 1 10.0.1.4 >/dev/null + + # Check that packets arrived in both ns3 and ns4 + for n in "$ns3" "$ns4"; do + ip netns exec "$n" $NFT "list counter netdev rxt rxc" | grep -q 'packets 1 ' + if [ $? -ne 0 ]; then + echo "ERROR: counter not incremented in $n" + die "$n" + fi + done +} + +trap cleanup EXIT + +set -e + +# Create network namespaces +for n in "$ns1" "$ns2" "$ns3" "$ns4"; do + ip netns add "$n" + ip -net "$n" link set lo up +done + +# Create veth pairs: +# ns1(veth0) <-> (veth1)ns2 bridge port +# ns3(veth0) <-> (veth3)ns2 bridge port +# ns4(veth0) <-> (veth4)ns2 bridge port +ip link add veth0 netns "$ns1" type veth peer name veth1 netns "$ns2" +ip link add veth0 netns "$ns3" type veth peer name veth3 netns "$ns2" +ip link add veth0 netns "$ns4" type veth peer name veth4 netns "$ns2" + +# Create bridge in ns2 +ip -net "$ns2" link add br0 type bridge + +# Add veth devices to bridge in ns2 +ip -net "$ns2" link set veth1 master br0 +ip -net "$ns2" link set veth3 master br0 +ip -net "$ns2" link set veth4 master br0 + +# Set up IP addresses +ip -net "$ns1" addr add 10.0.1.1/24 dev veth0 +ip -net "$ns2" addr add 10.0.1.2/24 dev br0 +ip -net "$ns3" addr add 10.0.1.3/24 dev veth0 +ip -net "$ns4" addr add 10.0.1.4/24 dev veth0 + +# Bring up interfaces +ip -net "$ns1" link set veth0 up +ip -net "$ns3" link set veth0 up +ip -net "$ns4" link set veth0 up + +ip -net "$ns2" link set veth1 up +ip -net "$ns2" link set veth3 up +ip -net "$ns2" link set veth4 up +ip -net "$ns2" link set br0 up + +# Validate setup: +ip netns exec "$ns1" ping -q -c 1 10.0.1.3 +ip netns exec "$ns1" ping -q -c 1 10.0.1.4 + +# Add nftables rule in ns2 to forward packets from veth1 to veth4 +ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF" +table netdev dup_bridge_test { + chain ingress_veth1 { + type filter hook ingress device veth1 priority 0; policy accept; + + # Dup ICMP packets destined for ns4 also to veth3 (ns3) + ip daddr 10.0.1.4 ip protocol icmp counter dup to "veth3" + } +} +EOF + +[ $? -ne 0 ] && exit 1 + +# so ingress hook in ns3 picks up packet for ns4 mac +ip -net "$ns3" link set veth0 promisc on + +for n in "$ns3" "$ns4"; do +ip netns exec "$n" $NFT -f /dev/stdin <<"EOF" +table netdev rxt { + counter rxc { } + + chain in_veth0 { + type filter hook ingress device veth0 priority 0; policy accept; + ip protocol icmp counter name "rxc" + } +} +EOF +done + +# Verify normal bridge forwarding still works for non-ICMP traffic +# Test that ns3/ns4 are reachable from ns1 without dup interference. +ip netns exec "$ns1" arping -c 1 -I veth0 10.0.1.3 >/dev/null +ip netns exec "$ns1" arping -c 1 -I veth0 10.0.1.4 >/dev/null + +set +e +ping_and_check + +# again, but use egress hook. +ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF" +flush ruleset +table netdev dup_test { + chain egress_veth4 { + type filter hook egress device veth4 priority 0; policy accept; + ip protocol icmp counter dup to "veth3" + } +} +EOF +[ $? -ne 0 ] && exit 1 + +echo "Egress ruleset loaded" +ip netns exec "$ns3" $NFT "reset counter netdev rxt rxc" +ip netns exec "$ns4" $NFT "reset counter netdev rxt rxc" +ping_and_check + +ip netns exec "$ns4" $NFT "reset counter netdev rxt rxc" +ip netns exec "$ns3" $NFT "reset counter netdev rxt rxc" +ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF" +flush ruleset +table netdev dup_test { + chain egress_veth4 { + type filter hook egress device veth4 priority 0; policy accept; + ip protocol icmp counter dup to "veth4" + } +} +EOF +[ $? -ne 0 ] && exit 1 + +# assert dup-to-self doesn't crash. +ip netns exec "$ns1" ping -c 1 -W 1 10.0.1.4 + +exit 0 diff --git a/tests/shell/testcases/packetpath/dup_ip b/tests/shell/testcases/packetpath/dup_ip new file mode 100755 index 00000000..ce57f60c --- /dev/null +++ b/tests/shell/testcases/packetpath/dup_ip @@ -0,0 +1,149 @@ +#!/bin/bash + +# Test case for nftables 'dup to' in ip family. + +# ns1 tx to n3, ns4 via ns2 (routing) +# if ok: +# add rules to ns2 to dup to ns3 +# add ns4 ip to ns3 +# send packet to ns3 and ns4 ip address +# check both arrived at ns3 +# check no packet arrived at ns4 +# +# Then repeat with ns2 having 'egress' ruleset. + +rnd=$(mktemp -u XXXXXXXX) +ns1="nft1in-$rnd" # tx +ns2="nft2in-$rnd" # dup (nft rules) +ns3="nft3in-$rnd" # rx +ns4="nft4in-$rnd" # rx + +cleanup() { + ip netns del "$ns1" 2>/dev/null + ip netns del "$ns2" 2>/dev/null + ip netns del "$ns3" 2>/dev/null + ip netns del "$ns4" 2>/dev/null +} + +die() { + local n="$1" + + ip netns exec "$n" $NFT list ruleset + + echo "ns2 (router)" + ip netns exec "$ns2" $NFT list ruleset + exit 1 +} + +trap cleanup EXIT + +ping_and_check() { + ip netns exec "$ns1" ping -c 1 -W 1 10.0.4.1 >/dev/null + + # Check that packets arrived in both ns3 and ns4 + for n in "$ns3" "$ns4"; do + ip netns exec "$n" $NFT "list counter inet rxt rxc" | grep -q 'packets 1 ' + if [ $? -ne 0 ]; then + echo "ERROR: counter not incremented in $n" + die "$n" + fi + done +} + +set -e +set -x + +for n in "$ns1" "$ns2" "$ns3" "$ns4"; do + ip netns add "$n" + ip -net "$n" link set lo up +done + + +# Create veth pairs: ns1(veth0) <-> (veth1)ns2 +# ns2(veth3) <-> (veth0)ns3 +# ns2(veth4) <-> (veth0)ns4 +ip link add veth0 netns "$ns1" type veth peer name veth1 netns "$ns2" +ip link add veth3 netns "$ns2" type veth peer name veth0 netns "$ns3" +ip link add veth4 netns "$ns2" type veth peer name veth0 netns "$ns4" + +# Set up addresses +ip -net "$ns1" addr add 10.0.1.1/24 dev veth0 + +ip -net "$ns2" addr add 10.0.1.2/24 dev veth1 +ip -net "$ns2" addr add 10.0.3.2/24 dev veth3 +ip -net "$ns2" addr add 10.0.4.2/24 dev veth4 + +ip -net "$ns3" addr add 10.0.3.1/24 dev veth0 +ip -net "$ns4" addr add 10.0.4.1/24 dev veth0 + +# Bring up interfaces +ip -net "$ns1" link set veth0 up + +ip -net "$ns2" link set veth1 up +ip -net "$ns2" link set veth3 up +ip -net "$ns2" link set veth4 up + +ip -net "$ns3" link set veth0 up +ip -net "$ns4" link set veth0 up + +ip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1 + +ip -net "$ns1" route add default via 10.0.1.2 + +ip -net "$ns3" route add default via 10.0.3.2 dev veth0 +ip -net "$ns4" route add default via 10.0.4.2 dev veth0 + +# Validate setup: +ip netns exec "$ns1" ping -q -c 1 10.0.3.1 +ip netns exec "$ns1" ping -q -c 1 10.0.4.1 + +# Add nftables rule in ns2 to forward packets from veth1 to veth3 +# Packets arriving on veth1 (10.0.1.x) will be forwarded to veth3 +ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF" +table ip dup_test { + chain pre { + type filter hook prerouting priority 0; policy accept; + # Forward ICMP packets to 10.0.3.1 via neigh + meta iifname veth1 ip protocol icmp counter dup to 10.0.3.1 device "veth3" + } +} +EOF +[ $? -ne 0 ] && exit 1 + +for n in "$ns3" "$ns4"; do +ip netns exec "$n" $NFT -f /dev/stdin <<"EOF" +table inet rxt { + counter rxc { } + counter rxc { } + + chain in_veth0 { + type filter hook input priority 0; policy accept; + ip protocol icmp counter name "rxc" + } +} +EOF +done + +# duplicate address so stack accepts .4.1 too +ip -net "$ns3" addr add 10.0.4.1/32 dev veth0 + +set +e +ping_and_check + +ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF" +delete chain ip dup_test pre +table ip dup_test { + chain post { + type filter hook postrouting priority 0; policy accept; + meta oifname "veth4" ip protocol icmp counter dup to 10.0.3.1 device "veth3" + } +} +EOF +[ $? -ne 0 ] && exit 1 + +echo "Egress ruleset loaded" +ip netns exec "$ns3" $NFT "reset counter inet rxt rxc" +ip netns exec "$ns4" $NFT "reset counter inet rxt rxc" +ping_and_check + +exit 0