1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro> 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# 28# pf divert-to action test cases 29# 30# -----------| |-- |----| ----| |----------- 31# ( ) inbound |pf_check_in| ) -> |host| -> ( ) |pf_check_out| outbound ) 32# -----------| | |-- |----| ----| | |----------- 33# | | 34# \|/ \|/ 35# |------| |------| 36# |divapp| |divapp| 37# |------| |------| 38# 39# The basic cases: 40# - inbound > diverted | divapp terminated 41# - inbound > diverted > inbound | host terminated 42# - inbound > diverted > outbound | network terminated 43# - outbound > diverted | divapp terminated 44# - outbound > diverted > outbound | network terminated 45# - outbound > diverted > inbound | e.g. host terminated 46# 47# When a packet is diverted, forwarded, and possibly diverted again: 48# - inbound > diverted > inbound > forwarded 49# > outbound | network terminated 50# - inbound > diverted > inbound > forwarded 51# > outbound > diverted > outbound | network terminated 52# 53# Test case naming legend: 54# in - inbound 55# div - diverted 56# out - outbound 57# fwd - forwarded 58# dn - delayed by dummynet 59# 60 61. $(atf_get_srcdir)/utils.subr 62 63divert_init() 64{ 65 if ! kldstat -q -m ipdivert; then 66 atf_skip "This test requires ipdivert" 67 fi 68} 69 70dummynet_init() 71{ 72 if ! kldstat -q -m dummynet; then 73 atf_skip "This test requires dummynet" 74 fi 75} 76 77atf_test_case "in_div" "cleanup" 78in_div_head() 79{ 80 atf_set descr 'Test inbound > diverted | divapp terminated' 81 atf_set require.user root 82} 83in_div_body() 84{ 85 pft_init 86 divert_init 87 88 epair=$(vnet_mkepair) 89 vnet_mkjail div ${epair}b 90 ifconfig ${epair}a 192.0.2.1/24 up 91 jexec div ifconfig ${epair}b 192.0.2.2/24 up 92 93 # Sanity check 94 atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 95 96 jexec div pfctl -e 97 pft_set_rules div \ 98 "pass all" \ 99 "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2000" 100 101 jexec div $(atf_get_srcdir)/../common/divapp 2000 & 102 divapp_pid=$! 103 # Wait for the divapp to be ready 104 sleep 1 105 106 # divapp is expected to "eat" the packet 107 atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2 108 109 wait $divapp_pid 110} 111in_div_cleanup() 112{ 113 pft_cleanup 114} 115 116atf_test_case "in_div_in" "cleanup" 117in_div_in_head() 118{ 119 atf_set descr 'Test inbound > diverted > inbound | host terminated' 120 atf_set require.user root 121} 122in_div_in_body() 123{ 124 pft_init 125 divert_init 126 127 epair=$(vnet_mkepair) 128 vnet_mkjail div ${epair}b 129 ifconfig ${epair}a 192.0.2.1/24 up 130 jexec div ifconfig ${epair}b 192.0.2.2/24 up 131 132 # Sanity check 133 atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 134 135 jexec div pfctl -e 136 pft_set_rules div \ 137 "pass all" \ 138 "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2000 no state" 139 140 jexec div $(atf_get_srcdir)/../common/divapp 2000 divert-back & 141 divapp_pid=$! 142 # Wait for the divapp to be ready 143 sleep 1 144 145 # divapp is NOT expected to "eat" the packet 146 atf_check -s exit:0 -o ignore ping -c1 192.0.2.2 147 148 wait $divapp_pid 149} 150in_div_in_cleanup() 151{ 152 pft_cleanup 153} 154 155atf_test_case "out_div" "cleanup" 156out_div_head() 157{ 158 atf_set descr 'Test outbound > diverted | divapp terminated' 159 atf_set require.user root 160} 161out_div_body() 162{ 163 pft_init 164 divert_init 165 166 epair=$(vnet_mkepair) 167 vnet_mkjail div ${epair}b 168 ifconfig ${epair}a 192.0.2.1/24 up 169 jexec div ifconfig ${epair}b 192.0.2.2/24 up 170 171 # Sanity check 172 atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 173 174 jexec div pfctl -e 175 pft_set_rules div \ 176 "pass all" \ 177 "pass in inet proto icmp icmp-type echoreq no state" \ 178 "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2000 no state" 179 180 jexec div $(atf_get_srcdir)/../common/divapp 2000 & 181 divapp_pid=$! 182 # Wait for the divapp to be ready 183 sleep 1 184 185 # divapp is expected to "eat" the packet 186 atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2 187 188 wait $divapp_pid 189} 190out_div_cleanup() 191{ 192 pft_cleanup 193} 194 195atf_test_case "out_div_out" "cleanup" 196out_div_out_head() 197{ 198 atf_set descr 'Test outbound > diverted > outbound | network terminated' 199 atf_set require.user root 200} 201out_div_out_body() 202{ 203 pft_init 204 divert_init 205 206 epair=$(vnet_mkepair) 207 vnet_mkjail div ${epair}b 208 ifconfig ${epair}a 192.0.2.1/24 up 209 jexec div ifconfig ${epair}b 192.0.2.2/24 up 210 211 # Sanity check 212 atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 213 214 jexec div pfctl -e 215 pft_set_rules div \ 216 "pass all" \ 217 "pass in inet proto icmp icmp-type echoreq no state" \ 218 "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2000 no state" 219 220 jexec div $(atf_get_srcdir)/../common/divapp 2000 divert-back & 221 divapp_pid=$! 222 # Wait for the divapp to be ready 223 sleep 1 224 225 # divapp is NOT expected to "eat" the packet 226 atf_check -s exit:0 -o ignore ping -c1 192.0.2.2 227 228 wait $divapp_pid 229} 230out_div_out_cleanup() 231{ 232 pft_cleanup 233} 234 235atf_test_case "in_div_in_fwd_out_div_out" "cleanup" 236in_div_in_fwd_out_div_out_head() 237{ 238 atf_set descr 'Test inbound > diverted > inbound > forwarded > outbound > diverted > outbound | network terminated' 239 atf_set require.user root 240} 241in_div_in_fwd_out_div_out_body() 242{ 243 pft_init 244 divert_init 245 246 # host <a--epair0--b> router <a--epair1--b> site 247 epair0=$(vnet_mkepair) 248 epair1=$(vnet_mkepair) 249 250 vnet_mkjail router ${epair0}b ${epair1}a 251 ifconfig ${epair0}a 192.0.2.1/24 up 252 jexec router sysctl net.inet.ip.forwarding=1 253 jexec router ifconfig ${epair0}b 192.0.2.2/24 up 254 jexec router ifconfig ${epair1}a 198.51.100.1/24 up 255 256 vnet_mkjail site ${epair1}b 257 jexec site ifconfig ${epair1}b 198.51.100.2/24 up 258 jexec site route add default 198.51.100.1 259 260 route add -net 198.51.100.0/24 192.0.2.2 261 262 # Sanity check 263 atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 264 265 # Should be routed without pf 266 atf_check -s exit:0 -o ignore ping -c3 198.51.100.2 267 268 jexec router pfctl -e 269 pft_set_rules router \ 270 "pass all" \ 271 "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2001 no state" \ 272 "pass out inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2002 no state" 273 274 jexec router $(atf_get_srcdir)/../common/divapp 2001 divert-back & 275 indivapp_pid=$! 276 jexec router $(atf_get_srcdir)/../common/divapp 2002 divert-back & 277 outdivapp_pid=$! 278 # Wait for the divappS to be ready 279 sleep 1 280 281 # Both divappS are NOT expected to "eat" the packet 282 atf_check -s exit:0 -o ignore ping -c1 198.51.100.2 283 284 wait $indivapp_pid && wait $outdivapp_pid 285} 286in_div_in_fwd_out_div_out_cleanup() 287{ 288 pft_cleanup 289} 290 291atf_test_case "in_dn_in_div_in_out_div_out_dn_out" "cleanup" 292in_dn_in_div_in_out_div_out_dn_out_head() 293{ 294 atf_set descr 'Test inbound > delayed+diverted > outbound > diverted+delayed > outbound | network terminated' 295 atf_set require.user root 296} 297in_dn_in_div_in_out_div_out_dn_out_body() 298{ 299 pft_init 300 divert_init 301 dummynet_init 302 303 epair=$(vnet_mkepair) 304 vnet_mkjail alcatraz ${epair}b 305 ifconfig ${epair}a 192.0.2.1/24 up 306 ifconfig ${epair}a ether 02:00:00:00:00:01 307 jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up 308 309 # Sanity check 310 atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 311 312 # a) ping should time out due to very narrow dummynet pipes { 313 314 jexec alcatraz dnctl pipe 1001 config bw 1Byte/s 315 jexec alcatraz dnctl pipe 1002 config bw 1Byte/s 316 317 jexec alcatraz pfctl -e 318 pft_set_rules alcatraz \ 319 "ether pass in from 02:00:00:00:00:01 l3 all dnpipe 1001" \ 320 "ether pass out to 02:00:00:00:00:01 l3 all dnpipe 1002 " \ 321 "pass all" \ 322 "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 1001 no state" \ 323 "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 1002 no state" 324 325 jexec alcatraz $(atf_get_srcdir)/../common/divapp 1001 divert-back & 326 indivapp_pid=$! 327 jexec alcatraz $(atf_get_srcdir)/../common/divapp 1002 divert-back & 328 outdivapp_pid=$! 329 # Wait for the divappS to be ready 330 sleep 1 331 332 atf_check -s not-exit:0 -o ignore ping -c1 -s56 -t1 192.0.2.2 333 334 wait $indivapp_pid 335 atf_check_not_equal 0 $? 336 wait $outdivapp_pid 337 atf_check_not_equal 0 $? 338 339 # } 340 341 # b) ping should NOT time out due to wide enough dummynet pipes { 342 343 jexec alcatraz dnctl pipe 2001 config bw 100KByte/s 344 jexec alcatraz dnctl pipe 2002 config bw 100KByte/s 345 346 jexec alcatraz pfctl -e 347 pft_set_rules alcatraz \ 348 "ether pass in from 02:00:00:00:00:01 l3 all dnpipe 2001" \ 349 "ether pass out to 02:00:00:00:00:01 l3 all dnpipe 2002 " \ 350 "pass all" \ 351 "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2001 no state" \ 352 "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2002 no state" 353 354 jexec alcatraz $(atf_get_srcdir)/../common/divapp 2001 divert-back & 355 indivapp_pid=$! 356 jexec alcatraz $(atf_get_srcdir)/../common/divapp 2002 divert-back & 357 outdivapp_pid=$! 358 # Wait for the divappS to be ready 359 sleep 1 360 361 atf_check -s exit:0 -o ignore ping -c1 -s56 -t1 192.0.2.2 362 363 wait $indivapp_pid 364 atf_check_equal 0 $? 365 wait $outdivapp_pid 366 atf_check_equal 0 $? 367 368 # } 369} 370in_dn_in_div_in_out_div_out_dn_out_cleanup() 371{ 372 pft_cleanup 373} 374 375atf_init_test_cases() 376{ 377 atf_add_test_case "in_div" 378 atf_add_test_case "in_div_in" 379 380 atf_add_test_case "out_div" 381 atf_add_test_case "out_div_out" 382 383 atf_add_test_case "in_div_in_fwd_out_div_out" 384 385 atf_add_test_case "in_dn_in_div_in_out_div_out_dn_out" 386} 387