Index: sys/net/if.h =================================================================== --- sys/net/if.h +++ sys/net/if.h @@ -181,6 +181,7 @@ #define LINK_STATE_UNKNOWN 0 /* link invalid/unknown */ #define LINK_STATE_DOWN 1 /* link is down */ #define LINK_STATE_UP 2 /* link is up */ +#define LINK_STATE_UP_FORCE 3 /* link is up force event */ /* * Some convenience macros used for setting ifi_baudrate. Index: sys/net/if.c =================================================================== --- sys/net/if.c +++ sys/net/if.c @@ -2028,6 +2028,9 @@ if (ifp->if_link_state == link_state) return; + if (link_state == LINK_STATE_UP_FORCE) + link_state = LINK_STATE_UP; + ifp->if_link_state = link_state; taskqueue_enqueue(taskqueue_swi, &ifp->if_linktask); Index: sys/net/if_lagg.c =================================================================== --- sys/net/if_lagg.c +++ sys/net/if_lagg.c @@ -1775,7 +1775,12 @@ /* Our link is considered up if at least one of our ports is active */ SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { if (lp->lp_ifp->if_link_state == LINK_STATE_UP) { - new_link = LINK_STATE_UP; + /* + * Force up to ensure ifnet_link_event is generated + * allowing IP protocols to notify other nodes of + * potential address move. + */ + new_link = LINK_STATE_UP_FORCE; break; } } @@ -1818,7 +1823,7 @@ LAGG_WUNLOCK(sc); } -struct lagg_port * +static __noinline struct lagg_port * lagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) { struct lagg_port *lp_next, *rval = NULL; Index: sys/net/if_var.h =================================================================== --- sys/net/if_var.h +++ sys/net/if_var.h @@ -529,6 +529,9 @@ #define IF_LLADDR(ifp) \ LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr)) +#define IFA_SINADDR(ifa) \ + ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr + uint64_t if_setbaudrate(if_t ifp, uint64_t baudrate); uint64_t if_getbaudrate(if_t ifp); int if_setcapabilities(if_t ifp, int capabilities); Index: sys/netinet/if_ether.h =================================================================== --- sys/netinet/if_ether.h +++ sys/netinet/if_ether.h @@ -119,6 +119,7 @@ void arprequest(struct ifnet *, const struct in_addr *, const struct in_addr *, u_char *); void arp_ifinit(struct ifnet *, struct ifaddr *); +void arp_announce(struct ifnet *); void arp_announce_ifaddr(struct ifnet *, struct in_addr addr, u_char *); #endif Index: sys/netinet/if_ether.c =================================================================== --- sys/netinet/if_ether.c +++ sys/netinet/if_ether.c @@ -145,6 +145,7 @@ static void arp_iflladdr(void *arg __unused, struct ifnet *ifp); static eventhandler_tag iflladdr_tag; +static eventhandler_tag ifnet_link_event_tag; static const struct netisr_handler arp_nh = { .nh_name = "arp", @@ -1158,29 +1159,45 @@ } /* - * Sends gratuitous ARPs for each ifaddr to notify other - * nodes about the address change. + * Send gratuitous ARPs for all interfaces addresses to notify other nodes of + * changes. + * + * This is a noop if the interface isn't up or has been flagged for no ARP. */ -static __noinline void -arp_handle_ifllchange(struct ifnet *ifp) +void +arp_announce(struct ifnet *ifp) { struct ifaddr *ifa; + if (!(ifp->if_flags & IFF_UP) || (ifp->if_flags & IFF_NOARP)) + return; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET) - arp_ifinit(ifp, ifa); + arp_announce_ifaddr(ifp, IFA_SINADDR(ifa), + IF_LLADDR(ifp)); } } /* - * A handler for interface link layer address change event. + * A handler for interface linkstate change events. + */ +static void +arp_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) +{ + + if (linkstate == LINK_STATE_UP) + arp_announce(ifp); +} + +/* + * A handler for interface link layer address change events. */ static __noinline void arp_iflladdr(void *arg __unused, struct ifnet *ifp) { - if ((ifp->if_flags & IFF_UP) != 0) - arp_handle_ifllchange(ifp); + arp_announce(ifp); } static void @@ -1188,8 +1205,12 @@ { netisr_register(&arp_nh); - if (IS_DEFAULT_VNET(curvnet)) + + if (IS_DEFAULT_VNET(curvnet)) { iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, arp_iflladdr, NULL, EVENTHANDLER_PRI_ANY); + ifnet_link_event_tag = EVENTHANDLER_REGISTER(ifnet_link_event, + arp_ifnet_link_event, 0, EVENTHANDLER_PRI_ANY); + } } SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); Index: sys/netinet/ip_carp.c =================================================================== --- sys/netinet/ip_carp.c +++ sys/netinet/ip_carp.c @@ -1009,13 +1009,12 @@ carp_send_arp(struct carp_softc *sc) { struct ifaddr *ifa; - struct in_addr addr; CARP_FOREACH_IFA(sc, ifa) { if (ifa->ifa_addr->sa_family != AF_INET) continue; - addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - arp_announce_ifaddr(sc->sc_carpdev, addr, LLADDR(&sc->sc_addr)); + arp_announce_ifaddr(sc->sc_carpdev, IFA_SINADDR(ifa), + LLADDR(&sc->sc_addr)); } } @@ -1037,17 +1036,13 @@ static void carp_send_na(struct carp_softc *sc) { - static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; struct ifaddr *ifa; - struct in6_addr *in6; CARP_FOREACH_IFA(sc, ifa) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; - in6 = IFA_IN6(ifa); - nd6_na_output(sc->sc_carpdev, &mcast, in6, - ND_NA_FLAG_OVERRIDE, 1, NULL); + nd6_announce_ifaddr(sc->sc_carpdev, ifa); DELAY(1000); /* XXX */ } } Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c +++ sys/netinet6/in6.c @@ -113,7 +113,7 @@ #define V_icmp6_nodeinfo_oldmcprefix VNET(icmp6_nodeinfo_oldmcprefix) /* - * Definitions of some costant IP6 addresses. + * Definitions of some constant IP6 addresses. */ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; Index: sys/netinet6/nd6.h =================================================================== --- sys/netinet6/nd6.h +++ sys/netinet6/nd6.h @@ -398,6 +398,8 @@ #ifdef VIMAGE void nd6_destroy(void); #endif +void nd6_announce(struct ifnet *); +void nd6_announce_ifaddr(struct ifnet *, struct ifaddr *); struct nd_ifinfo *nd6_ifattach(struct ifnet *); void nd6_ifdetach(struct nd_ifinfo *); int nd6_is_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *); Index: sys/netinet6/nd6.c =================================================================== --- sys/netinet6/nd6.c +++ sys/netinet6/nd6.c @@ -112,6 +112,7 @@ #endif static eventhandler_tag lle_event_eh; +static eventhandler_tag ifnet_link_event_eh; /* for debugging? */ #if 0 @@ -196,6 +197,46 @@ type == RTM_ADD ? RTF_UP: 0), 0, RT_DEFAULT_FIB); } +static void +nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) +{ + + if (linkstate == LINK_STATE_UP) + nd6_announce(ifp); +} + +/* + * Send unsolicited neighbor advertisements for all interface addresses to + * notify other nodes of changes. + * + * This is a noop if the interface isn't up. + */ +void +nd6_announce(struct ifnet *ifp) +{ + struct ifaddr *ifa; + + if (!(ifp->if_flags & IFF_UP)) + return; + + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family == AF_INET6) + nd6_announce_ifaddr(ifp, ifa); + } +} + +/* + * Send neighbor advertisement to notify other nodes of changes. + */ +void +nd6_announce_ifaddr(struct ifnet *ifp, struct ifaddr *ifa) +{ + + nd6_na_output(ifp, &in6addr_linklocal_allnodes, IFA_IN6(ifa), + (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_ANYCAST) ? 0 : ND_NA_FLAG_OVERRIDE, 1, NULL); +} + void nd6_init(void) { @@ -211,9 +252,12 @@ nd6_slowtimo, curvnet); nd6_dad_init(); - if (IS_DEFAULT_VNET(curvnet)) + if (IS_DEFAULT_VNET(curvnet)) { lle_event_eh = EVENTHANDLER_REGISTER(lle_event, nd6_lle_event, NULL, EVENTHANDLER_PRI_ANY); + ifnet_link_event_eh = EVENTHANDLER_REGISTER(ifnet_link_event, + nd6_ifnet_link_event, NULL, EVENTHANDLER_PRI_ANY); + } } #ifdef VIMAGE @@ -223,8 +267,10 @@ callout_drain(&V_nd6_slowtimo_ch); callout_drain(&V_nd6_timer_ch); - if (IS_DEFAULT_VNET(curvnet)) + if (IS_DEFAULT_VNET(curvnet)) { EVENTHANDLER_DEREGISTER(lle_event, lle_event_eh); + EVENTHANDLER_DEREGISTER(ifnet_link_event, ifnet_link_event_eh); + } } #endif