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