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 "reset_nonzero" "cleanup" 240reset_nonzero_head() 241{ 242 atf_set descr 'Test zeroing an address with non-zero counters' 243 atf_set require.user root 244} 245 246reset_nonzero_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 "table <bar> counters { }" \ 261 "block all" \ 262 "pass in from <foo> to any" \ 263 "pass out from any to <foo>" \ 264 "pass on notReallyAnIf from <bar> to <bar>" \ 265 "set skip on lo" 266 267 # Nonexisting table can't be reset, following `-T show`. 268 atf_check -o ignore \ 269 -s not-exit:0 \ 270 -e inline:"pfctl: Table does not exist.\n" \ 271 jexec alcatraz pfctl -t nonexistent -T reset 272 273 atf_check -o ignore \ 274 -s exit:0 \ 275 -e inline:"0/0 stats cleared.\n" \ 276 jexec alcatraz pfctl -t bar -T reset 277 278 # No-op is a valid operation. 279 atf_check -s exit:0 \ 280 -e inline:"0/2 stats cleared.\n" \ 281 jexec alcatraz pfctl -t foo -T reset 282 283 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2 284 285 atf_check -s exit:0 -e ignore \ 286 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 287 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 288 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 289 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 290 jexec alcatraz pfctl -t foo -vvT show 291 292 local clrd uniq 293 clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared` 294 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 295 atf_check_equal "$clrd" 2 296 atf_check_equal "$uniq" 1 # time they were added 297 298 atf_check -s exit:0 -e ignore \ 299 -e inline:"1/2 stats cleared.\n" \ 300 jexec alcatraz pfctl -t foo -T reset 301 302 clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared` 303 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 304 atf_check_equal "$clrd" 2 305 atf_check_equal "$uniq" 2 # 192.0.2.3 should get new timestamp 306 307 atf_check -s exit:0 -e ignore \ 308 -o not-match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 309 -o not-match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 310 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 311 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 312 jexec alcatraz pfctl -t foo -vvT show 313} 314 315reset_nonzero_cleanup() 316{ 317 pft_cleanup 318} 319 320atf_test_case "pr251414" "cleanup" 321pr251414_head() 322{ 323 atf_set descr 'Test PR 251414' 324 atf_set require.user root 325} 326 327pr251414_body() 328{ 329 pft_init 330 331 epair_send=$(vnet_mkepair) 332 ifconfig ${epair_send}a 192.0.2.1/24 up 333 334 vnet_mkjail alcatraz ${epair_send}b 335 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 336 jexec alcatraz pfctl -e 337 338 pft_set_rules alcatraz \ 339 "pass all" \ 340 "table <tab> { self }" \ 341 "pass in log to <tab>" 342 343 pft_set_rules noflush alcatraz \ 344 "pass all" \ 345 "table <tab> counters { self }" \ 346 "pass in log to <tab>" 347 348 atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2 349 350 jexec alcatraz pfctl -t tab -T show -vv 351} 352 353pr251414_cleanup() 354{ 355 pft_cleanup 356} 357 358atf_test_case "automatic" "cleanup" 359automatic_head() 360{ 361 atf_set descr "Test automatic - optimizer generated - tables" 362 atf_set require.user root 363} 364 365automatic_body() 366{ 367 pft_init 368 369 epair=$(vnet_mkepair) 370 ifconfig ${epair}a 192.0.2.1/24 up 371 372 vnet_mkjail alcatraz ${epair}b 373 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 374 jexec alcatraz pfctl -e 375 376 pft_set_rules alcatraz \ 377 "block in" \ 378 "pass in proto icmp from 192.0.2.1" \ 379 "pass in proto icmp from 192.0.2.3" \ 380 "pass in proto icmp from 192.0.2.4" \ 381 "pass in proto icmp from 192.0.2.5" \ 382 "pass in proto icmp from 192.0.2.6" \ 383 "pass in proto icmp from 192.0.2.7" \ 384 "pass in proto icmp from 192.0.2.8" \ 385 "pass in proto icmp from 192.0.2.9" 386 387 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 388} 389 390automatic_cleanup() 391{ 392 pft_cleanup 393} 394 395atf_test_case "network" "cleanup" 396network_head() 397{ 398 atf_set descr 'Test <ifgroup>:network' 399 atf_set require.user root 400} 401 402network_body() 403{ 404 pft_init 405 406 epair=$(vnet_mkepair) 407 ifconfig ${epair}a 192.0.2.1/24 up 408 409 vnet_mkjail alcatraz ${epair}b 410 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 411 jexec alcatraz pfctl -e 412 413 pft_set_rules alcatraz \ 414 "table <allow> const { epair:network }"\ 415 "block in" \ 416 "pass in from <allow>" 417 418 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 419} 420 421network_cleanup() 422{ 423 pft_cleanup 424} 425 426atf_test_case "pr259689" "cleanup" 427pr259689_head() 428{ 429 atf_set descr 'Test PR 259689' 430 atf_set require.user root 431} 432 433pr259689_body() 434{ 435 pft_init 436 437 vnet_mkjail alcatraz 438 jexec alcatraz pfctl -e 439 440 pft_set_rules alcatraz \ 441 "pass in" \ 442 "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 }" 443 444 atf_check -o match:'block drop in inet from <__automatic_.*:6> to any' \ 445 -e ignore \ 446 jexec alcatraz pfctl -sr -vv 447} 448 449pr259689_cleanup() 450{ 451 pft_cleanup 452} 453 454atf_test_case "precreate" "cleanup" 455precreate_head() 456{ 457 atf_set descr 'Test creating a table without counters, then loading rules that add counters' 458 atf_set require.user root 459} 460 461precreate_body() 462{ 463 pft_init 464 465 vnet_mkjail alcatraz 466 467 jexec alcatraz pfctl -t foo -T add 192.0.2.1 468 jexec alcatraz pfctl -t foo -T show 469 470 pft_set_rules noflush alcatraz \ 471 "table <foo> counters persist" \ 472 "pass in from <foo>" 473 474 # Expect all counters to be zero 475 atf_check -s exit:0 -e ignore \ 476 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 477 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 478 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 479 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 480 jexec alcatraz pfctl -t foo -T show -vv 481 482} 483 484precreate_cleanup() 485{ 486 pft_cleanup 487} 488 489atf_test_case "anchor" "cleanup" 490anchor_head() 491{ 492 atf_set descr 'Test tables in anchors' 493 atf_set require.user root 494} 495 496anchor_body() 497{ 498 pft_init 499 500 epair=$(vnet_mkepair) 501 ifconfig ${epair}a 192.0.2.1/24 up 502 503 vnet_mkjail alcatraz ${epair}b 504 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 505 jexec alcatraz pfctl -e 506 507 (echo "table <testtable> persist" 508 echo "block in quick from <testtable> to any" 509 ) | jexec alcatraz pfctl -a anchorage -f - 510 511 pft_set_rules noflush alcatraz \ 512 "pass" \ 513 "anchor anchorage" 514 515 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 516 517 # Tables belong to anchors, so this is a different table and won't affect anything 518 jexec alcatraz pfctl -t testtable -T add 192.0.2.1 519 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 520 521 # But when we add the address to the table in the anchor it does block traffic 522 jexec alcatraz pfctl -a anchorage -t testtable -T add 192.0.2.1 523 atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2 524} 525 526anchor_cleanup() 527{ 528 pft_cleanup 529} 530 531atf_init_test_cases() 532{ 533 atf_add_test_case "v4_counters" 534 atf_add_test_case "v6_counters" 535 atf_add_test_case "match_counters" 536 atf_add_test_case "zero_one" 537 atf_add_test_case "reset_nonzero" 538 atf_add_test_case "pr251414" 539 atf_add_test_case "automatic" 540 atf_add_test_case "network" 541 atf_add_test_case "pr259689" 542 atf_add_test_case "precreate" 543 atf_add_test_case "anchor" 544} 545