1#!/bin/bash 2# 3# This tests nf_queue: 4# 1. can process packets from all hooks 5# 2. support running nfqueue from more than one base chain 6# 7# Kselftest framework requirement - SKIP code is 4. 8ksft_skip=4 9ret=0 10 11sfx=$(mktemp -u "XXXXXXXX") 12ns1="ns1-$sfx" 13ns2="ns2-$sfx" 14nsrouter="nsrouter-$sfx" 15timeout=4 16 17cleanup() 18{ 19 ip netns pids ${ns1} | xargs kill 2>/dev/null 20 ip netns pids ${ns2} | xargs kill 2>/dev/null 21 ip netns pids ${nsrouter} | xargs kill 2>/dev/null 22 23 ip netns del ${ns1} 24 ip netns del ${ns2} 25 ip netns del ${nsrouter} 26 rm -f "$TMPFILE0" 27 rm -f "$TMPFILE1" 28 rm -f "$TMPFILE2" "$TMPFILE3" 29} 30 31nft --version > /dev/null 2>&1 32if [ $? -ne 0 ];then 33 echo "SKIP: Could not run test without nft tool" 34 exit $ksft_skip 35fi 36 37ip -Version > /dev/null 2>&1 38if [ $? -ne 0 ];then 39 echo "SKIP: Could not run test without ip tool" 40 exit $ksft_skip 41fi 42 43ip netns add ${nsrouter} 44if [ $? -ne 0 ];then 45 echo "SKIP: Could not create net namespace" 46 exit $ksft_skip 47fi 48 49TMPFILE0=$(mktemp) 50TMPFILE1=$(mktemp) 51TMPFILE2=$(mktemp) 52TMPFILE3=$(mktemp) 53trap cleanup EXIT 54 55ip netns add ${ns1} 56ip netns add ${ns2} 57 58ip link add veth0 netns ${nsrouter} type veth peer name eth0 netns ${ns1} > /dev/null 2>&1 59if [ $? -ne 0 ];then 60 echo "SKIP: No virtual ethernet pair device support in kernel" 61 exit $ksft_skip 62fi 63ip link add veth1 netns ${nsrouter} type veth peer name eth0 netns ${ns2} 64 65ip -net ${nsrouter} link set lo up 66ip -net ${nsrouter} link set veth0 up 67ip -net ${nsrouter} addr add 10.0.1.1/24 dev veth0 68ip -net ${nsrouter} addr add dead:1::1/64 dev veth0 69 70ip -net ${nsrouter} link set veth1 up 71ip -net ${nsrouter} addr add 10.0.2.1/24 dev veth1 72ip -net ${nsrouter} addr add dead:2::1/64 dev veth1 73 74ip -net ${ns1} link set lo up 75ip -net ${ns1} link set eth0 up 76 77ip -net ${ns2} link set lo up 78ip -net ${ns2} link set eth0 up 79 80ip -net ${ns1} addr add 10.0.1.99/24 dev eth0 81ip -net ${ns1} addr add dead:1::99/64 dev eth0 82ip -net ${ns1} route add default via 10.0.1.1 83ip -net ${ns1} route add default via dead:1::1 84 85ip -net ${ns2} addr add 10.0.2.99/24 dev eth0 86ip -net ${ns2} addr add dead:2::99/64 dev eth0 87ip -net ${ns2} route add default via 10.0.2.1 88ip -net ${ns2} route add default via dead:2::1 89 90load_ruleset() { 91 local name=$1 92 local prio=$2 93 94ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 95table inet $name { 96 chain nfq { 97 ip protocol icmp queue bypass 98 icmpv6 type { "echo-request", "echo-reply" } queue num 1 bypass 99 } 100 chain pre { 101 type filter hook prerouting priority $prio; policy accept; 102 jump nfq 103 } 104 chain input { 105 type filter hook input priority $prio; policy accept; 106 jump nfq 107 } 108 chain forward { 109 type filter hook forward priority $prio; policy accept; 110 tcp dport 12345 queue num 2 111 jump nfq 112 } 113 chain output { 114 type filter hook output priority $prio; policy accept; 115 tcp dport 12345 queue num 3 116 tcp sport 23456 queue num 3 117 jump nfq 118 } 119 chain post { 120 type filter hook postrouting priority $prio; policy accept; 121 jump nfq 122 } 123} 124EOF 125} 126 127load_counter_ruleset() { 128 local prio=$1 129 130ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 131table inet countrules { 132 chain pre { 133 type filter hook prerouting priority $prio; policy accept; 134 counter 135 } 136 chain input { 137 type filter hook input priority $prio; policy accept; 138 counter 139 } 140 chain forward { 141 type filter hook forward priority $prio; policy accept; 142 counter 143 } 144 chain output { 145 type filter hook output priority $prio; policy accept; 146 counter 147 } 148 chain post { 149 type filter hook postrouting priority $prio; policy accept; 150 counter 151 } 152} 153EOF 154} 155 156test_ping() { 157 ip netns exec ${ns1} ping -c 1 -q 10.0.2.99 > /dev/null 158 if [ $? -ne 0 ];then 159 return 1 160 fi 161 162 ip netns exec ${ns1} ping -c 1 -q dead:2::99 > /dev/null 163 if [ $? -ne 0 ];then 164 return 1 165 fi 166 167 return 0 168} 169 170test_ping_router() { 171 ip netns exec ${ns1} ping -c 1 -q 10.0.2.1 > /dev/null 172 if [ $? -ne 0 ];then 173 return 1 174 fi 175 176 ip netns exec ${ns1} ping -c 1 -q dead:2::1 > /dev/null 177 if [ $? -ne 0 ];then 178 return 1 179 fi 180 181 return 0 182} 183 184test_queue_blackhole() { 185 local proto=$1 186 187ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 188table $proto blackh { 189 chain forward { 190 type filter hook forward priority 0; policy accept; 191 queue num 600 192 } 193} 194EOF 195 if [ $proto = "ip" ] ;then 196 ip netns exec ${ns1} ping -W 2 -c 1 -q 10.0.2.99 > /dev/null 197 lret=$? 198 elif [ $proto = "ip6" ]; then 199 ip netns exec ${ns1} ping -W 2 -c 1 -q dead:2::99 > /dev/null 200 lret=$? 201 else 202 lret=111 203 fi 204 205 # queue without bypass keyword should drop traffic if no listener exists. 206 if [ $lret -eq 0 ];then 207 echo "FAIL: $proto expected failure, got $lret" 1>&2 208 exit 1 209 fi 210 211 ip netns exec ${nsrouter} nft delete table $proto blackh 212 if [ $? -ne 0 ] ;then 213 echo "FAIL: $proto: Could not delete blackh table" 214 exit 1 215 fi 216 217 echo "PASS: $proto: statement with no listener results in packet drop" 218} 219 220test_queue() 221{ 222 local expected=$1 223 local last="" 224 225 # spawn nf-queue listeners 226 ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t $timeout > "$TMPFILE0" & 227 ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE1" & 228 sleep 1 229 test_ping 230 ret=$? 231 if [ $ret -ne 0 ];then 232 echo "FAIL: netns routing/connectivity with active listener on queue $queue: $ret" 1>&2 233 exit $ret 234 fi 235 236 test_ping_router 237 ret=$? 238 if [ $ret -ne 0 ];then 239 echo "FAIL: netns router unreachable listener on queue $queue: $ret" 1>&2 240 exit $ret 241 fi 242 243 wait 244 ret=$? 245 246 for file in $TMPFILE0 $TMPFILE1; do 247 last=$(tail -n1 "$file") 248 if [ x"$last" != x"$expected packets total" ]; then 249 echo "FAIL: Expected $expected packets total, but got $last" 1>&2 250 cat "$file" 1>&2 251 252 ip netns exec ${nsrouter} nft list ruleset 253 exit 1 254 fi 255 done 256 257 echo "PASS: Expected and received $last" 258} 259 260test_tcp_forward() 261{ 262 ip netns exec ${nsrouter} ./nf-queue -q 2 -t $timeout & 263 local nfqpid=$! 264 265 tmpfile=$(mktemp) || exit 1 266 dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 267 ip netns exec ${ns2} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 268 local rpid=$! 269 270 sleep 1 271 ip netns exec ${ns1} nc -w 5 10.0.2.99 12345 <"$tmpfile" >/dev/null & 272 273 rm -f "$tmpfile" 274 275 wait $rpid 276 wait $lpid 277 [ $? -eq 0 ] && echo "PASS: tcp and nfqueue in forward chain" 278} 279 280test_tcp_localhost() 281{ 282 tmpfile=$(mktemp) || exit 1 283 284 dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 285 ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 286 local rpid=$! 287 288 ip netns exec ${nsrouter} ./nf-queue -q 3 -t $timeout & 289 local nfqpid=$! 290 291 sleep 1 292 ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null 293 rm -f "$tmpfile" 294 295 wait $rpid 296 [ $? -eq 0 ] && echo "PASS: tcp via loopback" 297 wait 2>/dev/null 298} 299 300test_tcp_localhost_connectclose() 301{ 302 tmpfile=$(mktemp) || exit 1 303 304 ip netns exec ${nsrouter} ./connect_close -p 23456 -t $timeout & 305 306 ip netns exec ${nsrouter} ./nf-queue -q 3 -t $timeout & 307 local nfqpid=$! 308 309 sleep 1 310 rm -f "$tmpfile" 311 312 wait $rpid 313 [ $? -eq 0 ] && echo "PASS: tcp via loopback with connect/close" 314 wait 2>/dev/null 315} 316 317test_tcp_localhost_requeue() 318{ 319ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 320flush ruleset 321table inet filter { 322 chain output { 323 type filter hook output priority 0; policy accept; 324 tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0 325 } 326 chain post { 327 type filter hook postrouting priority 0; policy accept; 328 tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0 329 } 330} 331EOF 332 tmpfile=$(mktemp) || exit 1 333 dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 334 ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 335 local rpid=$! 336 337 ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE2" & 338 339 # nfqueue 1 will be called via output hook. But this time, 340 # re-queue the packet to nfqueue program on queue 2. 341 ip netns exec ${nsrouter} ./nf-queue -G -d 150 -c -q 0 -Q 1 -t $timeout > "$TMPFILE3" & 342 343 sleep 1 344 ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null 345 rm -f "$tmpfile" 346 347 wait 348 349 if ! diff -u "$TMPFILE2" "$TMPFILE3" ; then 350 echo "FAIL: lost packets during requeue?!" 1>&2 351 return 352 fi 353 354 echo "PASS: tcp via loopback and re-queueing" 355} 356 357test_icmp_vrf() { 358 ip -net $ns1 link add tvrf type vrf table 9876 359 if [ $? -ne 0 ];then 360 echo "SKIP: Could not add vrf device" 361 return 362 fi 363 364 ip -net $ns1 li set eth0 master tvrf 365 ip -net $ns1 li set tvrf up 366 367 ip -net $ns1 route add 10.0.2.0/24 via 10.0.1.1 dev eth0 table 9876 368ip netns exec ${ns1} nft -f /dev/stdin <<EOF 369flush ruleset 370table inet filter { 371 chain output { 372 type filter hook output priority 0; policy accept; 373 meta oifname "tvrf" icmp type echo-request counter queue num 1 374 meta oifname "eth0" icmp type echo-request counter queue num 1 375 } 376 chain post { 377 type filter hook postrouting priority 0; policy accept; 378 meta oifname "tvrf" icmp type echo-request counter queue num 1 379 meta oifname "eth0" icmp type echo-request counter queue num 1 380 } 381} 382EOF 383 ip netns exec ${ns1} ./nf-queue -q 1 -t $timeout & 384 local nfqpid=$! 385 386 sleep 1 387 ip netns exec ${ns1} ip vrf exec tvrf ping -c 1 10.0.2.99 > /dev/null 388 389 for n in output post; do 390 for d in tvrf eth0; do 391 ip netns exec ${ns1} nft list chain inet filter $n | grep -q "oifname \"$d\" icmp type echo-request counter packets 1" 392 if [ $? -ne 0 ] ; then 393 echo "FAIL: chain $n: icmp packet counter mismatch for device $d" 1>&2 394 ip netns exec ${ns1} nft list ruleset 395 ret=1 396 return 397 fi 398 done 399 done 400 401 wait $nfqpid 402 [ $? -eq 0 ] && echo "PASS: icmp+nfqueue via vrf" 403 wait 2>/dev/null 404} 405 406ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null 407ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null 408ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null 409 410load_ruleset "filter" 0 411 412sleep 3 413 414test_ping 415ret=$? 416if [ $ret -eq 0 ];then 417 # queue bypass works (rules were skipped, no listener) 418 echo "PASS: ${ns1} can reach ${ns2}" 419else 420 echo "FAIL: ${ns1} cannot reach ${ns2}: $ret" 1>&2 421 exit $ret 422fi 423 424test_queue_blackhole ip 425test_queue_blackhole ip6 426 427# dummy ruleset to add base chains between the 428# queueing rules. We don't want the second reinject 429# to re-execute the old hooks. 430load_counter_ruleset 10 431 432# we are hooking all: prerouting/input/forward/output/postrouting. 433# we ping ${ns2} from ${ns1} via ${nsrouter} using ipv4 and ipv6, so: 434# 1x icmp prerouting,forward,postrouting -> 3 queue events (6 incl. reply). 435# 1x icmp prerouting,input,output postrouting -> 4 queue events incl. reply. 436# so we expect that userspace program receives 10 packets. 437test_queue 10 438 439# same. We queue to a second program as well. 440load_ruleset "filter2" 20 441test_queue 20 442 443test_tcp_forward 444test_tcp_localhost 445test_tcp_localhost_connectclose 446test_tcp_localhost_requeue 447test_icmp_vrf 448 449exit $ret 450