bridge: check allow_llz_overlap before member_ifaddrs

When adding a new interface to a bridge and allow_llz_overlap=0, which
is the default value, if_bridge checks if the interface has IPv6 link
local addresses assigned, and if it does, it calls in6_ifdetach() to
remove all IPv6 addresses from the interface.

This means it was possible to do this:

	% ifconfig bridge1 create inet6 -ifdisabled auto_linklocal up
	% ifconfig epair20 create inet6 -ifdisabled auto_linklocal up
	% ifconfig bridge1 addm epair20a

... with the result that the link-local address on epair20a would be
removed, then the interface would be added to the bridge.

If member_ifaddrs=0, which is also the default value, this no longer
works:

	% ifconfig bridge1 addm epair20a
	ifconfig: BRDGADD epair20a: Invalid argument

This is because the member_ifaddrs check runs before allow_llz_overlap
does its thing, and returns EINVAL since the new interface has IP
addresses on it.

To restore the previous behaviour, reverse the order of these two
checks, so the IPv6 addresses are removed before we check whether
the interface has IPv6 addresses.

MFC after:	1 week
Reviewed by:	kevans, kp
Approved by:	kevans (mentor)
Differential Revision:	https://reviews.freebsd.org/D50477

(cherry picked from commit da2dbdc297)
This commit is contained in:
Lexi Winter 2025-05-23 19:08:14 +01:00 committed by Franco Fichtner
parent 264cc9df0b
commit 9d97232f31

View file

@ -1289,25 +1289,6 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
return (EINVAL);
}
/*
* If member_ifaddrs is disabled, do not allow an interface with
* assigned IP addresses to be added to a bridge.
*/
if (!V_member_ifaddrs) {
struct ifaddr *ifa;
CK_STAILQ_FOREACH(ifa, &ifs->if_addrhead, ifa_link) {
#ifdef INET
if (ifa->ifa_addr->sa_family == AF_INET)
return (EINVAL);
#endif
#ifdef INET6
if (ifa->ifa_addr->sa_family == AF_INET6)
return (EINVAL);
#endif
}
}
#ifdef INET6
/*
* Two valid inet6 addresses with link-local scope must not be
@ -1346,6 +1327,26 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
}
}
#endif
/*
* If member_ifaddrs is disabled, do not allow an interface with
* assigned IP addresses to be added to a bridge.
*/
if (!V_member_ifaddrs) {
struct ifaddr *ifa;
CK_STAILQ_FOREACH(ifa, &ifs->if_addrhead, ifa_link) {
#ifdef INET
if (ifa->ifa_addr->sa_family == AF_INET)
return (EINVAL);
#endif
#ifdef INET6
if (ifa->ifa_addr->sa_family == AF_INET6)
return (EINVAL);
#endif
}
}
/* Allow the first Ethernet member to define the MTU */
if (CK_LIST_EMPTY(&sc->sc_iflist))
sc->sc_ifp->if_mtu = ifs->if_mtu;