ifnet: consistently call hooks when the interface gets up.

Some context on the current IPv6 interface setup & address management:

There are two data path for IPv6 initialisation in context of assigning
 LL addresses:
1) Userland explicitly requests IFF_UP for the interface w/o any addresses.
if_up() then calls in6_if_up(), which calls in6_ifattach().
The latter sets up some initial ND/IN6 state and disables IPv6 for the
interface if it’s not loopback. If the interface is loopback, then it
adds ::1/128 and LL addresses via in6_ifattach_loopback().
Then, devd notification is generated (if the VNET is the default one),
which triggers rc.network ifconfig_up(), causing ifdisabled to be removed
via SIOCSIFINFO_IN6 from ifconfig. The kernel SIOCSIFINFO_IN6 handler
calls in6_if_up() once again and it assigns the interface link-local address.

2) Userland adds IPv4 or IPv6 address to the interface. SIOCAIFADDR[_IN6]
kernel handler calls IPv4/IPv6 protocol handler to add the address.
Both then call if_ioctl() with SIOCSIFADDR. Ethernet/loopback ioctl handlers
silently sets IFF_UP for the interface. Finally, if.c:ifioctl() wrapper code
compares old and new interface flags and, if IFF_UP is added, it explicitly
calls in6_if_up(), which adds link-local address if either the original
address is IPv6 or the interface is loopback.

In the latter case, “formal” interface-up notifications are missing.
The kernel does not trigger event handler event, does not call carp hook
and does not provide any userland notification.

This diff unifies the event handling in both scenarios, providing the
necessary notifications to the kernel and userland.

Reviewed By: kp
Differential Revision: https://reviews.freebsd.org/D40332
MFC after:	2 weeks
This commit is contained in:
Alexander V. Chernikov 2023-06-01 11:44:15 +00:00
parent 54b955f4df
commit a77facd273
3 changed files with 22 additions and 32 deletions

View File

@ -278,7 +278,6 @@ static void if_attachdomain1(struct ifnet *);
static int ifconf(u_long, caddr_t);
static void if_input_default(struct ifnet *, struct mbuf *);
static int if_requestencap_default(struct ifnet *, struct if_encap_req *);
static void if_route(struct ifnet *, int flag, int fam);
static int if_setflag(struct ifnet *, int, int, int *, int);
static int if_transmit_default(struct ifnet *ifp, struct mbuf *m);
static void if_unroute(struct ifnet *, int flag, int fam);
@ -2141,26 +2140,6 @@ if_unroute(struct ifnet *ifp, int flag, int fam)
rt_ifmsg(ifp, IFF_UP);
}
/*
* Mark an interface up and notify protocols of
* the transition.
*/
static void
if_route(struct ifnet *ifp, int flag, int fam)
{
KASSERT(flag == IFF_UP, ("if_route: flag != IFF_UP"));
ifp->if_flags |= flag;
getmicrotime(&ifp->if_lastchange);
if (ifp->if_carp)
(*carp_linkstate_p)(ifp);
rt_ifmsg(ifp, IFF_UP);
#ifdef INET6
in6_if_up(ifp);
#endif
}
void (*vlan_link_state_p)(struct ifnet *); /* XXX: private from if_vlan */
void (*vlan_trunk_cap_p)(struct ifnet *); /* XXX: private from if_vlan */
struct ifnet *(*vlan_trunkdev_p)(struct ifnet *);
@ -2246,7 +2225,11 @@ void
if_up(struct ifnet *ifp)
{
if_route(ifp, IFF_UP, AF_UNSPEC);
ifp->if_flags |= IFF_UP;
getmicrotime(&ifp->if_lastchange);
if (ifp->if_carp)
(*carp_linkstate_p)(ifp);
rt_ifmsg(ifp, IFF_UP);
EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_UP);
}
@ -3109,13 +3092,8 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td)
cmd != SIOCSIFDSTADDR && cmd != SIOCSIFNETMASK)
error = (*ifp->if_ioctl)(ifp, cmd, data);
if ((oif_flags ^ ifp->if_flags) & IFF_UP) {
#ifdef INET6
if (ifp->if_flags & IFF_UP)
in6_if_up(ifp);
#endif
}
if (!(oif_flags & IFF_UP) && (ifp->if_flags & IFF_UP))
if_up(ifp);
out_ref:
if_rele(ifp);
out_noref:

View File

@ -2049,6 +2049,20 @@ in6_if_up(struct ifnet *ifp)
in6_ifattach(ifp, NULL);
}
static void
in6_ifevent(void *arg __unused, struct ifnet *ifp, int event)
{
if (event == IFNET_EVENT_UP)
in6_if_up(ifp);
}
static void
in6_init(void *arg __unused)
{
EVENTHANDLER_REGISTER(ifnet_event, in6_ifevent, NULL, EVENTHANDLER_PRI_ANY);
}
SYSINIT(in6_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, in6_init, NULL);
int
in6if_do_dad(struct ifnet *ifp)
{

View File

@ -1312,10 +1312,8 @@ rtnl_handle_addr(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
error = EAFNOSUPPORT;
}
#ifdef INET6
if (error == 0 && !(if_flags & IFF_UP) && (if_getflags(ifp) & IFF_UP))
in6_if_up(ifp);
#endif
if_up(ifp);
if_rele(ifp);