1# 2# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3# 4# Copyright © 2023 Orange Business Services 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 29sctp_init() 30{ 31 pft_init 32 if ! kldstat -q -m sctp; then 33 atf_skip "This test requires SCTP" 34 fi 35} 36 37atf_test_case "basic_v4" "cleanup" 38basic_v4_head() 39{ 40 atf_set descr 'Basic SCTP connection over IPv4 passthrough' 41 atf_set require.user root 42} 43 44basic_v4_body() 45{ 46 sctp_init 47 48 j="sctp:basic_v4" 49 epair=$(vnet_mkepair) 50 51 vnet_mkjail ${j}a ${epair}a 52 vnet_mkjail ${j}b ${epair}b 53 54 jexec ${j}a ifconfig ${epair}a 192.0.2.1/24 up 55 jexec ${j}b ifconfig ${epair}b 192.0.2.2/24 up 56 # Sanity check 57 atf_check -s exit:0 -o ignore \ 58 jexec ${j}a ping -c 1 192.0.2.2 59 60 jexec ${j}a pfctl -e 61 pft_set_rules ${j}a \ 62 "block" \ 63 "pass in proto sctp to port 1234" 64 65 echo "foo" | jexec ${j}a nc --sctp -N -l 1234 & 66 67 # Wait for the server to start 68 sleep 1 69 70 out=$(jexec ${j}b nc --sctp -N -w 3 192.0.2.1 1234) 71 if [ "$out" != "foo" ]; then 72 atf_fail "SCTP connection failed" 73 fi 74 75 # Now with scrub rules present, so normalization is done 76 pft_set_rules ${j}a \ 77 "scrub on ${j}a" \ 78 "block" \ 79 "pass in proto sctp to port 1234" 80 81 echo "foo" | jexec ${j}a nc --sctp -N -l 1234 & 82 sleep 1 83 84 out=$(jexec ${j}b nc --sctp -N -w 3 192.0.2.1 1234) 85 if [ "$out" != "foo" ]; then 86 atf_fail "SCTP connection failed" 87 fi 88 89 # Now fail with a blocked port 90 echo "foo" | jexec ${j}a nc --sctp -N -l 1235 & 91 sleep 1 92 93 out=$(jexec ${j}b nc --sctp -N -w 3 192.0.2.1 1235) 94 if [ "$out" == "foo" ]; then 95 atf_fail "SCTP port block failed" 96 fi 97 98 # Now fail with a blocked port but passing source port 99 out=$(jexec ${j}b nc --sctp -N -w 3 -p 1234 192.0.2.1 1235) 100 if [ "$out" == "foo" ]; then 101 atf_fail "SCTP port block failed" 102 fi 103} 104 105basic_v4_cleanup() 106{ 107 pft_cleanup 108} 109 110atf_test_case "basic_v6" "cleanup" 111basic_v6_head() 112{ 113 atf_set descr 'Basic SCTP connection over IPv6' 114 atf_set require.user root 115} 116 117basic_v6_body() 118{ 119 sctp_init 120 121 j="sctp:basic_v6" 122 epair=$(vnet_mkepair) 123 124 vnet_mkjail ${j}a ${epair}a 125 vnet_mkjail ${j}b ${epair}b 126 127 jexec ${j}a ifconfig ${epair}a inet6 2001:db8::a/64 up no_dad 128 jexec ${j}b ifconfig ${epair}b inet6 2001:db8::b/64 up no_dad 129 130 # Sanity check 131 atf_check -s exit:0 -o ignore \ 132 jexec ${j}a ping -6 -c 1 2001:db8::b 133 134 jexec ${j}a pfctl -e 135 pft_set_rules ${j}a \ 136 "block proto sctp" \ 137 "pass in proto sctp to port 1234" 138 139 echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1234 & 140 141 # Wait for the server to start 142 sleep 1 143 144 out=$(jexec ${j}b nc --sctp -N -w 3 2001:db8::a 1234) 145 if [ "$out" != "foo" ]; then 146 atf_fail "SCTP connection failed" 147 fi 148 149 # Now with scrub rules present, so normalization is done 150 pft_set_rules ${j}a \ 151 "scrub on ${j}a" \ 152 "block proto sctp" \ 153 "pass in proto sctp to port 1234" 154 155 echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1234 & 156 sleep 1 157 158 out=$(jexec ${j}b nc --sctp -N -w 3 2001:db8::a 1234) 159 if [ "$out" != "foo" ]; then 160 atf_fail "SCTP connection failed" 161 fi 162 163 # Now fail with a blocked port 164 echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1235 & 165 sleep 1 166 167 out=$(jexec ${j}b nc --sctp -N -w 3 2001:db8::a 1235) 168 if [ "$out" == "foo" ]; then 169 atf_fail "SCTP port block failed" 170 fi 171 172 # Now fail with a blocked port but passing source port 173 out=$(jexec ${j}b nc --sctp -N -w 3 -p 1234 2001:db8::a 1235) 174 if [ "$out" == "foo" ]; then 175 atf_fail "SCTP port block failed" 176 fi 177} 178 179basic_v6_cleanup() 180{ 181 pft_cleanup 182} 183 184atf_test_case "abort_v4" "cleanup" 185abort_v4_head() 186{ 187 atf_set descr 'Test sending ABORT messages' 188 atf_set require.user root 189} 190 191abort_v4_body() 192{ 193 sctp_init 194 195 j="sctp:abort_v4" 196 epair=$(vnet_mkepair) 197 198 vnet_mkjail ${j}a ${epair}a 199 vnet_mkjail ${j}b ${epair}b 200 201 jexec ${j}a ifconfig ${epair}a 192.0.2.1/24 up 202 jexec ${j}b ifconfig ${epair}b 192.0.2.2/24 up 203 204 # Sanity check 205 atf_check -s exit:0 -o ignore \ 206 jexec ${j}a ping -c 1 192.0.2.2 207 208 jexec ${j}a pfctl -e 209 pft_set_rules ${j}a \ 210 "block return in proto sctp to port 1234" 211 212 echo "foo" | jexec ${j}a nc --sctp -N -l 1234 & 213 214 # Wait for the server to start 215 sleep 1 216 217 # If we get the abort we'll exit immediately, if we don't timeout will 218 # stop nc. 219 out=$(jexec ${j}b timeout 3 nc --sctp -N 192.0.2.1 1234) 220 if [ $? -eq 124 ]; then 221 atf_fail 'Abort not received' 222 fi 223 if [ "$out" == "foo" ]; then 224 atf_fail "block failed entirely" 225 fi 226 227 # Without 'return' we will time out. 228 pft_set_rules ${j}a \ 229 "block in proto sctp to port 1234" 230 231 out=$(jexec ${j}b timeout 3 nc --sctp -N 192.0.2.1 1234) 232 if [ $? -ne 124 ]; then 233 atf_fail 'Abort sent anyway?' 234 fi 235} 236 237abort_v4_cleanup() 238{ 239 pft_cleanup 240} 241 242atf_test_case "abort_v6" "cleanup" 243abort_v6_head() 244{ 245 atf_set descr 'Test sending ABORT messages over IPv6' 246 atf_set require.user root 247} 248 249abort_v6_body() 250{ 251 sctp_init 252 253 j="sctp:abort_v6" 254 epair=$(vnet_mkepair) 255 256 vnet_mkjail ${j}a ${epair}a 257 vnet_mkjail ${j}b ${epair}b 258 259 jexec ${j}a ifconfig ${epair}a inet6 2001:db8::a/64 no_dad 260 jexec ${j}b ifconfig ${epair}b inet6 2001:db8::b/64 no_dad 261 262 # Sanity check 263 atf_check -s exit:0 -o ignore \ 264 jexec ${j}a ping -6 -c 1 2001:db8::b 265 266 jexec ${j}a pfctl -e 267 pft_set_rules ${j}a \ 268 "block return in proto sctp to port 1234" 269 270 echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1234 & 271 272 # Wait for the server to start 273 sleep 1 274 275 # If we get the abort we'll exit immediately, if we don't timeout will 276 # stop nc. 277 out=$(jexec ${j}b timeout 3 nc --sctp -N 2001:db8::a 1234) 278 if [ $? -eq 124 ]; then 279 atf_fail 'Abort not received' 280 fi 281 if [ "$out" == "foo" ]; then 282 atf_fail "block failed entirely" 283 fi 284 285 # Without 'return' we will time out. 286 pft_set_rules ${j}a \ 287 "block in proto sctp to port 1234" 288 289 out=$(jexec ${j}b timeout 3 nc --sctp -N 2001:db8::a 1234) 290 if [ $? -ne 124 ]; then 291 atf_fail 'Abort sent anyway?' 292 fi 293} 294 295abort_v6_cleanup() 296{ 297 pft_cleanup 298} 299 300atf_test_case "nat_v4" "cleanup" 301nat_v4_head() 302{ 303 atf_set descr 'Test NAT-ing SCTP over IPv4' 304 atf_set require.user root 305} 306 307nat_v4_body() 308{ 309 sctp_init 310 311 j="sctp:nat_v4" 312 epair_c=$(vnet_mkepair) 313 epair_srv=$(vnet_mkepair) 314 315 vnet_mkjail ${j}srv ${epair_srv}a 316 vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a 317 vnet_mkjail ${j}c ${epair_c}b 318 319 jexec ${j}srv ifconfig ${epair_srv}a 198.51.100.1/24 up 320 # No default route in srv jail, to ensure we're NAT-ing 321 jexec ${j}gw ifconfig ${epair_srv}b 198.51.100.2/24 up 322 jexec ${j}gw ifconfig ${epair_c}a 192.0.2.1/24 up 323 jexec ${j}gw sysctl net.inet.ip.forwarding=1 324 jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up 325 jexec ${j}c route add default 192.0.2.1 326 327 jexec ${j}gw pfctl -e 328 pft_set_rules ${j}gw \ 329 "nat on ${epair_srv}b from 192.0.2.0/24 -> (${epair_srv}b)" \ 330 "pass" 331 332 # Sanity check 333 atf_check -s exit:0 -o ignore \ 334 jexec ${j}c ping -c 1 198.51.100.1 335 336 echo "foo" | jexec ${j}srv nc --sctp -N -l 1234 & 337 338 # Wait for the server to start 339 sleep 1 340 341 out=$(jexec ${j}c nc --sctp -N -w 3 198.51.100.1 1234) 342 if [ "$out" != "foo" ]; then 343 atf_fail "SCTP connection failed" 344 fi 345} 346 347nat_v4_cleanup() 348{ 349 pft_cleanup 350} 351 352atf_test_case "nat_v6" "cleanup" 353nat_v6_head() 354{ 355 atf_set descr 'Test NAT-ing SCTP over IPv6' 356 atf_set require.user root 357} 358 359nat_v6_body() 360{ 361 sctp_init 362 363 j="sctp:nat_v6" 364 epair_c=$(vnet_mkepair) 365 epair_srv=$(vnet_mkepair) 366 367 vnet_mkjail ${j}srv ${epair_srv}a 368 vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a 369 vnet_mkjail ${j}c ${epair_c}b 370 371 jexec ${j}srv ifconfig ${epair_srv}a inet6 2001:db8::1/64 up no_dad 372 # No default route in srv jail, to ensure we're NAT-ing 373 jexec ${j}gw ifconfig ${epair_srv}b inet6 2001:db8::2/64 up no_dad 374 jexec ${j}gw ifconfig ${epair_c}a inet6 2001:db8:1::1/64 up no_dad 375 jexec ${j}gw sysctl net.inet6.ip6.forwarding=1 376 jexec ${j}c ifconfig ${epair_c}b inet6 2001:db8:1::2/64 up no_dad 377 jexec ${j}c route add -6 default 2001:db8:1::1 378 379 jexec ${j}gw pfctl -e 380 pft_set_rules ${j}gw \ 381 "nat on ${epair_srv}b from 2001:db8:1::/64 -> (${epair_srv}b)" \ 382 "pass" 383 384 # Sanity check 385 atf_check -s exit:0 -o ignore \ 386 jexec ${j}c ping -6 -c 1 2001:db8::1 387 388 echo "foo" | jexec ${j}srv nc -6 --sctp -N -l 1234 & 389 390 # Wait for the server to start 391 sleep 1 392 393 out=$(jexec ${j}c nc --sctp -N -w 3 2001:db8::1 1234) 394 if [ "$out" != "foo" ]; then 395 atf_fail "SCTP connection failed" 396 fi 397} 398 399nat_v6_cleanup() 400{ 401 pft_cleanup 402} 403 404atf_test_case "rdr_v4" "cleanup" 405rdr_v4_head() 406{ 407 atf_set descr 'Test rdr SCTP over IPv4' 408 atf_set require.user root 409} 410 411rdr_v4_body() 412{ 413 sctp_init 414 415 j="sctp:rdr_v4" 416 epair_c=$(vnet_mkepair) 417 epair_srv=$(vnet_mkepair) 418 419 vnet_mkjail ${j}srv ${epair_srv}a 420 vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a 421 vnet_mkjail ${j}c ${epair_c}b 422 423 jexec ${j}srv ifconfig ${epair_srv}a 198.51.100.1/24 up 424 # No default route in srv jail, to ensure we're NAT-ing 425 jexec ${j}gw ifconfig ${epair_srv}b 198.51.100.2/24 up 426 jexec ${j}gw ifconfig ${epair_c}a 192.0.2.1/24 up 427 jexec ${j}gw sysctl net.inet.ip.forwarding=1 428 jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up 429 jexec ${j}c route add default 192.0.2.1 430 431 jexec ${j}gw pfctl -e 432 pft_set_rules ${j}gw \ 433 "rdr pass on ${epair_srv}b proto sctp from 198.51.100.0/24 to any port 1234 -> 192.0.2.2 port 1234" \ 434 "pass" 435 436 echo "foo" | jexec ${j}c nc --sctp -N -l 1234 & 437 438 # Wait for the server to start 439 sleep 1 440 441 out=$(jexec ${j}srv nc --sctp -N -w 3 198.51.100.2 1234) 442 if [ "$out" != "foo" ]; then 443 atf_fail "SCTP connection failed" 444 fi 445 446 # Despite configuring port changes pf will not do so. 447 echo "bar" | jexec ${j}c nc --sctp -N -l 1234 & 448 449 pft_set_rules ${j}gw \ 450 "rdr pass on ${epair_srv}b proto sctp from 198.51.100.0/24 to any port 1234 -> 192.0.2.2 port 4321" \ 451 "pass" 452 453 # This will fail 454 out=$(jexec ${j}srv nc --sctp -N -w 3 198.51.100.2 4321) 455 if [ "$out" == "bar" ]; then 456 atf_fail "Port was unexpectedly changed." 457 fi 458 459 # This succeeds 460 out=$(jexec ${j}srv nc --sctp -N -w 3 198.51.100.2 1234) 461 if [ "$out" != "bar" ]; then 462 atf_fail "Port was unexpectedly changed." 463 fi 464} 465 466rdr_v4_cleanup() 467{ 468 pft_cleanup 469} 470 471atf_test_case "pfsync" "cleanup" 472pfsync_head() 473{ 474 atf_set descr 'Test pfsync-ing SCTP connections' 475 atf_set require.user root 476} 477 478pfsync_body() 479{ 480 # + Builds bellow topology and initiate an SCTP connection 481 # from client to server. 482 # + Tests that the connection remains open when we fail over from 483 # router one to router two. 484 # 485 # ┌──────┐ 486 # │client│ 487 # └───┬──┘ 488 # │ 489 # ┌───┴───┐ 490 # │bridge0│ 491 # └┬─────┬┘ 492 # │ │ 493 # ┌────────────────┴─┐ ┌─┴────────────────┐ 494 # │ one ├─┤ two │ 495 # └────────────────┬─┘ └─┬────────────────┘ 496 # │ │ 497 # ┌┴─────┴┐ 498 # │bridge1│ 499 # └───┬───┘ 500 # │ 501 # ┌───┴──┐ 502 # │server│ 503 # └──────┘ 504 505 sctp_init 506 pfsynct_init 507 if ! kldstat -q -m carp 508 then 509 atf_skip "This test requires carp" 510 fi 511 512 j="sctp:pfsync" 513 514 tmp=`pwd` 515 516 bridge0=$(vnet_mkbridge) 517 bridge1=$(vnet_mkbridge) 518 519 epair_c=$(vnet_mkepair) 520 epair_one0=$(vnet_mkepair) 521 epair_two0=$(vnet_mkepair) 522 epair_sync=$(vnet_mkepair) 523 epair_one1=$(vnet_mkepair) 524 epair_two1=$(vnet_mkepair) 525 epair_srv=$(vnet_mkepair) 526 527 ifconfig ${bridge0} addm ${epair_c}a addm ${epair_one0}a addm ${epair_two0}a 528 ifconfig ${epair_one0}a up 529 ifconfig ${epair_two0}a up 530 ifconfig ${epair_c}a up 531 ifconfig ${bridge0} up 532 533 ifconfig ${bridge1} addm ${epair_srv}a addm ${epair_one1}a addm ${epair_two1}a 534 ifconfig ${epair_one1}a up 535 ifconfig ${epair_two1}a up 536 ifconfig ${epair_srv}a up 537 ifconfig ${bridge1} up 538 539 vnet_mkjail ${j}c ${epair_c}b 540 jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up 541 jexec ${j}c route add default 192.0.2.1 542 543 vnet_mkjail ${j}one ${epair_one0}b ${epair_one1}b ${epair_sync}a 544 jexec ${j}one ifconfig ${epair_one0}b 192.0.2.3/24 up 545 jexec ${j}one ifconfig ${epair_one0}b \ 546 alias 192.0.2.1/32 vhid 1 pass 1234 547 jexec ${j}one ifconfig ${epair_one1}b 198.51.100.3/24 up 548 jexec ${j}one ifconfig ${epair_one1}b \ 549 alias 198.51.100.2/32 vhid 2 pass 4321 550 jexec ${j}one ifconfig ${epair_sync}a 203.0.113.1/24 up 551 jexec ${j}one ifconfig pfsync0 \ 552 syncdev ${epair_sync}a \ 553 maxupd 1 \ 554 up 555 jexec ${j}one sysctl net.inet.ip.forwarding=1 556 557 vnet_mkjail ${j}two ${epair_two0}b ${epair_two1}b ${epair_sync}b 558 jexec ${j}two ifconfig ${epair_two0}b 192.0.2.4/24 up 559 jexec ${j}two ifconfig ${epair_two0}b \ 560 alias 192.0.2.1/32 vhid 1 pass 1234 561 jexec ${j}two ifconfig ${epair_two1}b 198.51.100.4/24 up 562 jexec ${j}two ifconfig ${epair_two1}b \ 563 alias 198.51.100.2/32 vhid 2 pass 4321 564 jexec ${j}two ifconfig ${epair_sync}b 203.0.113.2/24 up 565 jexec ${j}two ifconfig pfsync0 \ 566 syncdev ${epair_sync}b \ 567 maxupd 1 \ 568 up 569 jexec ${j}two sysctl net.inet.ip.forwarding=1 570 571 vnet_mkjail ${j}srv ${epair_srv}b 572 jexec ${j}srv ifconfig ${epair_srv}b 198.51.100.1/24 up 573 jexec ${j}srv route add default 198.51.100.2 574 575 # Demote two, to avoid dealing with asymmetric routing 576 jexec ${j}two sysctl net.inet.carp.demotion=50 577 578 jexec ${j}one pfctl -e 579 pft_set_rules ${j}one \ 580 "block all" \ 581 "pass proto { icmp, pfsync, carp }" \ 582 "pass proto sctp to port 1234" \ 583 "pass proto tcp to port 1234" 584 585 jexec ${j}two pfctl -e 586 pft_set_rules ${j}two \ 587 "block all" \ 588 "pass proto { icmp, pfsync, carp }" \ 589 "pass proto sctp to port 1234" \ 590 "pass proto tcp to port 1234" 591 592 # Give carp time to get set up 593 sleep 2 594 595 # Sanity check 596 atf_check -s exit:0 -o ignore \ 597 jexec ${j}c ping -c 1 198.51.100.1 598 599 # Now start up an SCTP connection 600 touch ${tmp}/input 601 tail -F ${tmp}/input | jexec ${j}srv nc --sctp -l 1234 & 602 sleep 1 603 604 jexec ${j}c nc --sctp 198.51.100.1 1234 > ${tmp}/output & 605 echo "1" >> ${tmp}/input 606 607 # Give time for the traffic to arrive 608 sleep 1 609 line=$(tail -n -1 ${tmp}/output) 610 if [ "${line}" != "1" ]; 611 then 612 echo "Found ${line}" 613 cat ${tmp}/output 614 atf_fail "Initial SCTP connection failed" 615 fi 616 617 # Verify that two has the connection too 618 state=$(jexec ${j}two pfctl -ss | grep sctp) 619 if [ -z "${state}" ]; 620 then 621 jexec ${j}two pfctl -ss 622 atf_fail "Failed to find SCTP state on secondary pfsync host" 623 fi 624 625 # Now fail over (both carp IPs should switch here) 626 jexec ${j}one sysctl net.inet.carp.demotion=100 627 628 while ! jexec ${j}one ifconfig ${epair_one0}b | grep MASTER; 629 do 630 sleep 1 631 done 632 while ! jexec ${j}one ifconfig ${epair_one1}b | grep MASTER; 633 do 634 sleep 1 635 done 636 637 # Sanity check 638 atf_check -s exit:0 -o ignore \ 639 jexec ${j}c ping -c 1 198.51.100.1 640 641 # And check that the connection is still live 642 echo "2" >> ${tmp}/input 643 sleep 1 644 line=$(tail -n -1 ${tmp}/output) 645 if [ "${line}" != "2" ]; 646 then 647 echo "Found ${line}" 648 cat ${tmp}/output 649 atf_fail "SCTP failover failed" 650 fi 651} 652 653pfsync_cleanup() 654{ 655 pfsynct_cleanup 656} 657 658atf_init_test_cases() 659{ 660 atf_add_test_case "basic_v4" 661 atf_add_test_case "basic_v6" 662 atf_add_test_case "abort_v4" 663 atf_add_test_case "abort_v6" 664 atf_add_test_case "nat_v4" 665 atf_add_test_case "nat_v6" 666 atf_add_test_case "rdr_v4" 667 atf_add_test_case "pfsync" 668} 669