1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4source lib.sh 5 6clash_resolution_active=0 7dport=22111 8ret=0 9 10cleanup() 11{ 12 # netns cleanup also zaps any remaining socat echo server. 13 cleanup_all_ns 14} 15 16checktool "nft --version" "run test without nft" 17checktool "conntrack --version" "run test without conntrack" 18checktool "socat -h" "run test without socat" 19 20trap cleanup EXIT 21 22setup_ns nsclient1 nsclient2 nsrouter 23 24ip netns exec "$nsrouter" nft -f -<<EOF 25table ip t { 26 chain lb { 27 meta l4proto udp dnat to numgen random mod 3 map { 0 : 10.0.2.1 . 9000, 1 : 10.0.2.1 . 9001, 2 : 10.0.2.1 . 9002 } 28 } 29 30 chain prerouting { 31 type nat hook prerouting priority dstnat 32 33 udp dport $dport counter jump lb 34 } 35 36 chain output { 37 type nat hook output priority dstnat 38 39 udp dport $dport counter jump lb 40 } 41} 42EOF 43 44load_simple_ruleset() 45{ 46ip netns exec "$1" nft -f -<<EOF 47table ip t { 48 chain forward { 49 type filter hook forward priority 0 50 51 ct state new counter 52 } 53} 54EOF 55} 56 57spawn_servers() 58{ 59 local ns="$1" 60 local ports="9000 9001 9002" 61 62 for port in $ports; do 63 ip netns exec "$ns" socat UDP-RECVFROM:$port,fork PIPE 2>/dev/null & 64 done 65 66 for port in $ports; do 67 wait_local_port_listen "$ns" $port udp 68 done 69} 70 71add_addr() 72{ 73 local ns="$1" 74 local dev="$2" 75 local i="$3" 76 local j="$4" 77 78 ip -net "$ns" link set "$dev" up 79 ip -net "$ns" addr add "10.0.$i.$j/24" dev "$dev" 80} 81 82ping_test() 83{ 84 local ns="$1" 85 local daddr="$2" 86 87 if ! ip netns exec "$ns" ping -q -c 1 $daddr > /dev/null;then 88 echo "FAIL: ping from $ns to $daddr" 89 exit 1 90 fi 91} 92 93run_one_clash_test() 94{ 95 local ns="$1" 96 local ctns="$2" 97 local daddr="$3" 98 local dport="$4" 99 local entries 100 local cre 101 102 if ! ip netns exec "$ns" timeout 30 ./udpclash $daddr $dport;then 103 echo "INFO: did not receive expected number of replies for $daddr:$dport" 104 ip netns exec "$ctns" conntrack -S 105 # don't fail: check if clash resolution triggered after all. 106 fi 107 108 entries=$(ip netns exec "$ctns" conntrack -S | wc -l) 109 cre=$(ip netns exec "$ctns" conntrack -S | grep "clash_resolve=0" | wc -l) 110 111 if [ "$cre" -ne "$entries" ];then 112 clash_resolution_active=1 113 return 0 114 fi 115 116 # not a failure: clash resolution logic did not trigger. 117 # With right timing, xmit completed sequentially and 118 # no parallel insertion occurs. 119 return $ksft_skip 120} 121 122run_clash_test() 123{ 124 local ns="$1" 125 local ctns="$2" 126 local daddr="$3" 127 local dport="$4" 128 local softerr=0 129 130 for i in $(seq 1 10);do 131 run_one_clash_test "$ns" "$ctns" "$daddr" "$dport" 132 local rv=$? 133 if [ $rv -eq 0 ];then 134 echo "PASS: clash resolution test for $daddr:$dport on attempt $i" 135 return 0 136 elif [ $rv -eq $ksft_skip ]; then 137 softerr=1 138 fi 139 done 140 141 [ $softerr -eq 1 ] && echo "SKIP: clash resolution for $daddr:$dport did not trigger" 142} 143 144ip link add veth0 netns "$nsclient1" type veth peer name veth0 netns "$nsrouter" 145ip link add veth0 netns "$nsclient2" type veth peer name veth1 netns "$nsrouter" 146add_addr "$nsclient1" veth0 1 1 147add_addr "$nsclient2" veth0 2 1 148add_addr "$nsrouter" veth0 1 99 149add_addr "$nsrouter" veth1 2 99 150 151ip -net "$nsclient1" route add default via 10.0.1.99 152ip -net "$nsclient2" route add default via 10.0.2.99 153ip netns exec "$nsrouter" sysctl -q net.ipv4.ip_forward=1 154 155ping_test "$nsclient1" 10.0.1.99 156ping_test "$nsclient1" 10.0.2.1 157ping_test "$nsclient2" 10.0.1.1 158 159spawn_servers "$nsclient2" 160 161# exercise clash resolution with nat: 162# nsrouter is supposed to dnat to 10.0.2.1:900{0,1,2,3}. 163run_clash_test "$nsclient1" "$nsrouter" 10.0.1.99 "$dport" 164 165# exercise clash resolution without nat. 166load_simple_ruleset "$nsclient2" 167run_clash_test "$nsclient2" "$nsclient2" 127.0.0.1 9001 168 169if [ $clash_resolution_active -eq 0 ];then 170 [ "$ret" -eq 0 ] && ret=$ksft_skip 171 echo "SKIP: Clash resolution did not trigger" 172fi 173 174exit $ret 175