1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# Test NAT source port clash resolution 5# 6 7source lib.sh 8ret=0 9socatpid=0 10 11cleanup() 12{ 13 [ "$socatpid" -gt 0 ] && kill "$socatpid" 14 15 cleanup_all_ns 16} 17 18checktool "socat -h" "run test without socat" 19checktool "iptables --version" "run test without iptables" 20checktool "conntrack --version" "run test without conntrack" 21 22trap cleanup EXIT 23 24connect_done() 25{ 26 local ns="$1" 27 local port="$2" 28 29 ip netns exec "$ns" ss -nt -o state established "dport = :$port" | grep -q "$port" 30} 31 32check_ctstate() 33{ 34 local ns="$1" 35 local dp="$2" 36 37 if ! ip netns exec "$ns" conntrack --get -s 192.168.1.2 -d 192.168.1.1 -p tcp \ 38 --sport 10000 --dport "$dp" --state ESTABLISHED > /dev/null 2>&1;then 39 echo "FAIL: Did not find expected state for dport $2" 40 ip netns exec "$ns" bash -c 'conntrack -L; conntrack -S; ss -nt' 41 ret=1 42 fi 43} 44 45setup_ns ns1 ns2 46 47# Connect the namespaces using a veth pair 48ip link add name veth2 type veth peer name veth1 49ip link set netns "$ns1" dev veth1 50ip link set netns "$ns2" dev veth2 51 52ip netns exec "$ns1" ip link set up dev lo 53ip netns exec "$ns1" ip link set up dev veth1 54ip netns exec "$ns1" ip addr add 192.168.1.1/24 dev veth1 55 56ip netns exec "$ns2" ip link set up dev lo 57ip netns exec "$ns2" ip link set up dev veth2 58ip netns exec "$ns2" ip addr add 192.168.1.2/24 dev veth2 59 60# Create a server in one namespace 61ip netns exec "$ns1" socat -u TCP-LISTEN:5201,fork OPEN:/dev/null,wronly=1 & 62socatpid=$! 63 64# Restrict source port to just one so we don't have to exhaust 65# all others. 66ip netns exec "$ns2" sysctl -q net.ipv4.ip_local_port_range="10000 10000" 67 68# add a virtual IP using DNAT 69ip netns exec "$ns2" iptables -t nat -A OUTPUT -d 10.96.0.1/32 -p tcp --dport 443 -j DNAT --to-destination 192.168.1.1:5201 || exit 1 70 71# ... and route it to the other namespace 72ip netns exec "$ns2" ip route add 10.96.0.1 via 192.168.1.1 73 74# listener should be up by now, wait if it isn't yet. 75wait_local_port_listen "$ns1" 5201 tcp 76 77# add a persistent connection from the other namespace 78sleep 10 | ip netns exec "$ns2" socat -t 10 - TCP:192.168.1.1:5201 > /dev/null & 79cpid0=$! 80busywait "$BUSYWAIT_TIMEOUT" connect_done "$ns2" "5201" 81 82# ip daddr:dport will be rewritten to 192.168.1.1 5201 83# NAT must reallocate source port 10000 because 84# 192.168.1.2:10000 -> 192.168.1.1:5201 is already in use 85echo test | ip netns exec "$ns2" socat -t 3 -u STDIN TCP:10.96.0.1:443,connect-timeout=3 >/dev/null 86ret=$? 87 88# Check socat can connect to 10.96.0.1:443 (aka 192.168.1.1:5201). 89if [ $ret -eq 0 ]; then 90 echo "PASS: socat can connect via NAT'd address" 91else 92 echo "FAIL: socat cannot connect via NAT'd address" 93fi 94 95# check sport clashres. 96ip netns exec "$ns1" iptables -t nat -A PREROUTING -p tcp --dport 5202 -j REDIRECT --to-ports 5201 97ip netns exec "$ns1" iptables -t nat -A PREROUTING -p tcp --dport 5203 -j REDIRECT --to-ports 5201 98 99sleep 5 | ip netns exec "$ns2" socat -T 5 -u STDIN TCP:192.168.1.1:5202,connect-timeout=5 >/dev/null & 100cpid1=$! 101 102sleep 5 | ip netns exec "$ns2" socat -T 5 -u STDIN TCP:192.168.1.1:5203,connect-timeout=5 >/dev/null & 103cpid2=$! 104 105busywait "$BUSYWAIT_TIMEOUT" connect_done "$ns2" 5202 106busywait "$BUSYWAIT_TIMEOUT" connect_done "$ns2" 5203 107 108check_ctstate "$ns1" 5202 109check_ctstate "$ns1" 5203 110 111kill $socatpid $cpid0 $cpid1 $cpid2 112socatpid=0 113 114if [ $ret -eq 0 ]; then 115 echo "PASS: could connect to service via redirected ports" 116else 117 echo "FAIL: socat cannot connect to service via redirect" 118 ret=1 119fi 120 121exit $ret 122