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 304atf_init_test_cases() 305{ 306 atf_add_test_case "exhaust" 307 atf_add_test_case "nested_anchor" 308 atf_add_test_case "endpoint_independent" 309 atf_add_test_case "nat6_nolinklocal" 310} 311