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/raw_ip.c =================================================================== --- sys/netinet/raw_ip.c +++ sys/netinet/raw_ip.c @@ -89,6 +89,9 @@ #define V_ripcb VNET(ripcb) #define V_ripcbinfo VNET(ripcbinfo) +static eventhandler_tag maxsockets_change_eh; +static eventhandler_tag ifnet_link_event_eh; + /* * Control and data hooks for ipfw, dummynet, divert and so on. * The data hooks are not used here but it is convenient @@ -203,6 +206,25 @@ return (0); } +static void +rip_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) +{ + struct ifaddr *ifa; + + if (linkstate != LINK_STATE_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); + } + } +} + void rip_init(void) { @@ -210,8 +232,14 @@ in_pcbinfo_init(&V_ripcbinfo, "rip", &V_ripcb, INP_PCBHASH_RAW_SIZE, 1, "ripcb", rip_inpcb_init, NULL, UMA_ZONE_NOFREE, IPI_HASHFIELDS_NONE); - EVENTHANDLER_REGISTER(maxsockets_change, rip_zone_change, NULL, - EVENTHANDLER_PRI_ANY); + + maxsockets_change_eh = EVENTHANDLER_REGISTER(maxsockets_change, + rip_zone_change, NULL, EVENTHANDLER_PRI_ANY); + + if (IS_DEFAULT_VNET(curvnet)) { + ifnet_link_event_eh = EVENTHANDLER_REGISTER(ifnet_link_event, + rip_ifnet_link_event, 0, EVENTHANDLER_PRI_ANY); + } } #ifdef VIMAGE @@ -220,6 +248,12 @@ { in_pcbinfo_destroy(&V_ripcbinfo); + + EVENTHANDLER_DEREGISTER(maxsockets_change, maxsockets_change_eh); + + if (IS_DEFAULT_VNET(curvnet)) { + EVENTHANDLER_DEREGISTER(ifnet_link_event, ifnet_link_event_eh); + } } #endif 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