1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2021 Modirum MDPay 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26 27. $(atf_get_srcdir)/utils.subr 28 29common_dir=$(atf_get_srcdir)/../common 30 31syncookie_state() 32{ 33 jail=$1 34 35 jexec $jail pfctl -si -v | grep -A 2 '^Syncookies' | grep active \ 36 | awk '{ print($2); }' 37} 38 39atf_test_case "basic" "cleanup" 40basic_head() 41{ 42 atf_set descr 'Basic syncookie test' 43 atf_set require.user root 44} 45 46basic_body() 47{ 48 pft_init 49 50 epair=$(vnet_mkepair) 51 52 vnet_mkjail alcatraz ${epair}b 53 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 54 jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \ 55 $(atf_get_srcdir)/echo_inetd.conf 56 57 ifconfig ${epair}a 192.0.2.2/24 up 58 59 jexec alcatraz pfctl -e 60 pft_set_rules alcatraz \ 61 "set syncookies always" \ 62 "pass in" \ 63 "pass out" 64 65 # Sanity check 66 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 67 68 reply=$(echo foo | nc -N -w 5 192.0.2.1 7) 69 if [ "${reply}" != "foo" ]; 70 then 71 atf_fail "Failed to connect to syncookie protected echo daemon" 72 fi 73 74 # Check that status shows syncookies as being active 75 active=$(syncookie_state alcatraz) 76 if [ "$active" != "active" ]; 77 then 78 atf_fail "syncookies not active" 79 fi 80} 81 82basic_cleanup() 83{ 84 rm -f inetd-alcatraz.pid 85 pft_cleanup 86} 87 88atf_test_case "basic_v6" "cleanup" 89basic_v6_head() 90{ 91 atf_set descr 'Basic syncookie IPv6 test' 92 atf_set require.user root 93} 94 95basic_v6_body() 96{ 97 pft_init 98 99 epair=$(vnet_mkepair) 100 101 vnet_mkjail alcatraz ${epair}b 102 jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 103 jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \ 104 $(atf_get_srcdir)/echo_inetd.conf 105 106 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 107 108 jexec alcatraz pfctl -e 109 pft_set_rules alcatraz \ 110 "set syncookies always" \ 111 "pass in" \ 112 "pass out" 113 114 # Sanity check 115 atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1 116 117 reply=$(echo foo | nc -N -w 5 2001:db8::1 7) 118 if [ "${reply}" != "foo" ]; 119 then 120 atf_fail "Failed to connect to syncookie protected echo daemon" 121 fi 122 123 # Check that status shows syncookies as being active 124 active=$(syncookie_state alcatraz) 125 if [ "$active" != "active" ]; 126 then 127 atf_fail "syncookies not active" 128 fi 129} 130 131basic_v6_cleanup() 132{ 133 rm -f inetd-alcatraz.pid 134 pft_cleanup 135} 136 137atf_test_case "forward" "cleanup" 138forward_head() 139{ 140 atf_set descr 'Syncookies for forwarded hosts' 141 atf_set require.user root 142} 143 144forward_body() 145{ 146 pft_init 147 148 epair_in=$(vnet_mkepair) 149 epair_out=$(vnet_mkepair) 150 151 vnet_mkjail fwd ${epair_in}b ${epair_out}a 152 vnet_mkjail srv ${epair_out}b 153 154 jexec fwd ifconfig ${epair_in}b 192.0.2.1/24 up 155 jexec fwd ifconfig ${epair_out}a 198.51.100.1/24 up 156 jexec fwd sysctl net.inet.ip.forwarding=1 157 158 jexec srv ifconfig ${epair_out}b 198.51.100.2/24 up 159 jexec srv route add default 198.51.100.1 160 jexec srv /usr/sbin/inetd -p inetd-alcatraz.pid \ 161 $(atf_get_srcdir)/echo_inetd.conf 162 163 ifconfig ${epair_in}a 192.0.2.2/24 up 164 route add -net 198.51.100.0/24 192.0.2.1 165 166 jexec fwd pfctl -e 167 pft_set_rules fwd \ 168 "set syncookies always" \ 169 "pass in" \ 170 "pass out" 171 172 # Sanity check 173 atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2 174 175 reply=$(echo foo | nc -N -w 5 198.51.100.2 7) 176 if [ "${reply}" != "foo" ]; 177 then 178 atf_fail "Failed to connect to syncookie protected echo daemon" 179 fi 180} 181 182forward_cleanup() 183{ 184 rm -f inetd-alcatraz.pid 185 pft_cleanup 186} 187 188atf_test_case "forward_v6" "cleanup" 189forward_v6_head() 190{ 191 atf_set descr 'Syncookies for forwarded hosts' 192 atf_set require.user root 193} 194 195forward_v6_body() 196{ 197 pft_init 198 199 epair_in=$(vnet_mkepair) 200 epair_out=$(vnet_mkepair) 201 202 vnet_mkjail fwd ${epair_in}b ${epair_out}a 203 vnet_mkjail srv ${epair_out}b 204 205 jexec fwd ifconfig ${epair_in}b inet6 2001:db8::1/64 up no_dad 206 jexec fwd ifconfig ${epair_out}a inet6 2001:db8:1::1/64 up no_dad 207 jexec fwd sysctl net.inet6.ip6.forwarding=1 208 209 jexec srv ifconfig ${epair_out}b inet6 2001:db8:1::2/64 up no_dad 210 jexec srv route -6 add default 2001:db8:1::1 211 jexec srv /usr/sbin/inetd -p inetd-alcatraz.pid \ 212 $(atf_get_srcdir)/echo_inetd.conf 213 214 ifconfig ${epair_in}a inet6 2001:db8::2/64 up no_dad 215 route -6 add -net 2001:db8:1::/64 2001:db8::1 216 217 jexec fwd pfctl -e 218 pft_set_rules fwd \ 219 "set syncookies always" \ 220 "pass in" \ 221 "pass out" 222 223 # Sanity check 224 atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:1::2 225 226 reply=$(echo foo | nc -N -w 5 2001:db8:1::2 7) 227 if [ "${reply}" != "foo" ]; 228 then 229 atf_fail "Failed to connect to syncookie protected echo daemon" 230 fi 231} 232 233forward_v6_cleanup() 234{ 235 rm -f inetd-alcatraz.pid 236 pft_cleanup 237} 238 239atf_test_case "nostate" "cleanup" 240nostate_head() 241{ 242 atf_set descr 'Ensure that we do not create until SYN|ACK' 243 atf_set require.user root 244 atf_set require.progs scapy 245} 246 247nostate_body() 248{ 249 pft_init 250 251 epair=$(vnet_mkepair) 252 ifconfig ${epair}a 192.0.2.2/24 up 253 254 vnet_mkjail alcatraz ${epair}b 255 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 256 257 jexec alcatraz pfctl -e 258 pft_set_rules alcatraz \ 259 "set syncookies always" \ 260 "pass in" \ 261 "pass out" 262 263 # Sanity check 264 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 265 266 # Now syn flood to create many states 267 ${common_dir}/pft_synflood.py \ 268 --sendif ${epair}a \ 269 --to 192.0.2.2 \ 270 --count 20 271 272 states=$(jexec alcatraz pfctl -ss | grep tcp) 273 if [ -n "$states" ]; 274 then 275 echo "$states" 276 atf_fail "Found unexpected state" 277 fi 278} 279 280nostate_cleanup() 281{ 282 pft_cleanup 283} 284 285atf_test_case "nostate_v6" "cleanup" 286nostate_v6_head() 287{ 288 atf_set descr 'Ensure that we do not create until SYN|ACK' 289 atf_set require.user root 290 atf_set require.progs scapy 291} 292 293nostate_v6_body() 294{ 295 pft_init 296 297 epair=$(vnet_mkepair) 298 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 299 300 vnet_mkjail alcatraz ${epair}b 301 jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 302 303 jexec alcatraz pfctl -e 304 pft_set_rules alcatraz \ 305 "set syncookies always" \ 306 "pass in" \ 307 "pass out" 308 309 # Sanity check 310 atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1 311 312 # Now syn flood to create many states 313 ${common_dir}/pft_synflood.py \ 314 --ip6 \ 315 --sendif ${epair}a \ 316 --to 2001:db8::2 \ 317 --count 20 318 319 states=$(jexec alcatraz pfctl -ss | grep tcp) 320 if [ -n "$states" ]; 321 then 322 echo "$states" 323 atf_fail "Found unexpected state" 324 fi 325} 326 327nostate_v6_cleanup() 328{ 329 pft_cleanup 330} 331 332atf_test_case "adaptive" "cleanup" 333adaptive_head() 334{ 335 atf_set descr 'Adaptive mode test' 336 atf_set require.user root 337 atf_set require.progs scapy 338} 339 340adaptive_body() 341{ 342 pft_init 343 344 epair=$(vnet_mkepair) 345 ifconfig ${epair}a 192.0.2.2/24 up 346 347 vnet_mkjail alcatraz ${epair}b 348 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 349 350 jexec alcatraz pfctl -e 351 pft_set_rules alcatraz \ 352 "set limit states 100" \ 353 "set syncookies adaptive (start 10%%, end 5%%)" \ 354 "pass in" \ 355 "pass out" 356 357 # Sanity check 358 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 359 360 # Check that status shows syncookies as being inactive 361 active=$(syncookie_state alcatraz) 362 if [ "$active" != "inactive" ]; 363 then 364 atf_fail "syncookies active when they should not be" 365 fi 366 367 # Now syn flood to create many states 368 ${common_dir}/pft_synflood.py \ 369 --sendif ${epair}a \ 370 --to 192.0.2.2 \ 371 --count 100 372 373 # Check that status shows syncookies as being active 374 active=$(syncookie_state alcatraz) 375 if [ "$active" != "active" ]; 376 then 377 atf_fail "syncookies not active" 378 fi 379 380 # Adaptive mode should kick in and stop us from creating more than 381 # about 10 states 382 states=$(jexec alcatraz pfctl -ss | grep tcp | wc -l) 383 if [ "$states" -gt 20 ]; 384 then 385 echo "$states" 386 atf_fail "Found unexpected states" 387 fi 388} 389 390adaptive_cleanup() 391{ 392 pft_cleanup 393} 394 395atf_test_case "limits" "cleanup" 396limits_head() 397{ 398 atf_set descr 'Ensure limit calculation works for low or high state limits' 399 atf_set require.user root 400} 401 402limits_body() 403{ 404 pft_init 405 406 vnet_mkjail alcatraz 407 408 jexec alcatraz pfctl -e 409 pft_set_rules alcatraz \ 410 "set limit states 1" \ 411 "set syncookies adaptive (start 10%%, end 5%%)" \ 412 "pass in" \ 413 "pass out" 414 415 pft_set_rules alcatraz \ 416 "set limit states 326000000" \ 417 "set syncookies adaptive (start 10%%, end 5%%)" \ 418 "pass in" \ 419 "pass out" 420} 421 422limits_cleanup() 423{ 424 pft_cleanup 425} 426 427atf_test_case "port_reuse" "cleanup" 428port_reuse_head() 429{ 430 atf_set descr 'Test rapid port re-use' 431 atf_set require.user root 432} 433 434port_reuse_body() 435{ 436 pft_init 437 438 epair=$(vnet_mkepair) 439 440 vnet_mkjail alcatraz ${epair}b 441 vnet_mkjail singsing 442 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 443 jexec alcatraz /usr/sbin/inetd -p ${HOME}/inetd-alcatraz.pid \ 444 $(atf_get_srcdir)/echo_inetd.conf 445 446 ifconfig ${epair}a 192.0.2.2/24 up 447 448 jexec alcatraz pfctl -e 449 jexec alcatraz pfctl -x loud 450 pft_set_rules alcatraz \ 451 "set syncookies always" \ 452 "pass in" \ 453 "pass out" 454 455 # Sanity check 456 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 457 458 reply=$(echo foo | nc -p 1234 -N -w 5 192.0.2.1 7) 459 if [ "${reply}" != "foo" ]; 460 then 461 atf_fail "Failed to connect to syncookie protected echo daemon" 462 fi 463 464 # We can't re-use the source IP/port combo quickly enough, so we're 465 # going to play a really dirty trick, and move our interface to a new 466 # jail, and do it from there. 467 ifconfig ${epair}a vnet singsing 468 jexec singsing ifconfig ${epair}a 192.0.2.2/24 up 469 atf_check -s exit:0 -o ignore jexec singsing ping -c 1 192.0.2.1 470 471 reply=$(echo bar | jexec singsing nc -p 1234 -N -w 5 192.0.2.1 7) 472 if [ "${reply}" != "bar" ]; 473 then 474 atf_fail "Failed to connect to syncookie protected echo daemon (2)" 475 fi 476} 477 478port_reuse_cleanup() 479{ 480 pft_cleanup 481} 482 483atf_init_test_cases() 484{ 485 atf_add_test_case "basic" 486 atf_add_test_case "basic_v6" 487 atf_add_test_case "forward" 488 atf_add_test_case "forward_v6" 489 atf_add_test_case "nostate" 490 atf_add_test_case "nostate_v6" 491 atf_add_test_case "adaptive" 492 atf_add_test_case "limits" 493 atf_add_test_case "port_reuse" 494} 495