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 jump nfq 117 } 118 chain post { 119 type filter hook postrouting priority $prio; policy accept; 120 jump nfq 121 } 122} 123EOF 124} 125 126load_counter_ruleset() { 127 local prio=$1 128 129ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 130table inet countrules { 131 chain pre { 132 type filter hook prerouting priority $prio; policy accept; 133 counter 134 } 135 chain input { 136 type filter hook input priority $prio; policy accept; 137 counter 138 } 139 chain forward { 140 type filter hook forward priority $prio; policy accept; 141 counter 142 } 143 chain output { 144 type filter hook output priority $prio; policy accept; 145 counter 146 } 147 chain post { 148 type filter hook postrouting priority $prio; policy accept; 149 counter 150 } 151} 152EOF 153} 154 155test_ping() { 156 ip netns exec ${ns1} ping -c 1 -q 10.0.2.99 > /dev/null 157 if [ $? -ne 0 ];then 158 return 1 159 fi 160 161 ip netns exec ${ns1} ping -c 1 -q dead:2::99 > /dev/null 162 if [ $? -ne 0 ];then 163 return 1 164 fi 165 166 return 0 167} 168 169test_ping_router() { 170 ip netns exec ${ns1} ping -c 1 -q 10.0.2.1 > /dev/null 171 if [ $? -ne 0 ];then 172 return 1 173 fi 174 175 ip netns exec ${ns1} ping -c 1 -q dead:2::1 > /dev/null 176 if [ $? -ne 0 ];then 177 return 1 178 fi 179 180 return 0 181} 182 183test_queue_blackhole() { 184 local proto=$1 185 186ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 187table $proto blackh { 188 chain forward { 189 type filter hook forward priority 0; policy accept; 190 queue num 600 191 } 192} 193EOF 194 if [ $proto = "ip" ] ;then 195 ip netns exec ${ns1} ping -W 2 -c 1 -q 10.0.2.99 > /dev/null 196 lret=$? 197 elif [ $proto = "ip6" ]; then 198 ip netns exec ${ns1} ping -W 2 -c 1 -q dead:2::99 > /dev/null 199 lret=$? 200 else 201 lret=111 202 fi 203 204 # queue without bypass keyword should drop traffic if no listener exists. 205 if [ $lret -eq 0 ];then 206 echo "FAIL: $proto expected failure, got $lret" 1>&2 207 exit 1 208 fi 209 210 ip netns exec ${nsrouter} nft delete table $proto blackh 211 if [ $? -ne 0 ] ;then 212 echo "FAIL: $proto: Could not delete blackh table" 213 exit 1 214 fi 215 216 echo "PASS: $proto: statement with no listener results in packet drop" 217} 218 219test_queue() 220{ 221 local expected=$1 222 local last="" 223 224 # spawn nf-queue listeners 225 ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t $timeout > "$TMPFILE0" & 226 ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE1" & 227 sleep 1 228 test_ping 229 ret=$? 230 if [ $ret -ne 0 ];then 231 echo "FAIL: netns routing/connectivity with active listener on queue $queue: $ret" 1>&2 232 exit $ret 233 fi 234 235 test_ping_router 236 ret=$? 237 if [ $ret -ne 0 ];then 238 echo "FAIL: netns router unreachable listener on queue $queue: $ret" 1>&2 239 exit $ret 240 fi 241 242 wait 243 ret=$? 244 245 for file in $TMPFILE0 $TMPFILE1; do 246 last=$(tail -n1 "$file") 247 if [ x"$last" != x"$expected packets total" ]; then 248 echo "FAIL: Expected $expected packets total, but got $last" 1>&2 249 cat "$file" 1>&2 250 251 ip netns exec ${nsrouter} nft list ruleset 252 exit 1 253 fi 254 done 255 256 echo "PASS: Expected and received $last" 257} 258 259test_tcp_forward() 260{ 261 ip netns exec ${nsrouter} ./nf-queue -q 2 -t $timeout & 262 local nfqpid=$! 263 264 tmpfile=$(mktemp) || exit 1 265 dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 266 ip netns exec ${ns2} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 267 local rpid=$! 268 269 sleep 1 270 ip netns exec ${ns1} nc -w 5 10.0.2.99 12345 <"$tmpfile" >/dev/null & 271 272 rm -f "$tmpfile" 273 274 wait $rpid 275 wait $lpid 276 [ $? -eq 0 ] && echo "PASS: tcp and nfqueue in forward chain" 277} 278 279test_tcp_localhost() 280{ 281 tmpfile=$(mktemp) || exit 1 282 283 dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 284 ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 285 local rpid=$! 286 287 ip netns exec ${nsrouter} ./nf-queue -q 3 -t $timeout & 288 local nfqpid=$! 289 290 sleep 1 291 ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null 292 rm -f "$tmpfile" 293 294 wait $rpid 295 [ $? -eq 0 ] && echo "PASS: tcp via loopback" 296 wait 2>/dev/null 297} 298 299test_tcp_localhost_requeue() 300{ 301ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 302flush ruleset 303table inet filter { 304 chain output { 305 type filter hook output priority 0; policy accept; 306 tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0 307 } 308 chain post { 309 type filter hook postrouting priority 0; policy accept; 310 tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0 311 } 312} 313EOF 314 tmpfile=$(mktemp) || exit 1 315 dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 316 ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 317 local rpid=$! 318 319 ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE2" & 320 321 # nfqueue 1 will be called via output hook. But this time, 322 # re-queue the packet to nfqueue program on queue 2. 323 ip netns exec ${nsrouter} ./nf-queue -G -d 150 -c -q 0 -Q 1 -t $timeout > "$TMPFILE3" & 324 325 sleep 1 326 ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null 327 rm -f "$tmpfile" 328 329 wait 330 331 if ! diff -u "$TMPFILE2" "$TMPFILE3" ; then 332 echo "FAIL: lost packets during requeue?!" 1>&2 333 return 334 fi 335 336 echo "PASS: tcp via loopback and re-queueing" 337} 338 339test_icmp_vrf() { 340 ip -net $ns1 link add tvrf type vrf table 9876 341 if [ $? -ne 0 ];then 342 echo "SKIP: Could not add vrf device" 343 return 344 fi 345 346 ip -net $ns1 li set eth0 master tvrf 347 ip -net $ns1 li set tvrf up 348 349 ip -net $ns1 route add 10.0.2.0/24 via 10.0.1.1 dev eth0 table 9876 350ip netns exec ${ns1} nft -f /dev/stdin <<EOF 351flush ruleset 352table inet filter { 353 chain output { 354 type filter hook output priority 0; policy accept; 355 meta oifname "tvrf" icmp type echo-request counter queue num 1 356 meta oifname "eth0" icmp type echo-request counter queue num 1 357 } 358 chain post { 359 type filter hook postrouting priority 0; policy accept; 360 meta oifname "tvrf" icmp type echo-request counter queue num 1 361 meta oifname "eth0" icmp type echo-request counter queue num 1 362 } 363} 364EOF 365 ip netns exec ${ns1} ./nf-queue -q 1 -t $timeout & 366 local nfqpid=$! 367 368 sleep 1 369 ip netns exec ${ns1} ip vrf exec tvrf ping -c 1 10.0.2.99 > /dev/null 370 371 for n in output post; do 372 for d in tvrf eth0; do 373 ip netns exec ${ns1} nft list chain inet filter $n | grep -q "oifname \"$d\" icmp type echo-request counter packets 1" 374 if [ $? -ne 0 ] ; then 375 echo "FAIL: chain $n: icmp packet counter mismatch for device $d" 1>&2 376 ip netns exec ${ns1} nft list ruleset 377 ret=1 378 return 379 fi 380 done 381 done 382 383 wait $nfqpid 384 [ $? -eq 0 ] && echo "PASS: icmp+nfqueue via vrf" 385 wait 2>/dev/null 386} 387 388ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null 389ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null 390ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null 391 392load_ruleset "filter" 0 393 394sleep 3 395 396test_ping 397ret=$? 398if [ $ret -eq 0 ];then 399 # queue bypass works (rules were skipped, no listener) 400 echo "PASS: ${ns1} can reach ${ns2}" 401else 402 echo "FAIL: ${ns1} cannot reach ${ns2}: $ret" 1>&2 403 exit $ret 404fi 405 406test_queue_blackhole ip 407test_queue_blackhole ip6 408 409# dummy ruleset to add base chains between the 410# queueing rules. We don't want the second reinject 411# to re-execute the old hooks. 412load_counter_ruleset 10 413 414# we are hooking all: prerouting/input/forward/output/postrouting. 415# we ping ${ns2} from ${ns1} via ${nsrouter} using ipv4 and ipv6, so: 416# 1x icmp prerouting,forward,postrouting -> 3 queue events (6 incl. reply). 417# 1x icmp prerouting,input,output postrouting -> 4 queue events incl. reply. 418# so we expect that userspace program receives 10 packets. 419test_queue 10 420 421# same. We queue to a second program as well. 422load_ruleset "filter2" 20 423test_queue 20 424 425test_tcp_forward 426test_tcp_localhost 427test_tcp_localhost_requeue 428test_icmp_vrf 429 430exit $ret 431