git: 9fd9039c888d - main - tests: Add SLAAC interface identifier cases to ndp.sh
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 13 Oct 2026 23:34:20 UTC
The branch main has been updated by rcm:
URL: https://cgit.FreeBSD.org/src/commit/?id=9fd9039c888d2d3d6cec47a3383c6825b903fcba
commit 9fd9039c888d2d3d6cec47a3383c6825b903fcba
Author: R. Christian McDonald <rcm@FreeBSD.org>
AuthorDate: 2026-10-13 19:33:37 +0000
Commit: R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2026-10-13 23:31:53 +0000
tests: Add SLAAC interface identifier cases to ndp.sh
Cover the interface identifier selection in in6_ifadd(): a /64
link-local address provides the identifier, while a /128 link-local
address or no link-local address at all yields the EUI-64 identifier
derived from the MAC address. Each case asserts the exact
autoconfigured address. ndp_prefix_len_mismatch reaches the same
fallback with a /127 link-local address, but only checks that some
address was configured.
Stable addresses (RFC 7217) are the default and bypass both paths, so
the host disables them. The advertisement goes to all nodes, and an
interface only joins that group with its first address, so the case
without a link-local address gives the host a static address from
another prefix.
ra.py now takes the Ethernet source address from --sendif rather than
from a route lookup; for ff02::1 that lookup fails in scapy 2.7.0.
Reviewed by: pouria
MFC after: 2 weeks
Sponsored by: Rubicon Communications, LLC ("Netgate")
Differential Revision: https://reviews.freebsd.org/D60520
---
tests/sys/netinet6/ndp.sh | 159 ++++++++++++++++++++++++++++++++++++++++++++++
tests/sys/netinet6/ra.py | 2 +-
2 files changed, 160 insertions(+), 1 deletion(-)
diff --git a/tests/sys/netinet6/ndp.sh b/tests/sys/netinet6/ndp.sh
index d70258bb3925..74c35a0b554e 100755
--- a/tests/sys/netinet6/ndp.sh
+++ b/tests/sys/netinet6/ndp.sh
@@ -862,6 +862,162 @@ ndp_routeinfo_option_cleanup() {
}
+# SLAAC interface identifier selection in in6_ifadd(): a link-local
+# address with the advertised prefix length lends its identifier to the
+# autoconfigured address, otherwise the identifier is derived from the MAC
+# address, which covers PPP links with a /128 link-local address as well as
+# interfaces without a link-local address. The MAC address below gives the
+# EUI-64 based identifier 11:22ff:fe33:4455. Stable addresses (RFC 7217)
+# are switched off on the host, they bypass both paths.
+ndp_slaac_prefix="2001:db8:ffff:2000"
+ndp_slaac_mac="02:11:22:33:44:55"
+ndp_slaac_mac_ifid="11:22ff:fe33:4455"
+ndp_slaac_ll_ifid="dead:beef:1:2"
+
+# Prepare ${ifname} in jail ${jname} as a SLAAC host with the test MAC
+# address and without an automatic link-local address. DAD is off, so
+# that addresses are final once configured. The caller adds the addresses
+# its case needs and brings the interface up.
+ndp_slaac_host()
+{
+ local ifname=$1 jname=$2
+
+ atf_check ifconfig -j ${jname} ${ifname} ether ${ndp_slaac_mac}
+ atf_check ifconfig -j ${jname} ${ifname} inet6 -auto_linklocal \
+ -ifdisabled accept_rtadv -stableaddr no_dad
+}
+
+# Advertise ${ndp_slaac_prefix}::/64 to all nodes from ${sendif} and wait
+# for jail ${jname} to autoconfigure an address from it on ${ifname}. Any
+# further arguments are passed to ra.py.
+ndp_slaac_advertise()
+{
+ local sendif=$1 jname=$2 ifname=$3 i
+
+ shift 3
+ atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \
+ --sendif ${sendif} \
+ --dst ff02::1 \
+ --src $(ndp_if_lladdr ${sendif}) \
+ --prefix "${ndp_slaac_prefix}::" --prefixlen 64 "$@"
+ for i in $(jot 20); do
+ if ifconfig -j ${jname} ${ifname} inet6 | \
+ grep -q "inet6 ${ndp_slaac_prefix}:.*autoconf"; then
+ return
+ fi
+ sleep 0.1
+ done
+ atf_fail "No address from ${ndp_slaac_prefix}::/64 was autoconfigured on ${ifname}"
+}
+
+atf_test_case "ndp_slaac_ifid_lladdr" "cleanup"
+ndp_slaac_ifid_lladdr_head() {
+ atf_set descr 'SLAAC takes the interface ID from a /64 link-local address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ndp_slaac_ifid_lladdr_body() {
+ local epair jname
+
+ vnet_init
+
+ jname="v6t-ndp_slaac_ifid_lladdr"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair}a
+
+ ndp_slaac_host ${epair}a ${jname}
+ atf_check ifconfig -j ${jname} ${epair}a inet6 \
+ fe80::${ndp_slaac_ll_ifid}/64
+ atf_check ifconfig -j ${jname} ${epair}a up
+ ndp_if_up ${epair}b
+ ndp_slaac_advertise ${epair}b ${jname} ${epair}a
+
+ atf_check \
+ -o match:"inet6 ${ndp_slaac_prefix}:${ndp_slaac_ll_ifid} prefixlen 64.*autoconf" \
+ -o not-match:"inet6 ${ndp_slaac_prefix}:${ndp_slaac_mac_ifid} " \
+ ifconfig -j ${jname} ${epair}a inet6
+}
+
+ndp_slaac_ifid_lladdr_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "ndp_slaac_ifid_no_lladdr" "cleanup"
+ndp_slaac_ifid_no_lladdr_head() {
+ atf_set descr 'SLAAC derives the interface ID from the MAC address without a link-local address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ndp_slaac_ifid_no_lladdr_body() {
+ local epair jname
+
+ vnet_init
+
+ jname="v6t-ndp_slaac_ifid_no_lladdr"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair}a
+
+ # An interface joins the all-nodes group, and so receives
+ # advertisements, only once it has an IPv6 address: give it a
+ # static one from another prefix in place of the link-local one.
+ ndp_slaac_host ${epair}a ${jname}
+ atf_check ifconfig -j ${jname} ${epair}a inet6 \
+ 2001:db8:ffff:1::2/64
+ atf_check ifconfig -j ${jname} ${epair}a up
+ ndp_if_up ${epair}b
+ ndp_slaac_advertise ${epair}b ${jname} ${epair}a
+
+ atf_check \
+ -o match:"inet6 ${ndp_slaac_prefix}:${ndp_slaac_mac_ifid} prefixlen 64.*autoconf" \
+ -o not-match:"inet6 fe80:" \
+ ifconfig -j ${jname} ${epair}a inet6
+}
+
+ndp_slaac_ifid_no_lladdr_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "ndp_slaac_ifid_lladdr_plen128" "cleanup"
+ndp_slaac_ifid_lladdr_plen128_head() {
+ atf_set descr 'SLAAC derives the interface ID from the MAC address when the link-local address is a /128'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ndp_slaac_ifid_lladdr_plen128_body() {
+ local epair jname
+
+ vnet_init
+
+ jname="v6t-ndp_slaac_ifid_lladdr_plen128"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair}a
+
+ ndp_slaac_host ${epair}a ${jname}
+ atf_check ifconfig -j ${jname} ${epair}a inet6 \
+ fe80::${ndp_slaac_ll_ifid}/128
+ atf_check ifconfig -j ${jname} ${epair}a up
+ ndp_if_up ${epair}b
+ ndp_slaac_advertise ${epair}b ${jname} ${epair}a
+
+ atf_check \
+ -o match:"inet6 ${ndp_slaac_prefix}:${ndp_slaac_mac_ifid} prefixlen 64.*autoconf" \
+ -o not-match:"inet6 ${ndp_slaac_prefix}:${ndp_slaac_ll_ifid} " \
+ ifconfig -j ${jname} ${epair}a inet6
+}
+
+ndp_slaac_ifid_lladdr_plen128_cleanup() {
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "ndp_add_gu_success"
@@ -877,4 +1033,7 @@ atf_init_test_cases()
atf_add_test_case "ndp_input_validation_src_linklocal"
atf_add_test_case "ndp_multirouter_pref"
atf_add_test_case "ndp_routeinfo_option"
+ atf_add_test_case "ndp_slaac_ifid_lladdr"
+ atf_add_test_case "ndp_slaac_ifid_no_lladdr"
+ atf_add_test_case "ndp_slaac_ifid_lladdr_plen128"
}
diff --git a/tests/sys/netinet6/ra.py b/tests/sys/netinet6/ra.py
index e732e952be71..72680c1db4d5 100644
--- a/tests/sys/netinet6/ra.py
+++ b/tests/sys/netinet6/ra.py
@@ -56,7 +56,7 @@ def main():
help='The route info preference advertised')
args = parser.parse_args()
- pkt = sp.Ether() / \
+ pkt = sp.Ether(src=sp.get_if_hwaddr(args.sendif[0])) / \
sp.IPv6(src=args.src, dst=args.dst, hlim=args.hoplimit) / \
sp.ICMPv6ND_RA(chlim=64,
prf=args.rtrpref,