1#!/usr/bin/env atf-sh 2#- 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright (c) 2021 Alexander V. Chernikov 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 29. $(atf_get_srcdir)/../common/vnet.subr 30 31atf_test_case "ndp_add_gu_success" "cleanup" 32ndp_add_gu_success_head() { 33 atf_set descr 'Test static ndp record addition' 34 atf_set require.user root 35} 36 37ndp_add_gu_success_body() { 38 local epair0 jname 39 40 vnet_init 41 42 jname="v6t-ndp_add_success" 43 44 epair0=$(vnet_mkepair) 45 46 vnet_mkjail ${jname} ${epair0}a 47 jexec ${jname} ndp -i ${epair0}a -- -disabled 48 jexec ${jname} ifconfig ${epair0}a up 49 50 jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64 51 52 # wait for DAD to complete 53 while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do 54 sleep 0.1 55 done 56 57 atf_check jexec ${jname} ndp -s 2001:db8::2 90:10:00:01:02:03 58 59 t=`jexec ${jname} ndp -an | grep 2001:db8::2 | awk '{print $1, $2, $3, $4}'` 60 if [ "${t}" != "2001:db8::2 90:10:00:01:02:03 ${epair0}a permanent" ]; then 61 atf_fail "Wrong output: ${t}" 62 fi 63 echo "T='${t}'" 64} 65 66ndp_add_gu_success_cleanup() { 67 vnet_cleanup 68} 69 70atf_test_case "ndp_del_gu_success" "cleanup" 71ndp_del_gu_success_head() { 72 atf_set descr 'Test ndp record deletion' 73 atf_set require.user root 74} 75 76ndp_del_gu_success_body() { 77 local epair0 jname 78 79 vnet_init 80 81 jname="v6t-ndp_del_gu_success" 82 83 epair0=$(vnet_mkepair) 84 85 vnet_mkjail ${jname} ${epair0}a 86 87 jexec ${jname} ndp -i ${epair0}a -- -disabled 88 jexec ${jname} ifconfig ${epair0}a up 89 90 jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64 91 92 # wait for DAD to complete 93 while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do 94 sleep 0.1 95 done 96 97 jexec ${jname} ping -c1 -t1 2001:db8::2 98 99 atf_check -o match:"2001:db8::2 \(2001:db8::2\) deleted" jexec ${jname} ndp -nd 2001:db8::2 100} 101 102ndp_del_gu_success_cleanup() { 103 vnet_cleanup 104} 105 106ndp_if_up() 107{ 108 local ifname=$1 109 local jname=$2 110 111 if [ -n "$jname" ]; then 112 jname="jexec ${jname}" 113 fi 114 atf_check ${jname} ifconfig ${ifname} up 115 atf_check ${jname} ifconfig ${ifname} inet6 -ifdisabled 116 while ${jname} ifconfig ${ifname} inet6 | grep tentative; do 117 sleep 0.1 118 done 119} 120 121ndp_if_lladdr() 122{ 123 local ifname=$1 124 local jname=$2 125 126 if [ -n "$jname" ]; then 127 jname="jexec ${jname}" 128 fi 129 ${jname} ifconfig ${ifname} inet6 | \ 130 awk '/inet6 fe80:/{split($2, addr, "%"); print addr[1]}' 131} 132 133atf_test_case "ndp_slaac_default_route" "cleanup" 134ndp_slaac_default_route_head() { 135 atf_set descr 'Test default route installation via SLAAC' 136 atf_set require.user root 137 atf_set require.progs python3 scapy 138} 139 140ndp_slaac_default_route_body() { 141 local epair0 jname lladdr 142 143 vnet_init 144 145 jname="v6t-ndp_slaac_default_route" 146 147 epair0=$(vnet_mkepair) 148 149 vnet_mkjail ${jname} ${epair0}a 150 151 ndp_if_up ${epair0}a ${jname} 152 ndp_if_up ${epair0}b 153 atf_check jexec ${jname} ifconfig ${epair0}a inet6 accept_rtadv 154 155 # Make sure that NAs from us are flagged as coming from a router. 156 atf_check -o ignore sysctl net.inet6.ip6.forwarding=1 157 158 # Send an RA advertising a prefix. 159 atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \ 160 --sendif ${epair0}b \ 161 --dst $(ndp_if_lladdr ${epair0}a ${jname}) \ 162 --src $(ndp_if_lladdr ${epair0}b) \ 163 --prefix "2001:db8:ffff:1000::" --prefixlen 64 164 165 # Wait for a default router to appear. 166 while [ -z "$(jexec ${jname} ndp -r)" ]; do 167 sleep 0.1 168 done 169 atf_check -o match:"^default[[:space:]]+fe80:" \ 170 jexec ${jname} netstat -rn -6 171 172 # Get rid of the default route. 173 atf_check -o ignore jexec ${jname} route -6 flush 174 atf_check -o not-match:"^default[[:space:]]+fe80:" \ 175 jexec ${jname} netstat -rn -6 176 177 # Send another RA, make sure that the default route is installed again. 178 atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \ 179 --sendif ${epair0}b \ 180 --dst $(ndp_if_lladdr ${epair0}a ${jname}) \ 181 --src $(ndp_if_lladdr ${epair0}b) \ 182 --prefix "2001:db8:ffff:1000::" --prefixlen 64 183 while [ -z "$(jexec ${jname} ndp -r)" ]; do 184 sleep 0.1 185 done 186 atf_check -o match:"^default[[:space:]]+fe80:" \ 187 jexec ${jname} netstat -rn -6 188} 189 190ndp_slaac_default_route_cleanup() { 191 vnet_cleanup 192} 193 194atf_test_case "ndp_prefix_len_mismatch" "cleanup" 195ndp_prefix_len_mismatch_head() { 196 atf_set descr 'Test RAs on an interface without a /64 lladdr' 197 atf_set require.user root 198 atf_set require.progs python3 scapy 199} 200 201ndp_prefix_len_mismatch_body() { 202 vnet_init 203 204 epair=$(vnet_mkepair) 205 206 vnet_mkjail alcatraz ${epair}a 207 208 jexec alcatraz ifconfig ${epair}a inet6 -auto_linklocal 209 jexec alcatraz ifconfig ${epair}a inet6 -ifdisabled 210 jexec alcatraz ifconfig ${epair}a inet6 accept_rtadv 211 jexec alcatraz ifconfig ${epair}a inet6 fe80::5a9c:fcff:fe10:5d07/127 212 jexec alcatraz ifconfig ${epair}a up 213 214 ifconfig ${epair}b inet6 -ifdisabled 215 ifconfig ${epair}b up 216 217 atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \ 218 --sendif ${epair}b \ 219 --dst $(ndp_if_lladdr ${epair}a alcatraz) \ 220 --src $(ndp_if_lladdr ${epair}b) \ 221 --prefix "2001:db8:ffff:1000::" --prefixlen 64 222 223 atf_check \ 224 -o match:"inet6 2001:db8:ffff:1000:.* prefixlen 64.*autoconf.*" \ 225 jexec alcatraz ifconfig ${epair}a 226} 227 228ndp_prefix_len_mismatch_cleanup() { 229 vnet_cleanup 230} 231 232atf_test_case "ndp_prefix_lifetime" "cleanup" 233ndp_prefix_lifetime_head() { 234 atf_set descr 'Test ndp slaac address lifetime handling' 235 atf_set require.user root 236 atf_set require.progs python3 scapy 237} 238 239ndp_prefix_lifetime_body() { 240 local epair0 jname prefix 241 242 vnet_init 243 244 jname="v6t-ndp_prefix_lifetime" 245 246 epair0=$(vnet_mkepair) 247 248 vnet_mkjail ${jname} ${epair0}a 249 250 ndp_if_up ${epair0}a ${jname} 251 ndp_if_up ${epair0}b 252 atf_check jexec ${jname} ifconfig ${epair0}a inet6 accept_rtadv no_dad 253 254 prefix="2001:db8:ffff:1000:" 255 256 # Send an RA advertising a prefix. 257 atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \ 258 --sendif ${epair0}b \ 259 --dst $(ndp_if_lladdr ${epair0}a ${jname}) \ 260 --src $(ndp_if_lladdr ${epair0}b) \ 261 --prefix "2001:db8:ffff:1000::" --prefixlen 64 \ 262 --validlifetime 10 --preferredlifetime 5 263 264 # Wait for a default router to appear. 265 while [ -z "$(jexec ${jname} ndp -r)" ]; do 266 sleep 0.1 267 done 268 atf_check \ 269 -o match:"^default[[:space:]]+fe80:" \ 270 jexec ${jname} netstat -rn -6 271 272 atf_check \ 273 -o match:"inet6 ${prefix}.* prefixlen 64 autoconf pltime 5 vltime 10" \ 274 jexec ${jname} ifconfig ${epair0}a 275 276 # Wait for the address to become deprecated. 277 sleep 6 278 atf_check \ 279 -o match:"inet6 ${prefix}.* prefixlen 64 deprecated autoconf pltime 0 vltime [1-9]+" \ 280 jexec ${jname} ifconfig -L ${epair0}a 281 282 # Wait for the address to expire. 283 sleep 6 284 atf_check \ 285 -o not-match:"inet6 ${prefix}.*" \ 286 jexec ${jname} ifconfig ${epair0}a 287} 288 289ndp_prefix_lifetime_cleanup() { 290 vnet_cleanup 291} 292 293atf_test_case "ndp_prefix_lifetime_extend" 294ndp_prefix_lifetime_extend_head() { 295 atf_set descr 'Test prefix lifetime updates via ifconfig' 296 atf_set require.user root 297 atf_set require.progs jq 298} 299 300get_prefix_attr() { 301 local prefix=$1 302 local attr=$2 303 304 ndp -p --libxo json | \ 305 jq -r '.ndp.["prefix-list"][] | 306 select(.prefix == "'${prefix}'") | .["'${attr}'"]' 307} 308 309# Given a prefix, return its expiry time in seconds. 310prefix_expiry() { 311 get_prefix_attr $1 "expires_sec" 312} 313 314# Given a prefix, return its valid and preferred lifetimes. 315prefix_lifetimes() { 316 local p v 317 318 v=$(get_prefix_attr $1 "valid-lifetime") 319 p=$(get_prefix_attr $1 "preferred-lifetime") 320 echo $v $p 321} 322 323ndp_prefix_lifetime_extend_body() { 324 local epair ex1 ex2 ex3 prefix pltime vltime 325 326 atf_check -o save:epair ifconfig epair create 327 epair=$(cat epair) 328 atf_check ifconfig ${epair} up 329 330 prefix="2001:db8:ffff:1000::" 331 332 atf_check ifconfig ${epair} inet6 ${prefix}1/64 pltime 5 vltime 10 333 t=$(prefix_lifetimes ${prefix}/64) 334 if [ "${t}" != "10 5" ]; then 335 atf_fail "Unexpected lifetimes: ${t}" 336 fi 337 ex1=$(prefix_expiry ${prefix}/64) 338 if [ "${ex1}" -gt 10 ]; then 339 atf_fail "Unexpected expiry time: ${ex1}" 340 fi 341 342 # Double the address lifetime and verify that the prefix is 343 # updated. 344 atf_check ifconfig ${epair} inet6 ${prefix}1/64 pltime 10 vltime 20 345 t=$(prefix_lifetimes ${prefix}/64) 346 if [ "${t}" != "20 10" ]; then 347 atf_fail "Unexpected lifetimes: ${t}" 348 fi 349 ex2=$(prefix_expiry ${prefix}/64) 350 if [ "${ex2}" -le "${ex1}" ]; then 351 atf_fail "Expiry time was not extended: ${ex1} <= ${ex2}" 352 fi 353 354 # Add a second address from the same prefix with a shorter 355 # lifetime, and make sure that the prefix lifetime is not 356 # shortened. 357 atf_check ifconfig ${epair} inet6 ${prefix}2/64 pltime 5 vltime 10 358 t=$(prefix_lifetimes ${prefix}/64) 359 if [ "${t}" != "20 10" ]; then 360 atf_fail "Unexpected lifetimes: ${t}" 361 fi 362 ex3=$(prefix_expiry ${prefix}/64) 363 if [ "${ex3}" -le 10 -o "${ex3}" -gt 20 ]; then 364 atf_fail "Unexpected expiry time: ${ex3}" 365 fi 366} 367 368atf_init_test_cases() 369{ 370 atf_add_test_case "ndp_add_gu_success" 371 atf_add_test_case "ndp_del_gu_success" 372 atf_add_test_case "ndp_slaac_default_route" 373 atf_add_test_case "ndp_prefix_len_mismatch" 374 atf_add_test_case "ndp_prefix_lifetime" 375 atf_add_test_case "ndp_prefix_lifetime_extend" 376} 377