1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2020 Mark Johnston <markj@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 29TABLE_STATS_ZERO_REGEXP='Packets: 0[[:space:]]*Bytes: 0[[:space:]]' 30TABLE_STATS_NONZERO_REGEXP='Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]' 31 32atf_test_case "v4_counters" "cleanup" 33v4_counters_head() 34{ 35 atf_set descr 'Verify per-address counters for v4' 36 atf_set require.user root 37} 38 39v4_counters_body() 40{ 41 pft_init 42 43 epair_send=$(vnet_mkepair) 44 ifconfig ${epair_send}a 192.0.2.1/24 up 45 46 vnet_mkjail alcatraz ${epair_send}b 47 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 48 jexec alcatraz pfctl -e 49 50 pft_set_rules alcatraz \ 51 "table <foo> counters { 192.0.2.1 }" \ 52 "block all" \ 53 "pass in from <foo> to any" \ 54 "pass out from any to <foo>" \ 55 "set skip on lo" 56 57 atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2 58 59 atf_check -s exit:0 -e ignore \ 60 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 61 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 62 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 63 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 64 jexec alcatraz pfctl -t foo -T show -vv 65} 66 67v4_counters_cleanup() 68{ 69 pft_cleanup 70} 71 72atf_test_case "v6_counters" "cleanup" 73v6_counters_head() 74{ 75 atf_set descr 'Verify per-address counters for v6' 76 atf_set require.user root 77} 78 79v6_counters_body() 80{ 81 pft_init 82 83 epair_send=$(vnet_mkepair) 84 ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled 85 86 vnet_mkjail alcatraz ${epair_send}b 87 jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad 88 jexec alcatraz pfctl -e 89 90 pft_set_rules alcatraz \ 91 "table <foo6> counters { 2001:db8:42::1 }" \ 92 "block all" \ 93 "pass in from <foo6> to any" \ 94 "pass out from any to <foo6>" \ 95 "set skip on lo" 96 97 atf_check -s exit:0 -o ignore ping -6 -c 3 2001:db8:42::2 98 99 atf_check -s exit:0 -e ignore \ 100 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 101 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 102 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 103 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 104 jexec alcatraz pfctl -t foo6 -T show -vv 105} 106 107v6_counters_cleanup() 108{ 109 pft_cleanup 110} 111 112atf_test_case "match_counters" "cleanup" 113match_counters_head() 114{ 115 atf_set descr 'Test that counters for tables in match rules work' 116 atf_set require.user root 117} 118 119match_counters_body() 120{ 121 pft_init 122 123 epair_send=$(vnet_mkepair) 124 ifconfig ${epair_send}a 192.0.2.1/24 up 125 126 vnet_mkjail alcatraz ${epair_send}b 127 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 128 jexec alcatraz pfctl -e 129 130 pft_set_rules alcatraz \ 131 "table <foo> counters { 192.0.2.1 }" \ 132 "pass all" \ 133 "match in from <foo> to any" \ 134 "match out from any to <foo>" \ 135 "set skip on lo" 136 137 atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2 138 139 atf_check -s exit:0 -e ignore \ 140 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 141 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 142 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 143 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 144 jexec alcatraz pfctl -t foo -T show -vv 145} 146 147match_counters_cleanup() 148{ 149 pft_cleanup 150} 151 152atf_test_case "zero_one" "cleanup" 153zero_one_head() 154{ 155 atf_set descr 'Test zeroing a single address in a table' 156 atf_set require.user root 157} 158 159pft_cleared_ctime() 160{ 161 jexec "$1" pfctl -t "$2" -vvT show | awk -v ip="$3" ' 162 ($1 == ip) { m = 1 } 163 ($1 == "Cleared:" && m) { 164 sub("[[:space:]]*Cleared:[[:space:]]*", ""); print; exit }' 165} 166 167ctime_to_unixtime() 168{ 169 # NB: it's not TZ=UTC, it's TZ=/etc/localtime 170 date -jf '%a %b %d %H:%M:%S %Y' "$1" '+%s' 171} 172 173zero_one_body() 174{ 175 pft_init 176 177 epair_send=$(vnet_mkepair) 178 ifconfig ${epair_send}a 192.0.2.1/24 up 179 ifconfig ${epair_send}a inet alias 192.0.2.3/24 180 181 vnet_mkjail alcatraz ${epair_send}b 182 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 183 jexec alcatraz pfctl -e 184 185 pft_set_rules alcatraz \ 186 "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \ 187 "block all" \ 188 "pass in from <foo> to any" \ 189 "pass out from any to <foo>" \ 190 "set skip on lo" 191 192 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2 193 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2 194 195 jexec alcatraz pfctl -t foo -T show -vv 196 197 atf_check -s exit:0 -e ignore \ 198 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 199 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 200 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 201 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 202 jexec alcatraz pfctl -t foo -T show -vv 203 204 local uniq base ts1 ts3 205 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 206 atf_check_equal 1 "$uniq" # time they were added 207 208 base=`pft_cleared_ctime alcatraz foo 192.0.2.1` 209 210 atf_check -s exit:0 -e ignore \ 211 jexec alcatraz pfctl -t foo -T zero 192.0.2.3 212 213 ts1=`pft_cleared_ctime alcatraz foo 192.0.2.1` 214 atf_check_equal "$base" "$ts1" 215 216 ts3=`pft_cleared_ctime alcatraz foo 192.0.2.3` 217 atf_check test "$ts1" != "$ts3" 218 219 ts1=`ctime_to_unixtime "$ts1"` 220 ts3=`ctime_to_unixtime "$ts3"` 221 atf_check test $(( "$ts3" - "$ts1" )) -lt 10 # (3 pings * 2) + epsilon 222 atf_check test "$ts1" -lt "$ts3" 223 224 # We now have a zeroed and a non-zeroed counter, so both patterns 225 # should match 226 atf_check -s exit:0 -e ignore \ 227 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 228 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 229 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 230 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 231 jexec alcatraz pfctl -t foo -T show -vv 232} 233 234zero_one_cleanup() 235{ 236 pft_cleanup 237} 238 239atf_test_case "zero_all" "cleanup" 240zero_all_head() 241{ 242 atf_set descr 'Test zeroing all table entries' 243 atf_set require.user root 244} 245 246zero_all_body() 247{ 248 pft_init 249 250 epair_send=$(vnet_mkepair) 251 ifconfig ${epair_send}a 192.0.2.1/24 up 252 ifconfig ${epair_send}a inet alias 192.0.2.3/24 253 254 vnet_mkjail alcatraz ${epair_send}b 255 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 256 jexec alcatraz pfctl -e 257 258 pft_set_rules alcatraz \ 259 "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \ 260 "block all" \ 261 "pass in from <foo> to any" \ 262 "pass out from any to <foo>" \ 263 "set skip on lo" 264 265 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2 266 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2 267 268 jexec alcatraz pfctl -t foo -T show -vv 269 atf_check -s exit:0 -e ignore \ 270 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 271 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 272 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 273 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 274 jexec alcatraz pfctl -t foo -T show -vv 275 276 atf_check -s exit:0 -e ignore \ 277 jexec alcatraz pfctl -t foo -T zero 278 279 jexec alcatraz pfctl -t foo -T show -vv 280 atf_check -s exit:0 -e ignore \ 281 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 282 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 283 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 284 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 285 jexec alcatraz pfctl -t foo -T show -vv 286} 287 288zero_all_cleanup() 289{ 290 pft_cleanup 291} 292 293atf_test_case "reset_nonzero" "cleanup" 294reset_nonzero_head() 295{ 296 atf_set descr 'Test zeroing an address with non-zero counters' 297 atf_set require.user root 298} 299 300reset_nonzero_body() 301{ 302 pft_init 303 304 epair_send=$(vnet_mkepair) 305 ifconfig ${epair_send}a 192.0.2.1/24 up 306 ifconfig ${epair_send}a inet alias 192.0.2.3/24 307 308 vnet_mkjail alcatraz ${epair_send}b 309 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 310 jexec alcatraz pfctl -e 311 312 pft_set_rules alcatraz \ 313 "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \ 314 "table <bar> counters { }" \ 315 "block all" \ 316 "pass in from <foo> to any" \ 317 "pass out from any to <foo>" \ 318 "pass on notReallyAnIf from <bar> to <bar>" \ 319 "set skip on lo" 320 321 # Nonexisting table can't be reset, following `-T show`. 322 atf_check -o ignore \ 323 -s not-exit:0 \ 324 -e inline:"pfctl: Table does not exist.\n" \ 325 jexec alcatraz pfctl -t nonexistent -T reset 326 327 atf_check -o ignore \ 328 -s exit:0 \ 329 -e inline:"0/0 stats cleared.\n" \ 330 jexec alcatraz pfctl -t bar -T reset 331 332 # No-op is a valid operation. 333 atf_check -s exit:0 \ 334 -e inline:"0/2 stats cleared.\n" \ 335 jexec alcatraz pfctl -t foo -T reset 336 337 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2 338 339 atf_check -s exit:0 -e ignore \ 340 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 341 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 342 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 343 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 344 jexec alcatraz pfctl -t foo -vvT show 345 346 local clrd uniq 347 clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared` 348 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 349 atf_check_equal "$clrd" 2 350 atf_check_equal "$uniq" 1 # time they were added 351 352 atf_check -s exit:0 -e ignore \ 353 -e inline:"1/2 stats cleared.\n" \ 354 jexec alcatraz pfctl -t foo -T reset 355 356 clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared` 357 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 358 atf_check_equal "$clrd" 2 359 atf_check_equal "$uniq" 2 # 192.0.2.3 should get new timestamp 360 361 atf_check -s exit:0 -e ignore \ 362 -o not-match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 363 -o not-match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 364 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 365 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 366 jexec alcatraz pfctl -t foo -vvT show 367} 368 369reset_nonzero_cleanup() 370{ 371 pft_cleanup 372} 373 374atf_test_case "pr251414" "cleanup" 375pr251414_head() 376{ 377 atf_set descr 'Test PR 251414' 378 atf_set require.user root 379} 380 381pr251414_body() 382{ 383 pft_init 384 385 epair_send=$(vnet_mkepair) 386 ifconfig ${epair_send}a 192.0.2.1/24 up 387 388 vnet_mkjail alcatraz ${epair_send}b 389 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 390 jexec alcatraz pfctl -e 391 392 pft_set_rules alcatraz \ 393 "pass all" \ 394 "table <tab> { self }" \ 395 "pass in log to <tab>" 396 397 pft_set_rules noflush alcatraz \ 398 "pass all" \ 399 "table <tab> counters { self }" \ 400 "pass in log to <tab>" 401 402 atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2 403 404 jexec alcatraz pfctl -t tab -T show -vv 405} 406 407pr251414_cleanup() 408{ 409 pft_cleanup 410} 411 412atf_test_case "automatic" "cleanup" 413automatic_head() 414{ 415 atf_set descr "Test automatic - optimizer generated - tables" 416 atf_set require.user root 417} 418 419automatic_body() 420{ 421 pft_init 422 423 epair=$(vnet_mkepair) 424 ifconfig ${epair}a 192.0.2.1/24 up 425 426 vnet_mkjail alcatraz ${epair}b 427 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 428 jexec alcatraz pfctl -e 429 430 pft_set_rules alcatraz \ 431 "block in" \ 432 "pass in proto icmp from 192.0.2.1" \ 433 "pass in proto icmp from 192.0.2.3" \ 434 "pass in proto icmp from 192.0.2.4" \ 435 "pass in proto icmp from 192.0.2.5" \ 436 "pass in proto icmp from 192.0.2.6" \ 437 "pass in proto icmp from 192.0.2.7" \ 438 "pass in proto icmp from 192.0.2.8" \ 439 "pass in proto icmp from 192.0.2.9" 440 441 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 442} 443 444automatic_cleanup() 445{ 446 pft_cleanup 447} 448 449atf_test_case "network" "cleanup" 450network_head() 451{ 452 atf_set descr 'Test <ifgroup>:network' 453 atf_set require.user root 454} 455 456network_body() 457{ 458 pft_init 459 460 epair=$(vnet_mkepair) 461 ifconfig ${epair}a 192.0.2.1/24 up 462 463 vnet_mkjail alcatraz ${epair}b 464 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 465 jexec alcatraz pfctl -e 466 467 pft_set_rules alcatraz \ 468 "table <allow> const { epair:network }"\ 469 "block in" \ 470 "pass in from <allow>" 471 472 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 473} 474 475network_cleanup() 476{ 477 pft_cleanup 478} 479 480atf_test_case "pr259689" "cleanup" 481pr259689_head() 482{ 483 atf_set descr 'Test PR 259689' 484 atf_set require.user root 485} 486 487pr259689_body() 488{ 489 pft_init 490 491 vnet_mkjail alcatraz 492 jexec alcatraz pfctl -e 493 494 pft_set_rules alcatraz \ 495 "pass in" \ 496 "block in inet from { 1.1.1.1, 1.1.1.2, 2.2.2.2, 2.2.2.3, 4.4.4.4, 4.4.4.5 }" 497 498 atf_check -o match:'block drop in inet from <__automatic_.*:6> to any' \ 499 -e ignore \ 500 jexec alcatraz pfctl -sr -vv 501} 502 503pr259689_cleanup() 504{ 505 pft_cleanup 506} 507 508atf_test_case "precreate" "cleanup" 509precreate_head() 510{ 511 atf_set descr 'Test creating a table without counters, then loading rules that add counters' 512 atf_set require.user root 513} 514 515precreate_body() 516{ 517 pft_init 518 519 vnet_mkjail alcatraz 520 521 jexec alcatraz pfctl -t foo -T add 192.0.2.1 522 jexec alcatraz pfctl -t foo -T show 523 524 pft_set_rules noflush alcatraz \ 525 "table <foo> counters persist" \ 526 "pass in from <foo>" 527 528 # Expect all counters to be zero 529 atf_check -s exit:0 -e ignore \ 530 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 531 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 532 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 533 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 534 jexec alcatraz pfctl -t foo -T show -vv 535 536} 537 538precreate_cleanup() 539{ 540 pft_cleanup 541} 542 543atf_test_case "anchor" "cleanup" 544anchor_head() 545{ 546 atf_set descr 'Test tables in anchors' 547 atf_set require.user root 548} 549 550anchor_body() 551{ 552 pft_init 553 554 epair=$(vnet_mkepair) 555 ifconfig ${epair}a 192.0.2.1/24 up 556 557 vnet_mkjail alcatraz ${epair}b 558 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 559 jexec alcatraz pfctl -e 560 561 (echo "table <testtable> persist" 562 echo "block in quick from <testtable> to any" 563 ) | jexec alcatraz pfctl -a anchorage -f - 564 565 pft_set_rules noflush alcatraz \ 566 "pass" \ 567 "anchor anchorage" 568 569 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 570 571 # Tables belong to anchors, so this is a different table and won't affect anything 572 jexec alcatraz pfctl -t testtable -T add 192.0.2.1 573 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 574 575 # But when we add the address to the table in the anchor it does block traffic 576 jexec alcatraz pfctl -a anchorage -t testtable -T add 192.0.2.1 577 atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2 578} 579 580anchor_cleanup() 581{ 582 pft_cleanup 583} 584 585atf_init_test_cases() 586{ 587 atf_add_test_case "v4_counters" 588 atf_add_test_case "v6_counters" 589 atf_add_test_case "match_counters" 590 atf_add_test_case "zero_one" 591 atf_add_test_case "zero_all" 592 atf_add_test_case "reset_nonzero" 593 atf_add_test_case "pr251414" 594 atf_add_test_case "automatic" 595 atf_add_test_case "network" 596 atf_add_test_case "pr259689" 597 atf_add_test_case "precreate" 598 atf_add_test_case "anchor" 599} 600