1#!/bin/bash 2# 3# Test connection tracking zone and NAT source port reallocation support. 4# 5 6source lib.sh 7 8# Don't increase too much, 2000 clients should work 9# just fine but script can then take several minutes with 10# KASAN/debug builds. 11maxclients=100 12 13have_socat=0 14ret=0 15 16[ "$KSFT_MACHINE_SLOW" = yes ] && maxclients=40 17# client1---. 18# veth1-. 19# | 20# NAT Gateway --veth0--> Server 21# | | 22# veth2-' | 23# client2---' | 24# .... | 25# clientX----vethX---' 26 27# All clients share identical IP address. 28# NAT Gateway uses policy routing and conntrack zones to isolate client 29# namespaces. Each client connects to Server, each with colliding tuples: 30# clientsaddr:10000 -> serveraddr:dport 31# NAT Gateway is supposed to do port reallocation for each of the 32# connections. 33 34v4gc1=$(sysctl -n net.ipv4.neigh.default.gc_thresh1 2>/dev/null) 35v4gc2=$(sysctl -n net.ipv4.neigh.default.gc_thresh2 2>/dev/null) 36v4gc3=$(sysctl -n net.ipv4.neigh.default.gc_thresh3 2>/dev/null) 37v6gc1=$(sysctl -n net.ipv6.neigh.default.gc_thresh1 2>/dev/null) 38v6gc2=$(sysctl -n net.ipv6.neigh.default.gc_thresh2 2>/dev/null) 39v6gc3=$(sysctl -n net.ipv6.neigh.default.gc_thresh3 2>/dev/null) 40 41cleanup() 42{ 43 cleanup_all_ns 44 45 sysctl -q net.ipv4.neigh.default.gc_thresh1="$v4gc1" 2>/dev/null 46 sysctl -q net.ipv4.neigh.default.gc_thresh2="$v4gc2" 2>/dev/null 47 sysctl -q net.ipv4.neigh.default.gc_thresh3="$v4gc3" 2>/dev/null 48 sysctl -q net.ipv6.neigh.default.gc_thresh1="$v6gc1" 2>/dev/null 49 sysctl -q net.ipv6.neigh.default.gc_thresh2="$v6gc2" 2>/dev/null 50 sysctl -q net.ipv6.neigh.default.gc_thresh3="$v6gc3" 2>/dev/null 51} 52 53checktool "nft --version" echo "run test without nft tool" 54checktool "conntrack -V" "run test without conntrack tool" 55 56if socat -h >/dev/null 2>&1; then 57 have_socat=1 58fi 59 60setup_ns gw srv 61 62trap cleanup EXIT 63 64ip link add veth0 netns "$gw" type veth peer name eth0 netns "$srv" 65ip -net "$gw" link set veth0 up 66ip -net "$srv" link set eth0 up 67 68sysctl -q net.ipv6.neigh.default.gc_thresh1=512 2>/dev/null 69sysctl -q net.ipv6.neigh.default.gc_thresh2=1024 2>/dev/null 70sysctl -q net.ipv6.neigh.default.gc_thresh3=4096 2>/dev/null 71sysctl -q net.ipv4.neigh.default.gc_thresh1=512 2>/dev/null 72sysctl -q net.ipv4.neigh.default.gc_thresh2=1024 2>/dev/null 73sysctl -q net.ipv4.neigh.default.gc_thresh3=4096 2>/dev/null 74 75for i in $(seq 1 "$maxclients");do 76 setup_ns "cl$i" 77 78 cl=$(eval echo \$cl"$i") 79 if ! ip link add veth"$i" netns "$gw" type veth peer name eth0 netns "$cl" > /dev/null 2>&1;then 80 echo "SKIP: No virtual ethernet pair device support in kernel" 81 exit $ksft_skip 82 fi 83done 84 85for i in $(seq 1 "$maxclients");do 86 cl=$(eval echo \$cl"$i") 87 echo netns exec "$cl" ip link set eth0 up 88 echo netns exec "$cl" sysctl -q net.ipv4.tcp_syn_retries=2 89 echo netns exec "$gw" ip link set "veth$i" up 90 echo netns exec "$gw" sysctl -q net.ipv4.conf.veth"$i".arp_ignore=2 91 echo netns exec "$gw" sysctl -q net.ipv4.conf.veth"$i".rp_filter=0 92 93 # clients have same IP addresses. 94 echo netns exec "$cl" ip addr add 10.1.0.3/24 dev eth0 95 echo netns exec "$cl" ip addr add dead:1::3/64 dev eth0 nodad 96 echo netns exec "$cl" ip route add default via 10.1.0.2 dev eth0 97 echo netns exec "$cl" ip route add default via dead:1::2 dev eth0 98 99 # NB: same addresses on client-facing interfaces. 100 echo netns exec "$gw" ip addr add 10.1.0.2/24 dev "veth$i" 101 echo netns exec "$gw" ip addr add dead:1::2/64 dev "veth$i" nodad 102 103 # gw: policy routing 104 echo netns exec "$gw" ip route add 10.1.0.0/24 dev "veth$i" table $((1000+i)) 105 echo netns exec "$gw" ip route add dead:1::0/64 dev "veth$i" table $((1000+i)) 106 echo netns exec "$gw" ip route add 10.3.0.0/24 dev veth0 table $((1000+i)) 107 echo netns exec "$gw" ip route add dead:3::0/64 dev veth0 table $((1000+i)) 108 echo netns exec "$gw" ip rule add fwmark "$i" lookup $((1000+i)) 109done | ip -batch /dev/stdin 110 111ip -net "$gw" addr add 10.3.0.1/24 dev veth0 112ip -net "$gw" addr add dead:3::1/64 dev veth0 nodad 113 114ip -net "$srv" addr add 10.3.0.99/24 dev eth0 115ip -net "$srv" addr add dead:3::99/64 dev eth0 nodad 116 117ip netns exec "$gw" nft -f /dev/stdin<<EOF 118table inet raw { 119 map iiftomark { 120 type ifname : mark 121 } 122 123 map iiftozone { 124 typeof iifname : ct zone 125 } 126 127 set inicmp { 128 flags dynamic 129 type ipv4_addr . ifname . ipv4_addr 130 } 131 set inflows { 132 flags dynamic 133 type ipv4_addr . inet_service . ifname . ipv4_addr . inet_service 134 } 135 136 set inflows6 { 137 flags dynamic 138 type ipv6_addr . inet_service . ifname . ipv6_addr . inet_service 139 } 140 141 chain prerouting { 142 type filter hook prerouting priority -64000; policy accept; 143 ct original zone set meta iifname map @iiftozone 144 meta mark set meta iifname map @iiftomark 145 146 tcp flags & (syn|ack) == ack add @inflows { ip saddr . tcp sport . meta iifname . ip daddr . tcp dport counter } 147 add @inflows6 { ip6 saddr . tcp sport . meta iifname . ip6 daddr . tcp dport counter } 148 ip protocol icmp add @inicmp { ip saddr . meta iifname . ip daddr counter } 149 } 150 151 chain nat_postrouting { 152 type nat hook postrouting priority 0; policy accept; 153 ct mark set meta mark meta oifname veth0 masquerade 154 } 155 156 chain mangle_prerouting { 157 type filter hook prerouting priority -100; policy accept; 158 ct direction reply meta mark set ct mark 159 } 160} 161EOF 162if [ "$?" -ne 0 ];then 163 echo "SKIP: Could not add nftables rules" 164 exit $ksft_skip 165fi 166 167( echo add element inet raw iiftomark \{ 168 for i in $(seq 1 $((maxclients-1))); do 169 echo \"veth"$i"\" : "$i", 170 done 171 echo \"veth"$maxclients"\" : "$maxclients" \} 172 echo add element inet raw iiftozone \{ 173 for i in $(seq 1 $((maxclients-1))); do 174 echo \"veth"$i"\" : "$i", 175 done 176 echo \"veth$maxclients\" : $maxclients \} 177) | ip netns exec "$gw" nft -f /dev/stdin 178 179ip netns exec "$gw" sysctl -q net.ipv4.conf.all.forwarding=1 > /dev/null 180ip netns exec "$gw" sysctl -q net.ipv6.conf.all.forwarding=1 > /dev/null 181ip netns exec "$gw" sysctl -q net.ipv4.conf.all.rp_filter=0 >/dev/null 182 183# useful for debugging: allows to use 'ping' from clients to gateway. 184ip netns exec "$gw" sysctl -q net.ipv4.fwmark_reflect=1 > /dev/null 185ip netns exec "$gw" sysctl -q net.ipv6.fwmark_reflect=1 > /dev/null 186 187for i in $(seq 1 "$maxclients"); do 188 cl=$(eval echo \$cl"$i") 189 ip netns exec "$cl" ping -i 0.5 -q -c 3 10.3.0.99 > /dev/null 2>&1 & 190done 191 192wait || ret=1 193 194[ "$ret" -ne 0 ] && "FAIL: Ping failure from $cl" 1>&2 195 196for i in $(seq 1 "$maxclients"); do 197 if ! ip netns exec "$gw" nft get element inet raw inicmp "{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 }" | grep -q "{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 counter packets 3 bytes 252 }"; then 198 ret=1 199 echo "FAIL: counter icmp mismatch for veth$i" 1>&2 200 ip netns exec "$gw" nft get element inet raw inicmp "{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 }" 1>&2 201 break 202 fi 203done 204 205if ! ip netns exec "$gw" nft get element inet raw inicmp "{ 10.3.0.99 . \"veth0\" . 10.3.0.1 }" | grep -q "{ 10.3.0.99 . \"veth0\" . 10.3.0.1 counter packets $((3 * maxclients)) bytes $((252 * maxclients)) }"; then 206 ret=1 207 echo "FAIL: counter icmp mismatch for veth0: { 10.3.0.99 . \"veth0\" . 10.3.0.1 counter packets $((3 * maxclients)) bytes $((252 * maxclients)) }" 208 ip netns exec "$gw" nft get element inet raw inicmp "{ 10.3.99 . \"veth0\" . 10.3.0.1 }" 1>&2 209fi 210 211if [ $ret -eq 0 ]; then 212 echo "PASS: ping test from all $maxclients namespaces" 213fi 214 215if [ $have_socat -eq 0 ];then 216 echo "SKIP: socat not installed" 217 if [ $ret -ne 0 ];then 218 exit $ret 219 fi 220 exit $ksft_skip 221fi 222 223listener_ready() 224{ 225 ss -N "$1" -lnt -o "sport = :5201" | grep -q 5201 226} 227 228ip netns exec "$srv" socat -u TCP-LISTEN:5201,fork STDOUT > /dev/null 2>/dev/null & 229socatpid=$! 230 231busywait 1000 listener_ready "$srv" 232 233for i in $(seq 1 "$maxclients"); do 234 if [ $ret -ne 0 ]; then 235 break 236 fi 237 cl=$(eval echo \$cl"$i") 238 if ! ip netns exec "$cl" socat -4 -u STDIN TCP:10.3.0.99:5201,sourceport=10000 < /dev/null > /dev/null; then 239 echo "FAIL: Failure to connect for $cl" 1>&2 240 ip netns exec "$gw" conntrack -S 1>&2 241 ret=1 242 fi 243done 244if [ $ret -eq 0 ];then 245 echo "PASS: socat connections for all $maxclients net namespaces" 246fi 247 248kill $socatpid 249wait 250 251for i in $(seq 1 "$maxclients"); do 252 if ! ip netns exec "$gw" nft get element inet raw inflows "{ 10.1.0.3 . 10000 . \"veth$i\" . 10.3.0.99 . 5201 }" > /dev/null;then 253 ret=1 254 echo "FAIL: can't find expected tcp entry for veth$i" 1>&2 255 break 256 fi 257done 258if [ $ret -eq 0 ];then 259 echo "PASS: Found client connection for all $maxclients net namespaces" 260fi 261 262if ! ip netns exec "$gw" nft get element inet raw inflows "{ 10.3.0.99 . 5201 . \"veth0\" . 10.3.0.1 . 10000 }" > /dev/null;then 263 ret=1 264 echo "FAIL: cannot find return entry on veth0" 1>&2 265fi 266 267exit $ret 268