1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2018 Kristof Provost <kp@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 29atf_test_case "exhaust" "cleanup" 30exhaust_head() 31{ 32 atf_set descr 'Test exhausting the NAT pool' 33 atf_set require.user root 34} 35 36exhaust_body() 37{ 38 pft_init 39 40 epair_nat=$(vnet_mkepair) 41 epair_echo=$(vnet_mkepair) 42 43 vnet_mkjail nat ${epair_nat}b ${epair_echo}a 44 vnet_mkjail echo ${epair_echo}b 45 46 ifconfig ${epair_nat}a 192.0.2.2/24 up 47 route add -net 198.51.100.0/24 192.0.2.1 48 49 jexec nat ifconfig ${epair_nat}b 192.0.2.1/24 up 50 jexec nat ifconfig ${epair_echo}a 198.51.100.1/24 up 51 jexec nat sysctl net.inet.ip.forwarding=1 52 53 jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up 54 jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf 55 56 # Enable pf! 57 jexec nat pfctl -e 58 pft_set_rules nat \ 59 "nat pass on ${epair_echo}a inet from 192.0.2.0/24 to any -> (${epair_echo}a) port 30000:30001 sticky-address" 60 61 # Sanity check 62 atf_check -s exit:0 -o ignore ping -c 3 198.51.100.2 63 64 atf_check -s exit:0 -o match:foo* echo "foo" | nc -N 198.51.100.2 7 65 atf_check -s exit:0 -o match:foo* echo "foo" | nc -N 198.51.100.2 7 66 67 # This one will fail, but that's expected 68 echo "foo" | nc -N 198.51.100.2 7 & 69 70 sleep 1 71 72 # If the kernel is stuck in pf_get_sport() this will not succeed either. 73 timeout 2 jexec nat pfctl -sa 74 if [ $? -eq 124 ]; then 75 # Timed out 76 atf_fail "pfctl timeout" 77 fi 78} 79 80exhaust_cleanup() 81{ 82 pft_cleanup 83} 84 85atf_test_case "nested_anchor" "cleanup" 86nested_anchor_head() 87{ 88 atf_set descr 'Test setting and retrieving nested nat anchors' 89 atf_set require.user root 90} 91 92nested_anchor_body() 93{ 94 pft_init 95 96 epair=$(vnet_mkepair) 97 98 vnet_mkjail nat ${epair}a 99 100 pft_set_rules nat \ 101 "nat-anchor \"foo\"" 102 103 echo "nat-anchor \"bar\"" | jexec nat pfctl -g -a foo -f - 104 echo "nat on ${epair}a from any to any -> (${epair}a)" | jexec nat pfctl -g -a "foo/bar" -f - 105 106 atf_check -s exit:0 -o inline:"nat-anchor \"foo\" all { 107 nat-anchor \"bar\" all { 108 nat on ${epair}a all -> (${epair}a) round-robin 109 } 110} 111" jexec nat pfctl -sn -a "*" 112 113} 114 115atf_test_case "endpoint_independent" "cleanup" 116endpoint_independent_head() 117{ 118 atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers' 119 atf_set require.user root 120} 121 122endpoint_independent_body() 123{ 124 pft_init 125 filter="udp and dst port 1234" # only capture udp pings 126 127 epair_client=$(vnet_mkepair) 128 epair_nat=$(vnet_mkepair) 129 epair_server1=$(vnet_mkepair) 130 epair_server2=$(vnet_mkepair) 131 bridge=$(vnet_mkbridge) 132 133 vnet_mkjail nat ${epair_client}b ${epair_nat}a 134 vnet_mkjail client ${epair_client}a 135 vnet_mkjail server1 ${epair_server1}a 136 vnet_mkjail server2 ${epair_server2}a 137 138 ifconfig ${epair_server1}b up 139 ifconfig ${epair_server2}b up 140 ifconfig ${epair_nat}b up 141 ifconfig ${bridge} \ 142 addm ${epair_server1}b \ 143 addm ${epair_server2}b \ 144 addm ${epair_nat}b \ 145 up 146 147 jexec nat ifconfig ${epair_client}b 192.0.2.1/24 up 148 jexec nat ifconfig ${epair_nat}a 198.51.100.42/24 up 149 jexec nat sysctl net.inet.ip.forwarding=1 150 151 jexec client ifconfig ${epair_client}a 192.0.2.2/24 up 152 jexec client route add default 192.0.2.1 153 154 jexec server1 ifconfig ${epair_server1}a 198.51.100.32/24 up 155 jexec server2 ifconfig ${epair_server2}a 198.51.100.22/24 up 156 157 # Enable pf! 158 jexec nat pfctl -e 159 160 # validate non-endpoint independent nat rule behaviour 161 pft_set_rules nat \ 162 "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)" 163 164 jexec server1 tcpdump -i ${epair_server1}a -w ${PWD}/server1.pcap \ 165 --immediate-mode $filter & 166 server1tcppid="$!" 167 jexec server2 tcpdump -i ${epair_server2}a -w ${PWD}/server2.pcap \ 168 --immediate-mode $filter & 169 server2tcppid="$!" 170 171 # send out multiple packets 172 for i in $(seq 1 10); do 173 echo "ping" | jexec client nc -u 198.51.100.32 1234 -p 4242 -w 0 174 echo "ping" | jexec client nc -u 198.51.100.22 1234 -p 4242 -w 0 175 done 176 177 kill $server1tcppid 178 kill $server2tcppid 179 180 tuple_server1=$(tcpdump -r ${PWD}/server1.pcap | awk '{addr=$3} END {print addr}') 181 tuple_server2=$(tcpdump -r ${PWD}/server2.pcap | awk '{addr=$3} END {print addr}') 182 183 if [ -z $tuple_server1 ] 184 then 185 atf_fail "server1 did not receive connection from client (default)" 186 fi 187 188 if [ -z $tuple_server2 ] 189 then 190 atf_fail "server2 did not receive connection from client (default)" 191 fi 192 193 if [ "$tuple_server1" = "$tuple_server2" ] 194 then 195 echo "server1 tcpdump: $tuple_server1" 196 echo "server2 tcpdump: $tuple_server2" 197 atf_fail "Received same IP:port on server1 and server2 (default)" 198 fi 199 200 # validate endpoint independent nat rule behaviour 201 pft_set_rules nat \ 202 "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a) endpoint-independent" 203 204 jexec server1 tcpdump -i ${epair_server1}a -w ${PWD}/server1.pcap \ 205 --immediate-mode $filter & 206 server1tcppid="$!" 207 jexec server2 tcpdump -i ${epair_server2}a -w ${PWD}/server2.pcap \ 208 --immediate-mode $filter & 209 server2tcppid="$!" 210 211 # send out multiple packets, sometimes one fails to go through 212 for i in $(seq 1 10); do 213 echo "ping" | jexec client nc -u 198.51.100.32 1234 -p 4242 -w 0 214 echo "ping" | jexec client nc -u 198.51.100.22 1234 -p 4242 -w 0 215 done 216 217 kill $server1tcppid 218 kill $server2tcppid 219 220 tuple_server1=$(tcpdump -r ${PWD}/server1.pcap | awk '{addr=$3} END {print addr}') 221 tuple_server2=$(tcpdump -r ${PWD}/server2.pcap | awk '{addr=$3} END {print addr}') 222 223 if [ -z $tuple_server1 ] 224 then 225 atf_fail "server1 did not receive connection from client (endpoint-independent)" 226 fi 227 228 if [ -z $tuple_server2 ] 229 then 230 atf_fail "server2 did not receive connection from client (endpoint-independent)" 231 fi 232 233 if [ ! "$tuple_server1" = "$tuple_server2" ] 234 then 235 echo "server1 tcpdump: $tuple_server1" 236 echo "server2 tcpdump: $tuple_server2" 237 atf_fail "Received different IP:port on server1 than server2 (endpoint-independent)" 238 fi 239} 240 241endpoint_independent_cleanup() 242{ 243 pft_cleanup 244 rm -f server1.out 245 rm -f server2.out 246} 247 248nested_anchor_cleanup() 249{ 250 pft_cleanup 251} 252 253atf_test_case "nat6_nolinklocal" "cleanup" 254nat6_nolinklocal_head() 255{ 256 atf_set descr 'Ensure we do not use link-local addresses' 257 atf_set require.user root 258} 259 260nat6_nolinklocal_body() 261{ 262 pft_init 263 264 epair_nat=$(vnet_mkepair) 265 epair_echo=$(vnet_mkepair) 266 267 vnet_mkjail nat ${epair_nat}b ${epair_echo}a 268 vnet_mkjail echo ${epair_echo}b 269 270 ifconfig ${epair_nat}a inet6 2001:db8::2/64 no_dad up 271 route add -6 -net 2001:db8:1::/64 2001:db8::1 272 273 jexec nat ifconfig ${epair_nat}b inet6 2001:db8::1/64 no_dad up 274 jexec nat ifconfig ${epair_echo}a inet6 2001:db8:1::1/64 no_dad up 275 jexec nat sysctl net.inet6.ip6.forwarding=1 276 277 jexec echo ifconfig ${epair_echo}b inet6 2001:db8:1::2/64 no_dad up 278 # Ensure we can't reply to link-local pings 279 jexec echo pfctl -e 280 pft_set_rules echo \ 281 "pass" \ 282 "block in inet6 proto icmp6 from fe80::/10 to any icmp6-type echoreq" 283 284 jexec nat pfctl -e 285 pft_set_rules nat \ 286 "nat pass on ${epair_echo}a inet6 from 2001:db8::/64 to any -> (${epair_echo}a)" \ 287 "pass" 288 289 # Sanity check 290 atf_check -s exit:0 -o ignore \ 291 ping -6 -c 1 2001:db8::1 292 for i in `seq 0 10` 293 do 294 atf_check -s exit:0 -o ignore \ 295 ping -6 -c 1 2001:db8:1::2 296 done 297} 298 299nat6_nolinklocal_cleanup() 300{ 301 pft_cleanup 302} 303 304empty_table_common() 305{ 306 option=$1 307 308 pft_init 309 310 epair_wan=$(vnet_mkepair) 311 epair_lan=$(vnet_mkepair) 312 313 vnet_mkjail srv ${epair_wan}a 314 jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up 315 316 vnet_mkjail rtr ${epair_wan}b ${epair_lan}a 317 jexec rtr ifconfig ${epair_wan}b 192.0.2.1/24 up 318 jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up 319 jexec rtr sysctl net.inet.ip.forwarding=1 320 321 ifconfig ${epair_lan}b 198.51.100.2/24 up 322 route add default 198.51.100.1 323 324 jexec rtr pfctl -e 325 pft_set_rules rtr \ 326 "table <empty>" \ 327 "nat on ${epair_wan}b inet from 198.51.100.0/24 -> <empty> ${option}" \ 328 "pass" 329 330 # Sanity checks 331 atf_check -s exit:0 -o ignore \ 332 jexec rtr ping -c 1 192.0.2.2 333 atf_check -s exit:0 -o ignore \ 334 ping -c 1 198.51.100.1 335 atf_check -s exit:0 -o ignore \ 336 ping -c 1 192.0.2.1 337 338 # Provoke divide by zero 339 ping -c 1 192.0.2.2 340 true 341} 342 343atf_test_case "empty_table_source_hash" "cleanup" 344empty_table_source_hash_head() 345{ 346 atf_set descr 'Test source-hash on an emtpy table' 347 atf_set require.user root 348} 349 350empty_table_source_hash_body() 351{ 352 empty_table_common "source-hash" 353} 354 355empty_table_source_hash_cleanup() 356{ 357 pft_cleanup 358} 359 360atf_test_case "empty_table_random" "cleanup" 361empty_table_random_head() 362{ 363 atf_set descr 'Test random on an emtpy table' 364 atf_set require.user root 365} 366 367empty_table_random_body() 368{ 369 empty_table_common "random" 370} 371 372empty_table_random_cleanup() 373{ 374 pft_cleanup 375} 376 377no_addrs_common() 378{ 379 option=$1 380 381 pft_init 382 383 epair_wan=$(vnet_mkepair) 384 epair_lan=$(vnet_mkepair) 385 386 vnet_mkjail srv ${epair_wan}a 387 jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up 388 389 vnet_mkjail rtr ${epair_wan}b ${epair_lan}a 390 jexec rtr route add -net 192.0.2.0/24 -iface ${epair_wan}b 391 jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up 392 jexec rtr sysctl net.inet.ip.forwarding=1 393 394 ifconfig ${epair_lan}b 198.51.100.2/24 up 395 route add default 198.51.100.1 396 397 jexec rtr pfctl -e 398 pft_set_rules rtr \ 399 "nat on ${epair_wan}b inet from 198.51.100.0/24 -> (${epair_wan}b) ${option}" \ 400 "pass" 401 402 # Provoke divide by zero 403 ping -c 1 192.0.2.2 404 true 405} 406 407atf_test_case "no_addrs_source_hash" "cleanup" 408no_addrs_source_hash_head() 409{ 410 atf_set descr 'Test source-hash on an interface with no addresses' 411 atf_set require.user root 412} 413 414no_addrs_source_hash_body() 415{ 416 no_addrs_common "source-hash" 417} 418 419no_addrs_source_hash_cleanup() 420{ 421 pft_cleanup 422} 423 424atf_test_case "no_addrs_random" "cleanup" 425no_addrs_random_head() 426{ 427 atf_set descr 'Test random on an interface with no addresses' 428 atf_set require.user root 429} 430 431no_addrs_random_body() 432{ 433 no_addrs_common "random" 434} 435 436no_addrs_random_cleanup() 437{ 438 pft_cleanup 439} 440 441atf_init_test_cases() 442{ 443 atf_add_test_case "exhaust" 444 atf_add_test_case "nested_anchor" 445 atf_add_test_case "endpoint_independent" 446 atf_add_test_case "nat6_nolinklocal" 447 atf_add_test_case "empty_table_source_hash" 448 atf_add_test_case "no_addrs_source_hash" 449 atf_add_test_case "empty_table_random" 450 atf_add_test_case "no_addrs_random" 451} 452