1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4source lib.sh 5 6checktool "conntrack --version" "run test without conntrack" 7checktool "nft --version" "run test without nft tool" 8 9init_net_max=0 10ct_buckets=0 11tmpfile="" 12tmpfile_proc="" 13tmpfile_uniq="" 14ret=0 15have_socat=0 16 17socat -h > /dev/null && have_socat=1 18 19insert_count=2000 20[ "$KSFT_MACHINE_SLOW" = "yes" ] && insert_count=400 21 22modprobe -q nf_conntrack 23if ! sysctl -q net.netfilter.nf_conntrack_max >/dev/null;then 24 echo "SKIP: conntrack sysctls not available" 25 exit $KSFT_SKIP 26fi 27 28init_net_max=$(sysctl -n net.netfilter.nf_conntrack_max) || exit 1 29ct_buckets=$(sysctl -n net.netfilter.nf_conntrack_buckets) || exit 1 30 31cleanup() { 32 cleanup_all_ns 33 34 rm -f "$tmpfile" "$tmpfile_proc" "$tmpfile_uniq" 35 36 # restore original sysctl setting 37 sysctl -q net.netfilter.nf_conntrack_max=$init_net_max 38 sysctl -q net.netfilter.nf_conntrack_buckets=$ct_buckets 39} 40trap cleanup EXIT 41 42check_max_alias() 43{ 44 local expected="$1" 45 # old name, expected to alias to the first, i.e. changing one 46 # changes the other as well. 47 local lv=$(sysctl -n net.nf_conntrack_max) 48 49 if [ $expected -ne "$lv" ];then 50 echo "nf_conntrack_max sysctls should have identical values" 51 exit 1 52 fi 53} 54 55insert_ctnetlink() { 56 local ns="$1" 57 local count="$2" 58 local i=0 59 local bulk=16 60 61 while [ $i -lt $count ] ;do 62 ip netns exec "$ns" bash -c "for i in \$(seq 1 $bulk); do \ 63 if ! conntrack -I -s \$((\$RANDOM%256)).\$((\$RANDOM%256)).\$((\$RANDOM%256)).\$((\$RANDOM%255+1)) \ 64 -d \$((\$RANDOM%256)).\$((\$RANDOM%256)).\$((\$RANDOM%256)).\$((\$RANDOM%255+1)) \ 65 --protonum 17 --timeout 3600 --status ASSURED,SEEN_REPLY --sport \$RANDOM --dport 53; then \ 66 return;\ 67 fi & \ 68 done ; wait" 2>/dev/null 69 70 i=$((i+bulk)) 71 done 72} 73 74check_ctcount() { 75 local ns="$1" 76 local count="$2" 77 local msg="$3" 78 79 local now=$(ip netns exec "$ns" conntrack -C) 80 81 if [ $now -ne "$count" ] ;then 82 echo "expected $count entries in $ns, not $now: $msg" 83 exit 1 84 fi 85 86 echo "PASS: got $count connections: $msg" 87} 88 89ctresize() { 90 local duration="$1" 91 local now=$(date +%s) 92 local end=$((now + duration)) 93 94 while [ $now -lt $end ]; do 95 sysctl -q net.netfilter.nf_conntrack_buckets=$RANDOM 96 now=$(date +%s) 97 done 98} 99 100do_rsleep() { 101 local limit="$1" 102 local r=$RANDOM 103 104 r=$((r%limit)) 105 sleep "$r" 106} 107 108ct_flush_once() { 109 local ns="$1" 110 111 ip netns exec "$ns" conntrack -F 2>/dev/null 112} 113 114ctflush() { 115 local ns="$1" 116 local duration="$2" 117 local now=$(date +%s) 118 local end=$((now + duration)) 119 120 do_rsleep "$duration" 121 122 while [ $now -lt $end ]; do 123 ct_flush_once "$ns" 124 do_rsleep "$duration" 125 now=$(date +%s) 126 done 127} 128 129ct_pingflood() 130{ 131 local ns="$1" 132 local duration="$2" 133 local msg="$3" 134 local now=$(date +%s) 135 local end=$((now + duration)) 136 local j=0 137 local k=0 138 139 while [ $now -lt $end ]; do 140 j=$((j%256)) 141 k=$((k%256)) 142 143 ip netns exec "$ns" bash -c \ 144 "j=$j k=$k; for i in \$(seq 1 254); do ping -q -c 1 127.\$k.\$j.\$i & done; wait" >/dev/null 2>&1 145 146 j=$((j+1)) 147 148 if [ $j -eq 256 ];then 149 k=$((k+1)) 150 fi 151 152 now=$(date +%s) 153 done 154 155 wait 156} 157 158ct_udpflood() 159{ 160 local ns="$1" 161 local duration="$2" 162 local now=$(date +%s) 163 local end=$((now + duration)) 164 165 [ $have_socat -ne "1" ] && return 166 167 while [ $now -lt $end ]; do 168ip netns exec "$ns" bash<<"EOF" 169 for i in $(seq 1 100);do 170 dport=$(((RANDOM%65536)+1)) 171 172 echo bar | socat -u STDIN UDP:"127.0.0.1:$dport" & 173 done > /dev/null 2>&1 174 wait 175EOF 176 now=$(date +%s) 177 done 178} 179 180ct_udpclash() 181{ 182 local ns="$1" 183 local duration="$2" 184 local now=$(date +%s) 185 local end=$((now + duration)) 186 187 [ -x udpclash ] || return 188 189 while [ $now -lt $end ]; do 190 ip netns exec "$ns" timeout 30 ./udpclash 127.0.0.1 $((RANDOM%65536)) > /dev/null 2>&1 191 192 now=$(date +%s) 193 done 194} 195 196# dump to /dev/null. We don't want dumps to cause infinite loops 197# or use-after-free even when conntrack table is altered while dumps 198# are in progress. 199ct_nulldump() 200{ 201 local ns="$1" 202 203 ip netns exec "$ns" conntrack -L > /dev/null 2>&1 & 204 205 # Don't require /proc support in conntrack 206 if [ -r /proc/self/net/nf_conntrack ] ; then 207 ip netns exec "$ns" bash -c "wc -l < /proc/self/net/nf_conntrack" > /dev/null & 208 fi 209 210 wait 211} 212 213ct_nulldump_loop() 214{ 215 local ns="$1" 216 local duration="$2" 217 local now=$(date +%s) 218 local end=$((now + duration)) 219 220 while [ $now -lt $end ]; do 221 ct_nulldump "$ns" 222 sleep $((RANDOM%2)) 223 now=$(date +%s) 224 done 225} 226 227change_timeouts() 228{ 229 local ns="$1" 230 local r1=$((RANDOM%2)) 231 local r2=$((RANDOM%2)) 232 233 [ "$r1" -eq 1 ] && ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_icmp_timeout=$((RANDOM%5)) 234 [ "$r2" -eq 1 ] && ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_udp_timeout=$((RANDOM%5)) 235} 236 237ct_change_timeouts_loop() 238{ 239 local ns="$1" 240 local duration="$2" 241 local now=$(date +%s) 242 local end=$((now + duration)) 243 244 while [ $now -lt $end ]; do 245 change_timeouts "$ns" 246 sleep $((RANDOM%2)) 247 now=$(date +%s) 248 done 249 250 # restore defaults 251 ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_icmp_timeout=30 252 ip netns exec "$ns" sysctl -q net.netfilter.nf_conntrack_udp_timeout=30 253} 254 255check_taint() 256{ 257 local tainted_then="$1" 258 local msg="$2" 259 260 local tainted_now=0 261 262 if [ "$tainted_then" -ne 0 ];then 263 return 264 fi 265 266 read tainted_now < /proc/sys/kernel/tainted 267 268 if [ "$tainted_now" -eq 0 ];then 269 echo "PASS: $msg" 270 else 271 echo "TAINT: $msg" 272 dmesg 273 exit 1 274 fi 275} 276 277insert_flood() 278{ 279 local n="$1" 280 local timeout="$2" 281 local r=0 282 283 r=$((RANDOM%$insert_count)) 284 285 ct_pingflood "$n" "$timeout" "floodresize" & 286 ct_udpflood "$n" "$timeout" & 287 ct_udpclash "$n" "$timeout" & 288 289 insert_ctnetlink "$n" "$r" & 290 ctflush "$n" "$timeout" & 291 ct_nulldump_loop "$n" "$timeout" & 292 ct_change_timeouts_loop "$n" "$timeout" & 293 294 wait 295} 296 297test_floodresize_all() 298{ 299 local timeout=20 300 local n="" 301 local tainted_then="" 302 303 read tainted_then < /proc/sys/kernel/tainted 304 305 for n in "$nsclient1" "$nsclient2";do 306 insert_flood "$n" "$timeout" & 307 done 308 309 # resize table constantly while flood/insert/dump/flushs 310 # are happening in parallel. 311 ctresize "$timeout" 312 313 # wait for subshells to complete, everything is limited 314 # by $timeout. 315 wait 316 317 check_taint "$tainted_then" "resize+flood" 318} 319 320check_dump() 321{ 322 local ns="$1" 323 local protoname="$2" 324 local c=0 325 local proto=0 326 local proc=0 327 local unique="" 328 local lret=0 329 330 # NOTE: assumes timeouts are large enough to not have 331 # expirations in all following tests. 332 l=$(ip netns exec "$ns" conntrack -L 2>/dev/null | sort | tee "$tmpfile" | wc -l) 333 c=$(ip netns exec "$ns" conntrack -C) 334 335 if [ "$c" -eq 0 ]; then 336 echo "FAIL: conntrack count for $ns is 0" 337 lret=1 338 fi 339 340 if [ "$c" -ne "$l" ]; then 341 echo "FAIL: conntrack count inconsistency for $ns -L: $c != $l" 342 lret=1 343 fi 344 345 # check the dump we retrieved is free of duplicated entries. 346 unique=$(uniq "$tmpfile" | tee "$tmpfile_uniq" | wc -l) 347 if [ "$l" -ne "$unique" ]; then 348 echo "FAIL: listing contained redundant entries for $ns: $l != $unique" 349 diff -u "$tmpfile" "$tmpfile_uniq" 350 lret=1 351 fi 352 353 # we either inserted icmp or only udp, hence, --proto should return same entry count as without filter. 354 proto=$(ip netns exec "$ns" conntrack -L --proto $protoname 2>/dev/null | sort | uniq | tee "$tmpfile_uniq" | wc -l) 355 if [ "$l" -ne "$proto" ]; then 356 echo "FAIL: dump inconsistency for $ns -L --proto $protoname: $l != $proto" 357 diff -u "$tmpfile" "$tmpfile_uniq" 358 lret=1 359 fi 360 361 if [ -r /proc/self/net/nf_conntrack ] ; then 362 proc=$(ip netns exec "$ns" bash -c "sort < /proc/self/net/nf_conntrack | tee \"$tmpfile_proc\" | wc -l") 363 364 if [ "$l" -ne "$proc" ]; then 365 echo "FAIL: proc inconsistency for $ns: $l != $proc" 366 lret=1 367 fi 368 369 proc=$(uniq "$tmpfile_proc" | tee "$tmpfile_uniq" | wc -l) 370 if [ "$l" -ne "$proc" ]; then 371 echo "FAIL: proc inconsistency after uniq filter for $ns: $l != $proc" 372 diff -u "$tmpfile_proc" "$tmpfile_uniq" 373 lret=1 374 fi 375 fi 376 377 if [ $lret -eq 0 ];then 378 echo "PASS: dump in netns $ns had same entry count (-C $c, -L $l, -p $proto, /proc $proc)" 379 else 380 echo "FAIL: dump in netns $ns had different entry count (-C $c, -L $l, -p $proto, /proc $proc)" 381 ret=1 382 fi 383} 384 385test_dump_all() 386{ 387 local timeout=3 388 local tainted_then="" 389 390 read tainted_then < /proc/sys/kernel/tainted 391 392 ct_flush_once "$nsclient1" 393 ct_flush_once "$nsclient2" 394 395 ip netns exec "$nsclient1" sysctl -q net.netfilter.nf_conntrack_icmp_timeout=3600 396 397 ct_pingflood "$nsclient1" $timeout "dumpall" & 398 insert_ctnetlink "$nsclient2" $insert_count 399 400 wait 401 402 check_dump "$nsclient1" "icmp" 403 check_dump "$nsclient2" "udp" 404 405 check_taint "$tainted_then" "test parallel conntrack dumps" 406} 407 408check_sysctl_immutable() 409{ 410 local ns="$1" 411 local name="$2" 412 local failhard="$3" 413 local o=0 414 local n=0 415 416 o=$(ip netns exec "$ns" sysctl -n "$name" 2>/dev/null) 417 n=$((o+1)) 418 419 # return value isn't reliable, need to read it back 420 ip netns exec "$ns" sysctl -q "$name"=$n 2>/dev/null >/dev/null 421 422 n=$(ip netns exec "$ns" sysctl -n "$name" 2>/dev/null) 423 424 [ -z "$n" ] && return 1 425 426 if [ $o -ne $n ]; then 427 if [ $failhard -gt 0 ] ;then 428 echo "FAIL: net.$name should not be changeable from namespace (now $n)" 429 ret=1 430 fi 431 return 0 432 fi 433 434 return 1 435} 436 437test_conntrack_max_limit() 438{ 439 sysctl -q net.netfilter.nf_conntrack_max=100 440 insert_ctnetlink "$nsclient1" 101 441 442 # check netns is clamped by init_net, i.e., either netns follows 443 # init_net value, or a higher pernet limit (compared to init_net) is ignored. 444 check_ctcount "$nsclient1" 100 "netns conntrack_max is init_net bound" 445 446 sysctl -q net.netfilter.nf_conntrack_max=$init_net_max 447} 448 449test_conntrack_disable() 450{ 451 local timeout=2 452 453 # disable conntrack pickups 454 ip netns exec "$nsclient1" nft flush table ip test_ct 455 456 ct_flush_once "$nsclient1" 457 ct_flush_once "$nsclient2" 458 459 ct_pingflood "$nsclient1" "$timeout" "conntrack disable" 460 ip netns exec "$nsclient2" ping -q -c 1 127.0.0.1 >/dev/null 2>&1 461 462 # Disabled, should not have picked up any connection. 463 check_ctcount "$nsclient1" 0 "conntrack disabled" 464 465 # This one is still active, expect 1 connection. 466 check_ctcount "$nsclient2" 1 "conntrack enabled" 467} 468 469init_net_max=$(sysctl -n net.netfilter.nf_conntrack_max) 470 471check_max_alias $init_net_max 472 473sysctl -q net.netfilter.nf_conntrack_max="262000" 474check_max_alias 262000 475 476setup_ns nsclient1 nsclient2 477 478# check this only works from init_net 479for n in netfilter.nf_conntrack_buckets netfilter.nf_conntrack_expect_max net.nf_conntrack_max;do 480 check_sysctl_immutable "$nsclient1" "net.$n" 1 481done 482 483# won't work on older kernels. If it works, check that the netns obeys the limit 484if check_sysctl_immutable "$nsclient1" net.netfilter.nf_conntrack_max 0;then 485 # subtest: if pernet is changeable, check that reducing it in pernet 486 # limits the pernet entries. Inverse, pernet clamped by a lower init_net 487 # setting, is already checked by "test_conntrack_max_limit" test. 488 489 ip netns exec "$nsclient1" sysctl -q net.netfilter.nf_conntrack_max=1 490 insert_ctnetlink "$nsclient1" 2 491 check_ctcount "$nsclient1" 1 "netns conntrack_max is pernet bound" 492 ip netns exec "$nsclient1" sysctl -q net.netfilter.nf_conntrack_max=$init_net_max 493fi 494 495for n in "$nsclient1" "$nsclient2";do 496# enable conntrack in both namespaces 497ip netns exec "$n" nft -f - <<EOF 498table ip test_ct { 499 chain input { 500 type filter hook input priority 0 501 ct state new counter 502 } 503} 504EOF 505done 506 507tmpfile=$(mktemp) 508tmpfile_proc=$(mktemp) 509tmpfile_uniq=$(mktemp) 510test_conntrack_max_limit 511test_dump_all 512test_floodresize_all 513test_conntrack_disable 514 515exit $ret 516