xref: /src/tests/sys/netinet6/ndp.sh (revision 1eb727727a9acb5f1e66e3f70b0146e7c9c5f710)
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