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 "zero_one" "cleanup" 113zero_one_head() 114{ 115 atf_set descr 'Test zeroing a single address in a table' 116 atf_set require.user root 117} 118 119pft_cleared_ctime() 120{ 121 jexec "$1" pfctl -t "$2" -vvT show | awk -v ip="$3" ' 122 ($1 == ip) { m = 1 } 123 ($1 == "Cleared:" && m) { 124 sub("[[:space:]]*Cleared:[[:space:]]*", ""); print; exit }' 125} 126 127ctime_to_unixtime() 128{ 129 # NB: it's not TZ=UTC, it's TZ=/etc/localtime 130 date -jf '%a %b %d %H:%M:%S %Y' "$1" '+%s' 131} 132 133zero_one_body() 134{ 135 epair_send=$(vnet_mkepair) 136 ifconfig ${epair_send}a 192.0.2.1/24 up 137 ifconfig ${epair_send}a inet alias 192.0.2.3/24 138 139 vnet_mkjail alcatraz ${epair_send}b 140 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 141 jexec alcatraz pfctl -e 142 143 pft_set_rules alcatraz \ 144 "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \ 145 "block all" \ 146 "pass in from <foo> to any" \ 147 "pass out from any to <foo>" \ 148 "set skip on lo" 149 150 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2 151 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2 152 153 jexec alcatraz pfctl -t foo -T show -vv 154 155 atf_check -s exit:0 -e ignore \ 156 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 157 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 158 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 159 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 160 jexec alcatraz pfctl -t foo -T show -vv 161 162 local uniq base ts1 ts3 163 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 164 atf_check_equal 1 "$uniq" # time they were added 165 166 base=`pft_cleared_ctime alcatraz foo 192.0.2.1` 167 168 atf_check -s exit:0 -e ignore \ 169 jexec alcatraz pfctl -t foo -T zero 192.0.2.3 170 171 ts1=`pft_cleared_ctime alcatraz foo 192.0.2.1` 172 atf_check_equal "$base" "$ts1" 173 174 ts3=`pft_cleared_ctime alcatraz foo 192.0.2.3` 175 atf_check test "$ts1" != "$ts3" 176 177 ts1=`ctime_to_unixtime "$ts1"` 178 ts3=`ctime_to_unixtime "$ts3"` 179 atf_check test $(( "$ts3" - "$ts1" )) -lt 10 # (3 pings * 2) + epsilon 180 atf_check test "$ts1" -lt "$ts3" 181 182 # We now have a zeroed and a non-zeroed counter, so both patterns 183 # should match 184 atf_check -s exit:0 -e ignore \ 185 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 186 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 187 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 188 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 189 jexec alcatraz pfctl -t foo -T show -vv 190} 191 192zero_one_cleanup() 193{ 194 pft_cleanup 195} 196 197atf_test_case "reset_nonzero" "cleanup" 198reset_nonzero_head() 199{ 200 atf_set descr 'Test zeroing an address with non-zero counters' 201 atf_set require.user root 202} 203 204reset_nonzero_body() 205{ 206 epair_send=$(vnet_mkepair) 207 ifconfig ${epair_send}a 192.0.2.1/24 up 208 ifconfig ${epair_send}a inet alias 192.0.2.3/24 209 210 vnet_mkjail alcatraz ${epair_send}b 211 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 212 jexec alcatraz pfctl -e 213 214 pft_set_rules alcatraz \ 215 "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \ 216 "table <bar> counters { }" \ 217 "block all" \ 218 "pass in from <foo> to any" \ 219 "pass out from any to <foo>" \ 220 "pass on notReallyAnIf from <bar> to <bar>" \ 221 "set skip on lo" 222 223 # Nonexisting table can't be reset, following `-T show`. 224 atf_check -o ignore \ 225 -s not-exit:0 \ 226 -e inline:"pfctl: Table does not exist.\n" \ 227 jexec alcatraz pfctl -t nonexistent -T reset 228 229 atf_check -o ignore \ 230 -s exit:0 \ 231 -e inline:"0/0 stats cleared.\n" \ 232 jexec alcatraz pfctl -t bar -T reset 233 234 # No-op is a valid operation. 235 atf_check -s exit:0 \ 236 -e inline:"0/2 stats cleared.\n" \ 237 jexec alcatraz pfctl -t foo -T reset 238 239 atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2 240 241 atf_check -s exit:0 -e ignore \ 242 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 243 -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 244 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 245 -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 246 jexec alcatraz pfctl -t foo -vvT show 247 248 local clrd uniq 249 clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared` 250 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 251 atf_check_equal "$clrd" 2 252 atf_check_equal "$uniq" 1 # time they were added 253 254 atf_check -s exit:0 -e ignore \ 255 -e inline:"1/2 stats cleared.\n" \ 256 jexec alcatraz pfctl -t foo -T reset 257 258 clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared` 259 uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared` 260 atf_check_equal "$clrd" 2 261 atf_check_equal "$uniq" 2 # 192.0.2.3 should get new timestamp 262 263 atf_check -s exit:0 -e ignore \ 264 -o not-match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 265 -o not-match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \ 266 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 267 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 268 jexec alcatraz pfctl -t foo -vvT show 269} 270 271reset_nonzero_cleanup() 272{ 273 pft_cleanup 274} 275 276atf_test_case "pr251414" "cleanup" 277pr251414_head() 278{ 279 atf_set descr 'Test PR 251414' 280 atf_set require.user root 281} 282 283pr251414_body() 284{ 285 pft_init 286 287 epair_send=$(vnet_mkepair) 288 ifconfig ${epair_send}a 192.0.2.1/24 up 289 290 vnet_mkjail alcatraz ${epair_send}b 291 jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up 292 jexec alcatraz pfctl -e 293 294 pft_set_rules alcatraz \ 295 "pass all" \ 296 "table <tab> { self }" \ 297 "pass in log to <tab>" 298 299 pft_set_rules noflush alcatraz \ 300 "pass all" \ 301 "table <tab> counters { self }" \ 302 "pass in log to <tab>" 303 304 atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2 305 306 jexec alcatraz pfctl -t tab -T show -vv 307} 308 309pr251414_cleanup() 310{ 311 pft_cleanup 312} 313 314atf_test_case "automatic" "cleanup" 315automatic_head() 316{ 317 atf_set descr "Test automatic - optimizer generated - tables" 318 atf_set require.user root 319} 320 321automatic_body() 322{ 323 pft_init 324 325 epair=$(vnet_mkepair) 326 ifconfig ${epair}a 192.0.2.1/24 up 327 328 vnet_mkjail alcatraz ${epair}b 329 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 330 jexec alcatraz pfctl -e 331 332 pft_set_rules alcatraz \ 333 "block in" \ 334 "pass in proto icmp from 192.0.2.1" \ 335 "pass in proto icmp from 192.0.2.3" \ 336 "pass in proto icmp from 192.0.2.4" \ 337 "pass in proto icmp from 192.0.2.5" \ 338 "pass in proto icmp from 192.0.2.6" \ 339 "pass in proto icmp from 192.0.2.7" \ 340 "pass in proto icmp from 192.0.2.8" \ 341 "pass in proto icmp from 192.0.2.9" 342 343 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 344} 345 346automatic_cleanup() 347{ 348 pft_cleanup 349} 350 351atf_test_case "network" "cleanup" 352network_head() 353{ 354 atf_set descr 'Test <ifgroup>:network' 355 atf_set require.user root 356} 357 358network_body() 359{ 360 pft_init 361 362 epair=$(vnet_mkepair) 363 ifconfig ${epair}a 192.0.2.1/24 up 364 365 vnet_mkjail alcatraz ${epair}b 366 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 367 jexec alcatraz pfctl -e 368 369 pft_set_rules alcatraz \ 370 "table <allow> const { epair:network }"\ 371 "block in" \ 372 "pass in from <allow>" 373 374 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 375} 376 377network_cleanup() 378{ 379 pft_cleanup 380} 381 382atf_test_case "pr259689" "cleanup" 383pr259689_head() 384{ 385 atf_set descr 'Test PR 259689' 386 atf_set require.user root 387} 388 389pr259689_body() 390{ 391 pft_init 392 393 vnet_mkjail alcatraz 394 jexec alcatraz pfctl -e 395 396 pft_set_rules alcatraz \ 397 "pass in" \ 398 "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 }" 399 400 atf_check -o match:'block drop in inet from <__automatic_.*:6> to any' \ 401 -e ignore \ 402 jexec alcatraz pfctl -sr -vv 403} 404 405pr259689_cleanup() 406{ 407 pft_cleanup 408} 409 410atf_test_case "precreate" "cleanup" 411precreate_head() 412{ 413 atf_set descr 'Test creating a table without counters, then loading rules that add counters' 414 atf_set require.user root 415} 416 417precreate_body() 418{ 419 pft_init 420 421 vnet_mkjail alcatraz 422 423 jexec alcatraz pfctl -t foo -T add 192.0.2.1 424 jexec alcatraz pfctl -t foo -T show 425 426 pft_set_rules noflush alcatraz \ 427 "table <foo> counters persist" \ 428 "pass in from <foo>" 429 430 # Expect all counters to be zero 431 atf_check -s exit:0 -e ignore \ 432 -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 433 -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 434 -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \ 435 -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \ 436 jexec alcatraz pfctl -t foo -T show -vv 437 438} 439 440precreate_cleanup() 441{ 442 pft_cleanup 443} 444 445atf_test_case "anchor" "cleanup" 446anchor_head() 447{ 448 atf_set descr 'Test tables in anchors' 449 atf_set require.user root 450} 451 452anchor_body() 453{ 454 pft_init 455 456 epair=$(vnet_mkepair) 457 ifconfig ${epair}a 192.0.2.1/24 up 458 459 vnet_mkjail alcatraz ${epair}b 460 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 461 jexec alcatraz pfctl -e 462 463 (echo "table <testtable> persist" 464 echo "block in quick from <testtable> to any" 465 ) | jexec alcatraz pfctl -a anchorage -f - 466 467 pft_set_rules noflush alcatraz \ 468 "pass" \ 469 "anchor anchorage" 470 471 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 472 473 # Tables belong to anchors, so this is a different table and won't affect anything 474 jexec alcatraz pfctl -t testtable -T add 192.0.2.1 475 atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2 476 477 # But when we add the address to the table in the anchor it does block traffic 478 jexec alcatraz pfctl -a anchorage -t testtable -T add 192.0.2.1 479 atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2 480} 481 482anchor_cleanup() 483{ 484 pft_cleanup 485} 486 487atf_init_test_cases() 488{ 489 atf_add_test_case "v4_counters" 490 atf_add_test_case "v6_counters" 491 atf_add_test_case "zero_one" 492 atf_add_test_case "reset_nonzero" 493 atf_add_test_case "pr251414" 494 atf_add_test_case "automatic" 495 atf_add_test_case "network" 496 atf_add_test_case "pr259689" 497 atf_add_test_case "precreate" 498 atf_add_test_case "anchor" 499} 500