return $lret
 }
 
+# test port shadowing.
+# create two listening services, one on router (ns0), one
+# on client (ns2), which is masqueraded from ns1 point of view.
+# ns2 sends udp packet coming from service port to ns1, on a highport.
+# Later, if n1 uses same highport to connect to ns0:service, packet
+# might be port-forwarded to ns2 instead.
+
+# second argument tells if we expect the 'fake-entry' to take effect
+# (CLIENT) or not (ROUTER).
+test_port_shadow()
+{
+       local test=$1
+       local expect=$2
+       local daddrc="10.0.1.99"
+       local daddrs="10.0.1.1"
+       local result=""
+       local logmsg=""
+
+       echo ROUTER | ip netns exec "$ns0" nc -w 5 -u -l -p 1405 >/dev/null 2>&1 &
+       nc_r=$!
+
+       echo CLIENT | ip netns exec "$ns2" nc -w 5 -u -l -p 1405 >/dev/null 2>&1 &
+       nc_c=$!
+
+       # make shadow entry, from client (ns2), going to (ns1), port 41404, sport 1405.
+       echo "fake-entry" | ip netns exec "$ns2" nc -w 1 -p 1405 -u "$daddrc" 41404 > /dev/null
+
+       # ns1 tries to connect to ns0:1405.  With default settings this should connect
+       # to client, it matches the conntrack entry created above.
+
+       result=$(echo "" | ip netns exec "$ns1" nc -w 1 -p 41404 -u "$daddrs" 1405)
+
+       if [ "$result" = "$expect" ] ;then
+               echo "PASS: portshadow test $test: got reply from ${expect}${logmsg}"
+       else
+               echo "ERROR: portshadow test $test: got reply from \"$result\", not $expect as intended"
+               ret=1
+       fi
+
+       kill $nc_r $nc_c 2>/dev/null
+
+       # flush udp entries for next test round, if any
+       ip netns exec "$ns0" conntrack -F >/dev/null 2>&1
+}
+
+# This prevents port shadow of router service via packet filter,
+# packets claiming to originate from service port from internal
+# network are dropped.
+test_port_shadow_filter()
+{
+       local family=$1
+
+ip netns exec "$ns0" nft -f /dev/stdin <<EOF
+table $family filter {
+       chain forward {
+               type filter hook forward priority 0; policy accept;
+               meta iif veth1 udp sport 1405 drop
+       }
+}
+EOF
+       test_port_shadow "port-filter" "ROUTER"
+
+       ip netns exec "$ns0" nft delete table $family filter
+}
+
+# This prevents port shadow of router service via notrack.
+test_port_shadow_notrack()
+{
+       local family=$1
+
+ip netns exec "$ns0" nft -f /dev/stdin <<EOF
+table $family raw {
+       chain prerouting {
+               type filter hook prerouting priority -300; policy accept;
+               meta iif veth0 udp dport 1405 notrack
+               udp dport 1405 notrack
+       }
+       chain output {
+               type filter hook output priority -300; policy accept;
+               udp sport 1405 notrack
+       }
+}
+EOF
+       test_port_shadow "port-notrack" "ROUTER"
+
+       ip netns exec "$ns0" nft delete table $family raw
+}
+
+# This prevents port shadow of router service via sport remap.
+test_port_shadow_pat()
+{
+       local family=$1
+
+ip netns exec "$ns0" nft -f /dev/stdin <<EOF
+table $family pat {
+       chain postrouting {
+               type nat hook postrouting priority -1; policy accept;
+               meta iif veth1 udp sport <= 1405 masquerade to : 1406-65535 random
+       }
+}
+EOF
+       test_port_shadow "pat" "ROUTER"
+
+       ip netns exec "$ns0" nft delete table $family pat
+}
+
+test_port_shadowing()
+{
+       local family="ip"
+
+       ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
+       ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
+
+       ip netns exec "$ns0" nft -f /dev/stdin <<EOF
+table $family nat {
+       chain postrouting {
+               type nat hook postrouting priority 0; policy accept;
+               meta oif veth0 masquerade
+       }
+}
+EOF
+       if [ $? -ne 0 ]; then
+               echo "SKIP: Could not add add $family masquerade hook"
+               return $ksft_skip
+       fi
+
+       # test default behaviour. Packet from ns1 to ns0 is redirected to ns2.
+       test_port_shadow "default" "CLIENT"
+
+       # test packet filter based mitigation: prevent forwarding of
+       # packets claiming to come from the service port.
+       test_port_shadow_filter "$family"
+
+       # test conntrack based mitigation: connections going or coming
+       # from router:service bypass connection tracking.
+       test_port_shadow_notrack "$family"
+
+       # test nat based mitigation: fowarded packets coming from service port
+       # are masqueraded with random highport.
+       test_port_shadow_pat "$family"
+
+       ip netns exec "$ns0" nft delete table $family nat
+}
 
 # ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99
 for i in 0 1 2; do
 $test_inet_nat && test_redirect inet
 $test_inet_nat && test_redirect6 inet
 
+test_port_shadowing
+
 if [ $ret -ne 0 ];then
        echo -n "FAIL: "
        nft --version