1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2023 Rubicon Communications, LLC (Netgate) 5# Copyright (c) 2024 Deciso B.V. 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 30common_dir=$(atf_get_srcdir)/../common 31 32atf_test_case "malformed" "cleanup" 33malformed_head() 34{ 35 atf_set descr 'Test that we do not log malformed packets as passing' 36 atf_set require.user root 37 atf_set require.progs scapy 38} 39 40malformed_body() 41{ 42 pflog_init 43 44 epair=$(vnet_mkepair) 45 46 vnet_mkjail srv ${epair}b 47 jexec srv ifconfig ${epair}b 192.0.2.1/24 up 48 49 vnet_mkjail cl ${epair}a 50 jexec cl ifconfig ${epair}a 192.0.2.2/24 up 51 52 jexec cl pfctl -e 53 jexec cl ifconfig pflog0 up 54 pft_set_rules cl \ 55 "pass log keep state" 56 57 # Not required, but the 'pf: dropping packet with ip options' kernel log can 58 # help when debugging the test. 59 jexec cl pfctl -x loud 60 61 jexec cl tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt & 62 sleep 1 # Wait for tcpdump to start 63 64 # Sanity check 65 atf_check -s exit:0 -o ignore \ 66 jexec srv ping -c 1 192.0.2.2 67 68 jexec srv ${common_dir}/pft_ping.py \ 69 --sendif ${epair}b \ 70 --to 192.0.2.2 \ 71 --send-nop \ 72 --recvif ${epair}b 73 74 atf_check -o match:".*rule 0/8\(ip-option\): block in on ${epair}a: 192.0.2.1 > 192.0.2.2: ICMP echo request.*" \ 75 cat pflog.txt 76} 77 78malformed_cleanup() 79{ 80 pft_cleanup 81} 82 83atf_test_case "matches" "cleanup" 84matches_head() 85{ 86 atf_set descr 'Test the pflog matches keyword' 87 atf_set require.user root 88} 89 90matches_body() 91{ 92 pflog_init 93 94 epair=$(vnet_mkepair) 95 96 vnet_mkjail alcatraz ${epair}a 97 jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up 98 99 ifconfig ${epair}b 192.0.2.2/24 up 100 101 # Sanity check 102 atf_check -s exit:0 -o ignore \ 103 ping -c 1 192.0.2.1 104 105 jexec alcatraz pfctl -e 106 jexec alcatraz ifconfig pflog0 up 107 pft_set_rules alcatraz \ 108 "match log(matches) inet proto icmp" \ 109 "match log(matches) inet from 192.0.2.2" \ 110 "pass" 111 112 jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog.txt & 113 sleep 1 # Wait for tcpdump to start 114 115 atf_check -s exit:0 -o ignore \ 116 ping -c 1 192.0.2.1 117 118 echo "Rules" 119 jexec alcatraz pfctl -sr -vv 120 echo "States" 121 jexec alcatraz pfctl -ss -vv 122 echo "Log" 123 cat ${PWD}/pflog.txt 124 125 atf_check -o match:".*rule 0/0\(match\): match in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \ 126 cat pflog.txt 127 atf_check -o match:".*rule 1/0\(match\): match in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \ 128 cat pflog.txt 129} 130 131matches_cleanup() 132{ 133 pft_cleanup 134} 135 136atf_test_case "state_max" "cleanup" 137state_max_head() 138{ 139 atf_set descr 'Ensure that drops due to state limits are logged' 140 atf_set require.user root 141} 142 143state_max_body() 144{ 145 pflog_init 146 147 epair=$(vnet_mkepair) 148 149 vnet_mkjail alcatraz ${epair}a 150 jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up 151 152 ifconfig ${epair}b 192.0.2.2/24 up 153 154 # Sanity check 155 atf_check -s exit:0 -o ignore \ 156 ping -c 1 192.0.2.1 157 158 jexec alcatraz pfctl -e 159 jexec alcatraz ifconfig pflog0 up 160 pft_set_rules alcatraz "pass log inet keep state (max 1)" 161 162 jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog.txt & 163 sleep 1 # Wait for tcpdump to start 164 165 atf_check -s exit:0 -o ignore \ 166 ping -c 1 192.0.2.1 167 168 atf_check -s exit:2 -o ignore \ 169 ping -c 1 192.0.2.1 170 171 echo "Rules" 172 jexec alcatraz pfctl -sr -vv 173 echo "States" 174 jexec alcatraz pfctl -ss -vv 175 echo "Log" 176 cat ${PWD}/pflog.txt 177 178 # First ping passes. 179 atf_check -o match:".*rule 0/0\(match\): pass in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \ 180 cat pflog.txt 181 182 # Second ping is blocked due to the state limit. 183 atf_check -o match:".*rule 0/0\(match\): block in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \ 184 cat pflog.txt 185 186 # At most three lines should be written: one for the first ping, and 187 # two for the second: one for the initial pass through the ruleset, and 188 # then a drop because of the state limit. Ideally only the drop would 189 # be logged; if this is fixed, the count will be 2 instead of 3. 190 atf_check -o match:3 grep -c . pflog.txt 191} 192 193state_max_cleanup() 194{ 195 pft_cleanup 196} 197 198atf_test_case "unspecified_v4" "cleanup" 199unspecified_v4_head() 200{ 201 atf_set descr 'Ensure that packets to the unspecified address are visible to pfil hooks' 202 atf_set require.user root 203} 204 205unspecified_v4_body() 206{ 207 pflog_init 208 209 vnet_mkjail alcatraz 210 jexec alcatraz ifconfig lo0 inet 127.0.0.1 211 jexec alcatraz route add default 127.0.0.1 212 213 jexec alcatraz pfctl -e 214 jexec alcatraz ifconfig pflog0 up 215 pft_set_rules alcatraz "block log on lo0 to 0.0.0.0" 216 217 jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt & 218 sleep 1 # Wait for tcpdump to start 219 220 atf_check -s not-exit:0 -o ignore -e ignore \ 221 jexec alcatraz ping -S 127.0.0.1 -c 1 0.0.0.0 222 223 atf_check -o match:".*: block out on lo0: 127.0.0.1 > 0.0.0.0: ICMP echo request,.*" \ 224 cat pflog.txt 225} 226 227unspecified_v4_cleanup() 228{ 229 pft_cleanup 230} 231 232atf_test_case "unspecified_v6" "cleanup" 233unspecified_v6_head() 234{ 235 atf_set descr 'Ensure that packets to the unspecified address are visible to pfil hooks' 236 atf_set require.user root 237} 238 239unspecified_v6_body() 240{ 241 pflog_init 242 243 vnet_mkjail alcatraz 244 jexec alcatraz ifconfig lo0 up 245 jexec alcatraz route -6 add ::0 ::1 246 247 jexec alcatraz pfctl -e 248 jexec alcatraz ifconfig pflog0 up 249 pft_set_rules alcatraz "block log on lo0 to ::0" 250 251 jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt & 252 sleep 1 # Wait for tcpdump to start 253 254 atf_check -s not-exit:0 -o ignore -e ignore \ 255 jexec alcatraz ping -6 -S ::1 -c 1 ::0 256 257 cat pflog.txt 258 atf_check -o match:".*: block out on lo0: ::1 > ::: ICMP6, echo request,.*" \ 259 cat pflog.txt 260} 261 262unspecified_v6_cleanup() 263{ 264 pft_cleanup 265} 266 267atf_init_test_cases() 268{ 269 atf_add_test_case "malformed" 270 atf_add_test_case "matches" 271 atf_add_test_case "state_max" 272 atf_add_test_case "unspecified_v4" 273 atf_add_test_case "unspecified_v6" 274} 275