1#!/bin/bash 2# 3# This tests tproxy on the following scenario: 4# 5# +------------+ 6# +-------+ | nsrouter | +-------+ 7# |ns1 |.99 .1| |.1 .99| ns2| 8# | eth0|---------------|veth0 veth1|------------------|eth0 | 9# | | 10.0.1.0/24 | | 10.0.2.0/24 | | 10# +-------+ dead:1::/64 | veth2 | dead:2::/64 +-------+ 11# +------------+ 12# |.1 13# | 14# | 15# | +-------+ 16# | .99| ns3| 17# +------------------------|eth0 | 18# 10.0.3.0/24 | | 19# dead:3::/64 +-------+ 20# 21# The tproxy implementation acts as an echo server so the client 22# must receive the same message it sent if it has been proxied. 23# If is not proxied the servers return PONG_NS# with the number 24# of the namespace the server is running. 25# shellcheck disable=SC2162,SC2317 26 27source lib.sh 28ret=0 29# UDP is slow 30timeout=15 31 32cleanup() 33{ 34 ip netns pids "$ns1" | xargs kill 2>/dev/null 35 ip netns pids "$ns2" | xargs kill 2>/dev/null 36 ip netns pids "$ns3" | xargs kill 2>/dev/null 37 ip netns pids "$nsrouter" | xargs kill 2>/dev/null 38 39 cleanup_all_ns 40} 41 42checktool "nft --version" "test without nft tool" 43checktool "socat -h" "run test without socat" 44 45trap cleanup EXIT 46setup_ns ns1 ns2 ns3 nsrouter 47 48if ! ip link add veth0 netns "$nsrouter" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1; then 49 echo "SKIP: No virtual ethernet pair device support in kernel" 50 exit $ksft_skip 51fi 52ip link add veth1 netns "$nsrouter" type veth peer name eth0 netns "$ns2" 53ip link add veth2 netns "$nsrouter" type veth peer name eth0 netns "$ns3" 54 55ip -net "$nsrouter" link set veth0 up 56ip -net "$nsrouter" addr add 10.0.1.1/24 dev veth0 57ip -net "$nsrouter" addr add dead:1::1/64 dev veth0 nodad 58 59ip -net "$nsrouter" link set veth1 up 60ip -net "$nsrouter" addr add 10.0.2.1/24 dev veth1 61ip -net "$nsrouter" addr add dead:2::1/64 dev veth1 nodad 62 63ip -net "$nsrouter" link set veth2 up 64ip -net "$nsrouter" addr add 10.0.3.1/24 dev veth2 65ip -net "$nsrouter" addr add dead:3::1/64 dev veth2 nodad 66 67ip -net "$ns1" link set eth0 up 68ip -net "$ns2" link set eth0 up 69ip -net "$ns3" link set eth0 up 70 71ip -net "$ns1" addr add 10.0.1.99/24 dev eth0 72ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad 73ip -net "$ns1" route add default via 10.0.1.1 74ip -net "$ns1" route add default via dead:1::1 75 76ip -net "$ns2" addr add 10.0.2.99/24 dev eth0 77ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad 78ip -net "$ns2" route add default via 10.0.2.1 79ip -net "$ns2" route add default via dead:2::1 80 81ip -net "$ns3" addr add 10.0.3.99/24 dev eth0 82ip -net "$ns3" addr add dead:3::99/64 dev eth0 nodad 83ip -net "$ns3" route add default via 10.0.3.1 84ip -net "$ns3" route add default via dead:3::1 85 86ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null 87ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null 88ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null 89ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth2.forwarding=1 > /dev/null 90 91test_ping() { 92 if ! ip netns exec "$ns1" ping -c 1 -q 10.0.2.99 > /dev/null; then 93 return 1 94 fi 95 96 if ! ip netns exec "$ns1" ping -c 1 -q dead:2::99 > /dev/null; then 97 return 2 98 fi 99 100 if ! ip netns exec "$ns1" ping -c 1 -q 10.0.3.99 > /dev/null; then 101 return 1 102 fi 103 104 if ! ip netns exec "$ns1" ping -c 1 -q dead:3::99 > /dev/null; then 105 return 2 106 fi 107 108 return 0 109} 110 111test_ping_router() { 112 if ! ip netns exec "$ns1" ping -c 1 -q 10.0.2.1 > /dev/null; then 113 return 3 114 fi 115 116 if ! ip netns exec "$ns1" ping -c 1 -q dead:2::1 > /dev/null; then 117 return 4 118 fi 119 120 return 0 121} 122 123 124listener_ready() 125{ 126 local ns="$1" 127 local port="$2" 128 local proto="$3" 129 ss -N "$ns" -ln "$proto" -o "sport = :$port" | grep -q "$port" 130} 131 132test_tproxy_udp_forward() 133{ 134 local ip_proto="$1" 135 136 local expect_ns1_ns2="I_M_PROXIED" 137 local expect_ns1_ns3="PONG_NS3" 138 local expect_nsrouter_ns2="PONG_NS2" 139 local expect_nsrouter_ns3="PONG_NS3" 140 141 # derived variables 142 local testname="test_${ip_proto}_udp_forward" 143 local socat_ipproto 144 local ns1_ip 145 local ns2_ip 146 local ns3_ip 147 local ns1_ip_port 148 local ns2_ip_port 149 local ns3_ip_port 150 local ip_command 151 152 # socat 1.8.0 has a bug that requires to specify the IP family to bind (fixed in 1.8.0.1) 153 case $ip_proto in 154 "ip") 155 socat_ipproto="-4" 156 ns1_ip=10.0.1.99 157 ns2_ip=10.0.2.99 158 ns3_ip=10.0.3.99 159 ns1_ip_port="$ns1_ip:18888" 160 ns2_ip_port="$ns2_ip:8080" 161 ns3_ip_port="$ns3_ip:8080" 162 ip_command="ip" 163 ;; 164 "ip6") 165 socat_ipproto="-6" 166 ns1_ip=dead:1::99 167 ns2_ip=dead:2::99 168 ns3_ip=dead:3::99 169 ns1_ip_port="[$ns1_ip]:18888" 170 ns2_ip_port="[$ns2_ip]:8080" 171 ns3_ip_port="[$ns3_ip]:8080" 172 ip_command="ip -6" 173 ;; 174 *) 175 echo "FAIL: unsupported protocol" 176 exit 255 177 ;; 178 esac 179 180 # shellcheck disable=SC2046 # Intended splitting of ip_command 181 ip netns exec "$nsrouter" $ip_command rule add fwmark 1 table 100 182 ip netns exec "$nsrouter" $ip_command route add local "$ns2_ip" dev lo table 100 183 ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF 184flush ruleset 185table inet filter { 186 chain divert { 187 type filter hook prerouting priority 0; policy accept; 188 $ip_proto daddr $ns2_ip udp dport 8080 tproxy $ip_proto to :12345 meta mark set 1 accept 189 } 190} 191EOF 192 193 timeout "$timeout" ip netns exec "$nsrouter" socat -u "$socat_ipproto" udp-listen:12345,fork,ip-transparent,reuseport udp:"$ns1_ip_port",ip-transparent,reuseport,bind="$ns2_ip_port" 2>/dev/null & 194 local tproxy_pid=$! 195 196 timeout "$timeout" ip netns exec "$ns2" socat "$socat_ipproto" udp-listen:8080,fork SYSTEM:"echo PONG_NS2" 2>/dev/null & 197 local server2_pid=$! 198 199 timeout "$timeout" ip netns exec "$ns3" socat "$socat_ipproto" udp-listen:8080,fork SYSTEM:"echo PONG_NS3" 2>/dev/null & 200 local server3_pid=$! 201 202 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$nsrouter" 12345 "-u" 203 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$ns2" 8080 "-u" 204 busywait "$BUSYWAIT_TIMEOUT" listener_ready "$ns3" 8080 "-u" 205 206 local result 207 # request from ns1 to ns2 (forwarded traffic) 208 result=$(echo I_M_PROXIED | ip netns exec "$ns1" socat -t 2 -T 2 STDIO udp:"$ns2_ip_port",sourceport=18888) 209 if [ "$result" == "$expect_ns1_ns2" ] ;then 210 echo "PASS: tproxy test $testname: ns1 got reply \"$result\" connecting to ns2" 211 else 212 echo "ERROR: tproxy test $testname: ns1 got reply \"$result\" connecting to ns2, not \"${expect_ns1_ns2}\" as intended" 213 ret=1 214 fi 215 216 # request from ns1 to ns3 (forwarded traffic) 217 result=$(echo I_M_PROXIED | ip netns exec "$ns1" socat -t 2 -T 2 STDIO udp:"$ns3_ip_port") 218 if [ "$result" = "$expect_ns1_ns3" ] ;then 219 echo "PASS: tproxy test $testname: ns1 got reply \"$result\" connecting to ns3" 220 else 221 echo "ERROR: tproxy test $testname: ns1 got reply \"$result\" connecting to ns3, not \"$expect_ns1_ns3\" as intended" 222 ret=1 223 fi 224 225 # request from nsrouter to ns2 (localy originated traffic) 226 result=$(echo I_M_PROXIED | ip netns exec "$nsrouter" socat -t 2 -T 2 STDIO udp:"$ns2_ip_port") 227 if [ "$result" == "$expect_nsrouter_ns2" ] ;then 228 echo "PASS: tproxy test $testname: nsrouter got reply \"$result\" connecting to ns2" 229 else 230 echo "ERROR: tproxy test $testname: nsrouter got reply \"$result\" connecting to ns2, not \"$expect_nsrouter_ns2\" as intended" 231 ret=1 232 fi 233 234 # request from nsrouter to ns3 (localy originated traffic) 235 result=$(echo I_M_PROXIED | ip netns exec "$nsrouter" socat -t 2 -T 2 STDIO udp:"$ns3_ip_port") 236 if [ "$result" = "$expect_nsrouter_ns3" ] ;then 237 echo "PASS: tproxy test $testname: nsrouter got reply \"$result\" connecting to ns3" 238 else 239 echo "ERROR: tproxy test $testname: nsrouter got reply \"$result\" connecting to ns3, not \"$expect_nsrouter_ns3\" as intended" 240 ret=1 241 fi 242 243 # cleanup 244 kill "$tproxy_pid" "$server2_pid" "$server3_pid" 2>/dev/null 245 # shellcheck disable=SC2046 # Intended splitting of ip_command 246 ip netns exec "$nsrouter" $ip_command rule del fwmark 1 table 100 247 ip netns exec "$nsrouter" $ip_command route flush table 100 248} 249 250 251if test_ping; then 252 # queue bypass works (rules were skipped, no listener) 253 echo "PASS: ${ns1} can reach ${ns2}" 254else 255 echo "FAIL: ${ns1} cannot reach ${ns2}: $ret" 1>&2 256 exit $ret 257fi 258 259test_tproxy_udp_forward "ip" 260test_tproxy_udp_forward "ip6" 261 262exit $ret 263