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