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/netinet/if_ether.c =================================================================== --- sys/netinet/if_ether.c +++ sys/netinet/if_ether.c @@ -143,6 +143,7 @@ struct ifnet *ifp, int bridged, struct llentry *la); static void arp_mark_lle_reachable(struct llentry *la); +static eventhandler_tag ifnet_link_event_eh; static const struct netisr_handler arp_nh = { .nh_name = "arp", @@ -1151,9 +1152,41 @@ } static void +arp_notify(struct ifnet *ifp) +{ + struct ifaddr *ifa; + + if (!(ifp->if_flags & IFF_UP) || (ifp->if_flags & IFF_NOARP)) + return; + + /* + * Send gratuitous ARPs to notify other nodes about potential address + * move. + */ + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family == AF_INET) { + arp_ifinit(ifp, ifa); + } + } +} + +static void +arp_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) +{ + + if (linkstate == LINK_STATE_UP) + arp_notify(ifp); +} + +static void arp_init(void) { netisr_register(&arp_nh); + + if (IS_DEFAULT_VNET(curvnet)) { + ifnet_link_event_eh = 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/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.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,29 @@ type == RTM_ADD ? RTF_UP: 0), 0, RT_DEFAULT_FIB); } +static void +nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) +{ + struct ifaddr *ifa; + struct in6_addr in6_all; + + if (linkstate != LINK_STATE_UP) + return; + + /* + * Send neighbor advertisements to notify other nodes about potential + * address move. + */ + in6_all = in6addr_linklocal_allnodes; + + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family == AF_INET6) { + nd6_na_output(ifp, &in6_all, IFA_IN6(ifa), + ND_NA_FLAG_OVERRIDE, 1, NULL); + } + } +} + void nd6_init(void) { @@ -211,9 +235,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 +250,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