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 ${PWD}/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 ${PWD}/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 ${PWD}/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 pft_cleanup 134} 135 136atf_test_case "forward" "cleanup" 137forward_head() 138{ 139 atf_set descr 'Syncookies for forwarded hosts' 140 atf_set require.user root 141} 142 143forward_body() 144{ 145 pft_init 146 147 epair_in=$(vnet_mkepair) 148 epair_out=$(vnet_mkepair) 149 150 vnet_mkjail fwd ${epair_in}b ${epair_out}a 151 vnet_mkjail srv ${epair_out}b 152 153 jexec fwd ifconfig ${epair_in}b 192.0.2.1/24 up 154 jexec fwd ifconfig ${epair_out}a 198.51.100.1/24 up 155 jexec fwd sysctl net.inet.ip.forwarding=1 156 157 jexec srv ifconfig ${epair_out}b 198.51.100.2/24 up 158 jexec srv route add default 198.51.100.1 159 jexec srv /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \ 160 $(atf_get_srcdir)/echo_inetd.conf 161 162 ifconfig ${epair_in}a 192.0.2.2/24 up 163 route add -net 198.51.100.0/24 192.0.2.1 164 165 jexec fwd pfctl -e 166 pft_set_rules fwd \ 167 "set syncookies always" \ 168 "pass in" \ 169 "pass out" 170 171 # Sanity check 172 atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2 173 174 reply=$(echo foo | nc -N -w 5 198.51.100.2 7) 175 if [ "${reply}" != "foo" ]; 176 then 177 atf_fail "Failed to connect to syncookie protected echo daemon" 178 fi 179} 180 181forward_cleanup() 182{ 183 pft_cleanup 184} 185 186atf_test_case "forward_v6" "cleanup" 187forward_v6_head() 188{ 189 atf_set descr 'Syncookies for forwarded hosts' 190 atf_set require.user root 191} 192 193forward_v6_body() 194{ 195 pft_init 196 197 epair_in=$(vnet_mkepair) 198 epair_out=$(vnet_mkepair) 199 200 vnet_mkjail fwd ${epair_in}b ${epair_out}a 201 vnet_mkjail srv ${epair_out}b 202 203 jexec fwd ifconfig ${epair_in}b inet6 2001:db8::1/64 up no_dad 204 jexec fwd ifconfig ${epair_out}a inet6 2001:db8:1::1/64 up no_dad 205 jexec fwd sysctl net.inet6.ip6.forwarding=1 206 207 jexec srv ifconfig ${epair_out}b inet6 2001:db8:1::2/64 up no_dad 208 jexec srv route -6 add default 2001:db8:1::1 209 jexec srv /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \ 210 $(atf_get_srcdir)/echo_inetd.conf 211 212 ifconfig ${epair_in}a inet6 2001:db8::2/64 up no_dad 213 route -6 add -net 2001:db8:1::/64 2001:db8::1 214 215 jexec fwd pfctl -e 216 pft_set_rules fwd \ 217 "set syncookies always" \ 218 "pass in" \ 219 "pass out" 220 221 # Sanity check 222 atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:1::2 223 224 reply=$(echo foo | nc -N -w 5 2001:db8:1::2 7) 225 if [ "${reply}" != "foo" ]; 226 then 227 atf_fail "Failed to connect to syncookie protected echo daemon" 228 fi 229} 230 231forward_v6_cleanup() 232{ 233 pft_cleanup 234} 235 236atf_test_case "nostate" "cleanup" 237nostate_head() 238{ 239 atf_set descr 'Ensure that we do not create until SYN|ACK' 240 atf_set require.user root 241 atf_set require.progs scapy 242} 243 244nostate_body() 245{ 246 pft_init 247 248 epair=$(vnet_mkepair) 249 ifconfig ${epair}a 192.0.2.2/24 up 250 251 vnet_mkjail alcatraz ${epair}b 252 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 253 254 jexec alcatraz pfctl -e 255 pft_set_rules alcatraz \ 256 "set syncookies always" \ 257 "pass in" \ 258 "pass out" 259 260 # Sanity check 261 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 262 263 # Now syn flood to create many states 264 ${common_dir}/pft_synflood.py \ 265 --sendif ${epair}a \ 266 --to 192.0.2.2 \ 267 --count 20 268 269 states=$(jexec alcatraz pfctl -ss | grep tcp) 270 if [ -n "$states" ]; 271 then 272 echo "$states" 273 atf_fail "Found unexpected state" 274 fi 275} 276 277nostate_cleanup() 278{ 279 pft_cleanup 280} 281 282atf_test_case "nostate_v6" "cleanup" 283nostate_v6_head() 284{ 285 atf_set descr 'Ensure that we do not create until SYN|ACK' 286 atf_set require.user root 287 atf_set require.progs scapy 288} 289 290nostate_v6_body() 291{ 292 pft_init 293 294 epair=$(vnet_mkepair) 295 ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad 296 297 vnet_mkjail alcatraz ${epair}b 298 jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad 299 300 jexec alcatraz pfctl -e 301 pft_set_rules alcatraz \ 302 "set syncookies always" \ 303 "pass in" \ 304 "pass out" 305 306 # Sanity check 307 atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1 308 309 # Now syn flood to create many states 310 ${common_dir}/pft_synflood.py \ 311 --ip6 \ 312 --sendif ${epair}a \ 313 --to 2001:db8::2 \ 314 --count 20 315 316 states=$(jexec alcatraz pfctl -ss | grep tcp) 317 if [ -n "$states" ]; 318 then 319 echo "$states" 320 atf_fail "Found unexpected state" 321 fi 322} 323 324nostate_v6_cleanup() 325{ 326 pft_cleanup 327} 328 329atf_test_case "adaptive" "cleanup" 330adaptive_head() 331{ 332 atf_set descr 'Adaptive mode test' 333 atf_set require.user root 334 atf_set require.progs scapy 335} 336 337adaptive_body() 338{ 339 pft_init 340 341 epair=$(vnet_mkepair) 342 ifconfig ${epair}a 192.0.2.2/24 up 343 344 vnet_mkjail alcatraz ${epair}b 345 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 346 347 jexec alcatraz pfctl -e 348 pft_set_rules alcatraz \ 349 "set limit states 100" \ 350 "set syncookies adaptive (start 10%%, end 5%%)" \ 351 "pass in" \ 352 "pass out" 353 354 # Sanity check 355 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 356 357 # Check that status shows syncookies as being inactive 358 active=$(syncookie_state alcatraz) 359 if [ "$active" != "inactive" ]; 360 then 361 atf_fail "syncookies active when they should not be" 362 fi 363 364 # Now syn flood to create many states 365 ${common_dir}/pft_synflood.py \ 366 --sendif ${epair}a \ 367 --to 192.0.2.2 \ 368 --count 100 369 370 # Check that status shows syncookies as being active 371 active=$(syncookie_state alcatraz) 372 if [ "$active" != "active" ]; 373 then 374 atf_fail "syncookies not active" 375 fi 376 377 # Adaptive mode should kick in and stop us from creating more than 378 # about 10 states 379 states=$(jexec alcatraz pfctl -ss | grep tcp | wc -l) 380 if [ "$states" -gt 20 ]; 381 then 382 echo "$states" 383 atf_fail "Found unexpected states" 384 fi 385} 386 387adaptive_cleanup() 388{ 389 pft_cleanup 390} 391 392atf_test_case "limits" "cleanup" 393limits_head() 394{ 395 atf_set descr 'Ensure limit calculation works for low or high state limits' 396 atf_set require.user root 397} 398 399limits_body() 400{ 401 pft_init 402 403 vnet_mkjail alcatraz 404 405 jexec alcatraz pfctl -e 406 pft_set_rules alcatraz \ 407 "set limit states 1" \ 408 "set syncookies adaptive (start 10%%, end 5%%)" \ 409 "pass in" \ 410 "pass out" 411 412 pft_set_rules alcatraz \ 413 "set limit states 326000000" \ 414 "set syncookies adaptive (start 10%%, end 5%%)" \ 415 "pass in" \ 416 "pass out" 417} 418 419limits_cleanup() 420{ 421 pft_cleanup 422} 423 424atf_test_case "port_reuse" "cleanup" 425port_reuse_head() 426{ 427 atf_set descr 'Test rapid port re-use' 428 atf_set require.user root 429} 430 431port_reuse_body() 432{ 433 pft_init 434 435 epair=$(vnet_mkepair) 436 437 vnet_mkjail alcatraz ${epair}b 438 vnet_mkjail singsing 439 jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up 440 jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \ 441 $(atf_get_srcdir)/echo_inetd.conf 442 443 ifconfig ${epair}a 192.0.2.2/24 up 444 445 jexec alcatraz pfctl -e 446 jexec alcatraz pfctl -x loud 447 pft_set_rules alcatraz \ 448 "set syncookies always" \ 449 "pass in" \ 450 "pass out" 451 452 # Sanity check 453 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 454 455 reply=$(echo foo | nc -p 1234 -N -w 5 192.0.2.1 7) 456 if [ "${reply}" != "foo" ]; 457 then 458 atf_fail "Failed to connect to syncookie protected echo daemon" 459 fi 460 461 # We can't re-use the source IP/port combo quickly enough, so we're 462 # going to play a really dirty trick, and move our interface to a new 463 # jail, and do it from there. 464 ifconfig ${epair}a vnet singsing 465 jexec singsing ifconfig ${epair}a 192.0.2.2/24 up 466 atf_check -s exit:0 -o ignore jexec singsing ping -c 1 192.0.2.1 467 468 reply=$(echo bar | jexec singsing nc -p 1234 -N -w 5 192.0.2.1 7) 469 if [ "${reply}" != "bar" ]; 470 then 471 atf_fail "Failed to connect to syncookie protected echo daemon (2)" 472 fi 473} 474 475port_reuse_cleanup() 476{ 477 pft_cleanup 478} 479 480atf_init_test_cases() 481{ 482 atf_add_test_case "basic" 483 atf_add_test_case "basic_v6" 484 atf_add_test_case "forward" 485 atf_add_test_case "forward_v6" 486 atf_add_test_case "nostate" 487 atf_add_test_case "nostate_v6" 488 atf_add_test_case "adaptive" 489 atf_add_test_case "limits" 490 atf_add_test_case "port_reuse" 491} 492