Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: netfilter: add conntrack stress test

Add a new test case to check:
- conntrack_max limit is effective
- conntrack_max limit cannot be exceeded from within a netns
- resizing the hash table while packets are inflight works
- removal of all conntrack rules disables conntrack in netns
- conntrack tool dump (conntrack -L) returns expected number
of (unique) entries
- procfs interface - if available - has same number of entries
as conntrack -L dump

Expected output with selftest framework:
selftests: net/netfilter: conntrack_resize.sh
PASS: got 1 connections: netns conntrack_max is pernet bound
PASS: got 100 connections: netns conntrack_max is init_net bound
PASS: dump in netns had same entry count (-C 1778, -L 1778, -p 1778, /proc 0)
PASS: dump in netns had same entry count (-C 2000, -L 2000, -p 2000, /proc 0)
PASS: test parallel conntrack dumps
PASS: resize+flood
PASS: got 0 connections: conntrack disabled
PASS: got 1 connections: conntrack enabled
ok 1 selftests: net/netfilter: conntrack_resize.sh

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
d33f889f aa04c6f4

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