1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org> 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 31atf_test_case "too_many_fragments" "cleanup" 32 33too_many_fragments_head() 34{ 35 atf_set descr 'IPv4 fragment limitation test' 36 atf_set require.user root 37} 38 39too_many_fragments_body() 40{ 41 pft_init 42 43 epair=$(vnet_mkepair) 44 vnet_mkjail alcatraz ${epair}a 45 46 ifconfig ${epair}b inet 192.0.2.1/24 up 47 jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up 48 49 ifconfig ${epair}b mtu 200 50 jexec alcatraz ifconfig ${epair}a mtu 200 51 52 jexec alcatraz pfctl -e 53 pft_set_rules alcatraz \ 54 "set reassemble yes" \ 55 "pass keep state" 56 57 # So we know pf is limiting things 58 jexec alcatraz sysctl net.inet.ip.maxfragsperpacket=1024 59 60 # Sanity check 61 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 62 63 # We can ping with < 64 fragments 64 atf_check -s exit:0 -o ignore ping -c 1 -s 800 192.0.2.2 65 66 # Too many fragments should fail 67 atf_check -s exit:2 -o ignore ping -c 1 -s 20000 192.0.2.2 68} 69 70too_many_fragments_cleanup() 71{ 72 pft_cleanup 73} 74 75atf_test_case "v6" "cleanup" 76v6_head() 77{ 78 atf_set descr 'IPv6 fragmentation test' 79 atf_set require.user root 80 atf_set require.progs scapy 81} 82 83v6_body() 84{ 85 pft_init 86 87 epair_send=$(vnet_mkepair) 88 epair_link=$(vnet_mkepair) 89 90 vnet_mkjail alcatraz ${epair_send}b ${epair_link}a 91 vnet_mkjail singsing ${epair_link}b 92 93 ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up 94 95 jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up 96 jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up 97 jexec alcatraz sysctl net.inet6.ip6.forwarding=1 98 99 jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up 100 jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2 101 route add -6 2001:db8:43::/64 2001:db8:42::2 102 103 jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled 104 jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled 105 jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled 106 ifconfig ${epair_send}a inet6 -ifdisabled 107 108 ifconfig ${epair_send}a 109 jexec alcatraz ifconfig ${epair_send}b 110 lladdr=$(jexec alcatraz ifconfig ${epair_send}b | awk '/ scopeid / { print($2); }' | cut -f 1 -d %) 111 112 jexec alcatraz pfctl -e 113 pft_set_rules alcatraz \ 114 "set reassemble yes" \ 115 "pass keep state" \ 116 "block in" \ 117 "pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 118 "pass in inet6 proto icmp6 icmp6-type { echoreq, echorep }" \ 119 "set skip on lo" 120 121 # Host test 122 atf_check -s exit:0 -o ignore \ 123 ping -6 -c 1 2001:db8:42::2 124 125 atf_check -s exit:0 -o ignore \ 126 ping -6 -c 1 -s 4500 2001:db8:42::2 127 128 atf_check -s exit:0 -o ignore\ 129 ping -6 -c 1 -b 70000 -s 65000 2001:db8:42::2 130 131 # Force an NDP lookup 132 ping -6 -c 1 ${lladdr}%${epair_send}a 133 134 atf_check -s exit:0 -o ignore\ 135 ping -6 -c 1 -b 70000 -s 65000 ${lladdr}%${epair_send}a 136 137 # Forwarding test 138 atf_check -s exit:0 -o ignore \ 139 ping -6 -c 1 2001:db8:43::3 140 141 atf_check -s exit:0 -o ignore \ 142 ping -6 -c 1 -s 4500 2001:db8:43::3 143 144 atf_check -s exit:0 -o ignore\ 145 ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3 146 147 $(atf_get_srcdir)/CVE-2019-5597.py \ 148 ${epair_send}a \ 149 2001:db8:42::1 \ 150 2001:db8:43::3 151} 152 153v6_cleanup() 154{ 155 pft_cleanup 156} 157 158atf_test_case "v6_route_to" "cleanup" 159v6_route_to_head() 160{ 161 atf_set descr 'Test IPv6 reassembly combined with route-to' 162 atf_set require.user root 163} 164 165v6_route_to_body() 166{ 167 pft_init 168 169 epair_send=$(vnet_mkepair) 170 epair_link=$(vnet_mkepair) 171 172 vnet_mkjail alcatraz ${epair_send}b ${epair_link}a 173 vnet_mkjail singsing ${epair_link}b 174 175 ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up 176 177 jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up 178 jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up 179 jexec alcatraz sysctl net.inet6.ip6.forwarding=1 180 181 jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up 182 jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2 183 route add -6 2001:db8:43::/64 2001:db8:42::2 184 185 jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled 186 jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled 187 jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled 188 ifconfig ${epair_send}a inet6 -ifdisabled 189 190 jexec alcatraz pfctl -e 191 pft_set_rules alcatraz \ 192 "set reassemble yes" \ 193 "pass" \ 194 "pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state" 195 196 # Forwarding test 197 atf_check -s exit:0 -o ignore \ 198 ping -6 -c 1 2001:db8:43::3 199 200 atf_check -s exit:0 -o ignore \ 201 ping -6 -c 1 -s 4500 2001:db8:43::3 202 203 atf_check -s exit:0 -o ignore\ 204 ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3 205 206 # Now test this without fragmentation 207 pft_set_rules alcatraz \ 208 "set reassemble no" \ 209 "pass" \ 210 "pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state" 211 212 atf_check -s exit:0 -o ignore \ 213 ping -6 -c 1 2001:db8:43::3 214 215 atf_check -s exit:0 -o ignore \ 216 ping -6 -c 1 -s 4500 2001:db8:43::3 217 218 atf_check -s exit:0 -o ignore\ 219 ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3 220} 221 222v6_route_to_cleanup() 223{ 224 pft_cleanup 225} 226 227atf_test_case "mtu_diff" "cleanup" 228mtu_diff_head() 229{ 230 atf_set descr 'Test reassembly across different MTUs, PR #255432' 231 atf_set require.user root 232} 233 234mtu_diff_body() 235{ 236 pft_init 237 238 epair_small=$(vnet_mkepair) 239 epair_large=$(vnet_mkepair) 240 241 vnet_mkjail first ${epair_small}b ${epair_large}a 242 vnet_mkjail second ${epair_large}b 243 244 ifconfig ${epair_small}a 192.0.2.1/25 up 245 jexec first ifconfig ${epair_small}b 192.0.2.2/25 up 246 247 jexec first sysctl net.inet.ip.forwarding=1 248 jexec first ifconfig ${epair_large}a 192.0.2.130/25 up 249 jexec first ifconfig ${epair_large}a mtu 9000 250 jexec second ifconfig ${epair_large}b 192.0.2.131/25 up 251 jexec second ifconfig ${epair_large}b mtu 9000 252 jexec second route add default 192.0.2.130 253 254 route add 192.0.2.128/25 192.0.2.2 255 256 jexec first pfctl -e 257 pft_set_rules first \ 258 "set reassemble yes" \ 259 "pass keep state" 260 261 # Sanity checks 262 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 263 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.130 264 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.131 265 266 # Large packet that'll get reassembled and sent out in one on the large 267 # epair 268 atf_check -s exit:0 -o ignore ping -c 1 -s 8000 192.0.2.131 269} 270 271mtu_diff_cleanup() 272{ 273 pft_cleanup 274} 275 276frag_common() 277{ 278 name=$1 279 280 pft_init 281 282 epair=$(vnet_mkepair) 283 vnet_mkjail alcatraz ${epair}a 284 285 ifconfig ${epair}b inet 192.0.2.1/24 up 286 jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up 287 288 jexec alcatraz pfctl -e 289 pft_set_rules alcatraz \ 290 "set reassemble yes" \ 291 "pass keep state" 292 293 # Sanity check 294 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 295 296 atf_check -s exit:0 -o ignore $(atf_get_srcdir)/frag-${1}.py \ 297 --to 192.0.2.2 \ 298 --fromaddr 192.0.2.1 \ 299 --sendif ${epair}b \ 300 --recvif ${epair}b 301} 302 303atf_test_case "overreplace" "cleanup" 304overreplace_head() 305{ 306 atf_set descr 'ping fragment that overlaps fragment at index boundary and replace it' 307 atf_set require.user root 308 atf_set require.progs scapy 309} 310 311overreplace_body() 312{ 313 frag_common overreplace 314} 315 316overreplace_cleanup() 317{ 318 pft_cleanup 319} 320 321atf_test_case "overindex" "cleanup" 322overindex_head() 323{ 324 atf_set descr 'ping fragment that overlaps the first fragment at index boundary' 325 atf_set require.user root 326 atf_set require.progs scapy 327} 328 329overindex_body() 330{ 331 frag_common overindex 332} 333 334overindex_cleanup() 335{ 336 pft_cleanup 337} 338 339atf_test_case "overlimit" "cleanup" 340overlimit_head() 341{ 342 atf_set descr 'ping fragment at index boundary that cannot be requeued' 343 atf_set require.user root 344 atf_set require.progs scapy 345} 346 347overlimit_body() 348{ 349 frag_common overlimit 350} 351 352overlimit_cleanup() 353{ 354 pft_cleanup 355} 356 357atf_test_case "overhole" "cleanup" 358overhole_head() 359{ 360 atf_set descr 'ping fragment at index boundary which modifies pf hole counter' 361 atf_set require.user root 362 atf_set require.progs scapy 363} 364 365overhole_body() 366{ 367 frag_common overhole 368} 369 370overhole_cleanup() 371{ 372 pft_cleanup 373} 374 375atf_test_case "adjhole" "cleanup" 376adjhole_head() 377{ 378 atf_set descr 'overlapping ping fragments which modifies pf hole counter' 379 atf_set require.user root 380 atf_set require.progs scapy 381} 382 383adjhole_body() 384{ 385 frag_common adjhole 386} 387 388adjhole_cleanup() 389{ 390 pft_cleanup 391} 392 393atf_test_case "reassemble" "cleanup" 394reassemble_head() 395{ 396 atf_set descr 'Test reassembly' 397 atf_set require.user root 398} 399 400reassemble_body() 401{ 402 pft_init 403 404 epair=$(vnet_mkepair) 405 vnet_mkjail alcatraz ${epair}a 406 407 ifconfig ${epair}b inet 192.0.2.1/24 up 408 jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up 409 410 # Sanity check 411 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 412 413 jexec alcatraz pfctl -e 414 pft_set_rules alcatraz \ 415 "pass out" \ 416 "block in" \ 417 "pass in inet proto icmp all icmp-type echoreq" 418 419 # Single fragment passes 420 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 421 422 # But a fragmented ping does not 423 atf_check -s exit:2 -o ignore ping -c 1 -s 2000 192.0.2.2 424 425 pft_set_rules alcatraz \ 426 "set reassemble yes" \ 427 "pass out" \ 428 "block in" \ 429 "pass in inet proto icmp all icmp-type echoreq" 430 431 # Both single packet & fragmented pass when we scrub 432 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 433 atf_check -s exit:0 -o ignore ping -c 1 -s 2000 192.0.2.2 434} 435 436reassemble_cleanup() 437{ 438 pft_cleanup 439} 440 441atf_test_case "no_df" "cleanup" 442no_df_head() 443{ 444 atf_set descr 'Test removing of DF flag' 445 atf_set require.user root 446 atf_set require.progs scapy 447} 448 449no_df_body() 450{ 451 setup_router_server_ipv4 452 453 # Tester can send long packets which will get fragmented by the router. 454 # Replies from server will come in fragments which might get 455 # reassembled resulting in a long reply packet sent back to tester. 456 ifconfig ${epair_tester}a mtu 9000 457 jexec router ifconfig ${epair_tester}b mtu 9000 458 jexec router ifconfig ${epair_server}a mtu 1500 459 jexec server ifconfig ${epair_server}b mtu 1500 460 461 # Sanity check. 462 ping_server_check_reply exit:0 --ping-type=icmp 463 464 # Enable packet reassembly with clearing of the no-df flag. 465 pft_set_rules router \ 466 "scrub all fragment reassemble no-df" \ 467 "block" \ 468 "pass inet proto icmp all icmp-type echoreq" 469 # Ping with non-fragmentable packets. 470 # pf will strip the DF flag resulting in fragmentation and packets 471 # getting properly forwarded. 472 ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000 --send-flags DF 473} 474 475no_df_cleanup() 476{ 477 pft_cleanup 478} 479 480atf_test_case "reassemble_slowpath" "cleanup" 481reassemble_slowpath_head() 482{ 483 atf_set descr 'Test reassembly on the slow path' 484 atf_set require.user root 485 atf_set require.progs scapy 486} 487 488reassemble_slowpath_body() 489{ 490 if ! sysctl -q kern.features.ipsec >/dev/null ; then 491 atf_skip "This test requires ipsec" 492 fi 493 494 setup_router_server_ipv4 495 496 # Now define an ipsec policy so we end up taking the slow path. 497 # We don't actually need the traffic to go through ipsec, we just don't 498 # want to go through ip_tryforward(). 499 echo "flush; 500 spdflush; 501 spdadd 203.0.113.1/32 203.0.113.2/32 any -P out ipsec esp/transport//require; 502 add 203.0.113.1 203.0.113.2 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \ 503 | jexec router setkey -c 504 505 # Sanity check. 506 ping_server_check_reply exit:0 --ping-type=icmp 507 508 # Enable packet reassembly with clearing of the no-df flag. 509 pft_set_rules router \ 510 "scrub in on ${epair_tester}b fragment no reassemble" \ 511 "scrub on ${epair_server}a fragment reassemble" \ 512 "pass" 513 514 # Ensure that the packet makes it through the slow path 515 atf_check -s exit:0 -o ignore \ 516 ping -c 1 -s 2000 198.51.100.2 517} 518 519reassemble_slowpath_cleanup() 520{ 521 pft_cleanup 522} 523 524atf_test_case "dummynet" "cleanup" 525dummynet_head() 526{ 527 atf_set descr 'dummynet + reassembly test' 528 atf_set require.user root 529} 530 531dummynet_body() 532{ 533 pft_init 534 dummynet_init 535 536 epair=$(vnet_mkepair) 537 vnet_mkjail alcatraz ${epair}a 538 539 ifconfig ${epair}b inet 192.0.2.1/24 up 540 jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up 541 542 # Sanity check 543 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 544 545 jexec alcatraz dnctl pipe 1 config bw 600Byte/s 546 jexec alcatraz dnctl pipe 2 config bw 700Byte/s 547 548 jexec alcatraz pfctl -e 549 pft_set_rules alcatraz \ 550 "set reassemble yes" \ 551 "block" \ 552 "pass inet proto icmp all icmp-type echoreq dnpipe (1, 2)" 553 554 atf_check -s exit:0 -o ignore ping -s 2000 -c 1 192.0.2.2 555} 556 557dummynet_cleanup() 558{ 559 pft_cleanup 560} 561 562atf_test_case "dummynet_nat" "cleanup" 563dummynet_nat_head() 564{ 565 atf_set descr 'Test dummynet on NATed fragmented traffic' 566 atf_set require.user root 567} 568 569dummynet_nat_body() 570{ 571 pft_init 572 dummynet_init 573 574 epair_one=$(vnet_mkepair) 575 ifconfig ${epair_one}a 192.0.2.1/24 up 576 577 epair_two=$(vnet_mkepair) 578 579 vnet_mkjail alcatraz ${epair_one}b ${epair_two}a 580 jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up 581 jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up 582 jexec alcatraz sysctl net.inet.ip.forwarding=1 583 584 vnet_mkjail singsing ${epair_two}b 585 jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up 586 jexec singsing route add default 198.51.100.1 587 588 route add 198.51.100.0/24 192.0.2.2 589 590 jexec alcatraz dnctl pipe 1 config bw 1600Byte/s 591 jexec alcatraz dnctl pipe 2 config bw 1700Byte/s 592 593 jexec alcatraz pfctl -e 594 pft_set_rules alcatraz \ 595 "set reassemble yes" \ 596 "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \ 597 "block in" \ 598 "pass in inet proto icmp all icmp-type echoreq dnpipe (1, 2)" 599 600 atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2 601 atf_check -s exit:0 -o ignore ping -c 1 -s 2000 198.51.100.2 602} 603 604dummynet_nat_cleanup() 605{ 606 pft_cleanup 607} 608 609atf_test_case "dummynet_fragmented" "cleanup" 610dummynet_fragmented_head() 611{ 612 atf_set descr 'Test dummynet on NATed fragmented traffic' 613 atf_set require.user root 614 atf_set require.progs scapy 615} 616 617dummynet_fragmented_body() 618{ 619 pft_init 620 dummynet_init 621 622 # No test for IPv6. IPv6 fragment reassembly can't be disabled. 623 setup_router_dummy_ipv4 624 625 jexec router dnctl pipe 1 config delay 1 626 627 pft_set_rules router \ 628 "set reassemble no" \ 629 "block" \ 630 "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \ 631 "pass in on ${epair_tester}b inet proto udp dnpipe (1, 1)" \ 632 "pass out on ${epair_server}a inet proto udp" \ 633 634 ping_dummy_check_request exit:0 --ping-type=udp --send-length=10000 --send-frag-length=1280 635 636 rules=$(mktemp) || exit 1 637 jexec router pfctl -qvsr | normalize_pfctl_s > $rules 638 639 # Count that fragmented packets have hit the rule only once and that 640 # they have not created states. There is no stateful firewall support 641 # for fragmented packets. 642 grep -qE 'pass in on epair0b inet proto udp all keep state dnpipe\(1, 1\) .* Packets: 8 Bytes: 10168 States: 0 ' $rules || 643 atf_fail "Fragmented packets not counted correctly" 644} 645 646dummynet_fragmented_cleanup() 647{ 648 pft_cleanup 649} 650 651atf_init_test_cases() 652{ 653 atf_add_test_case "too_many_fragments" 654 atf_add_test_case "v6" 655 atf_add_test_case "v6_route_to" 656 atf_add_test_case "mtu_diff" 657 atf_add_test_case "overreplace" 658 atf_add_test_case "overindex" 659 atf_add_test_case "overlimit" 660 atf_add_test_case "overhole" 661 atf_add_test_case "adjhole" 662 atf_add_test_case "reassemble" 663 atf_add_test_case "no_df" 664 atf_add_test_case "reassemble_slowpath" 665 atf_add_test_case "dummynet" 666 atf_add_test_case "dummynet_nat" 667 atf_add_test_case "dummynet_fragmented" 668} 669