Index: sys/net/if.c =================================================================== --- sys/net/if.c +++ sys/net/if.c @@ -2015,17 +2015,27 @@ int (*vlan_setcookie_p)(struct ifnet *, void *); void *(*vlan_cookie_p)(struct ifnet *); +void +if_link_state_change(struct ifnet *ifp, int link_state) +{ + + return if_link_state_change_cond(ifp, link_state, 0); +} + /* * Handle a change in the interface link state. To avoid LORs * between driver lock and upper layer locks, as well as possible * recursions, we post event to taskqueue, and all job * is done in static do_link_state_change(). + * + * If the current link state matches link_state and force isn't + * specified no action is taken. */ void -if_link_state_change(struct ifnet *ifp, int link_state) +if_link_state_change_cond(struct ifnet *ifp, int link_state, int force) { - /* Return if state hasn't changed. */ - if (ifp->if_link_state == link_state) + + if (ifp->if_link_state == link_state && !force) return; ifp->if_link_state = link_state; Index: sys/net/if_lagg.c =================================================================== --- sys/net/if_lagg.c +++ sys/net/if_lagg.c @@ -1779,7 +1779,12 @@ break; } } - if_link_state_change(sc->sc_ifp, new_link); + + /* + * Force state change to ensure ifnet_link_event is generated allowing + * protocols to notify other nodes of potential address move. + */ + if_link_state_change_cond(sc->sc_ifp, new_link, 1); /* Update if_baudrate to reflect the max possible speed */ switch (sc->sc_proto) { @@ -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 @@ -492,6 +492,7 @@ void if_free(struct ifnet *); void if_initname(struct ifnet *, const char *, int); void if_link_state_change(struct ifnet *, int); +void if_link_state_change_cond(struct ifnet *, int, int); int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3); void if_ref(struct ifnet *); void if_rele(struct ifnet *); Index: sys/netinet/if_ether.h =================================================================== --- sys/netinet/if_ether.h +++ sys/netinet/if_ether.h @@ -119,7 +119,8 @@ void arprequest(struct ifnet *, const struct in_addr *, const struct in_addr *, u_char *); void arp_ifinit(struct ifnet *, struct ifaddr *); -void arp_announce_ifaddr(struct ifnet *, struct in_addr addr, u_char *); +void arp_announce(struct ifnet *); +void arp_announce_ifaddr(struct ifnet *, const struct in_addr *addr, u_char *); #endif #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", @@ -1144,43 +1145,87 @@ if (ntohl(dst_in->sin_addr.s_addr) == INADDR_ANY) return; - arp_announce_ifaddr(ifp, dst_in->sin_addr, IF_LLADDR(ifp)); + arp_announce_ifaddr(ifp, &dst_in->sin_addr, IF_LLADDR(ifp)); arp_add_ifa_lle(ifp, dst); } void -arp_announce_ifaddr(struct ifnet *ifp, struct in_addr addr, u_char *enaddr) +arp_announce_ifaddr(struct ifnet *ifp, const struct in_addr *addr, u_char *enaddr) { - if (ntohl(addr.s_addr) != INADDR_ANY) - arprequest(ifp, &addr, &addr, enaddr); + if (ntohl(addr->s_addr) != INADDR_ANY) + arprequest(ifp, addr, addr, enaddr); } /* - * 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 __noinline +arp_announce(struct ifnet *ifp) { struct ifaddr *ifa; + struct ann { + struct in_addr addr; + u_char enaddr[ETHER_ADDR_LEN]; + STAILQ_ENTRY(ann) entries; + } *ann1, *ann2; + STAILQ_HEAD(, ann) head = STAILQ_HEAD_INITIALIZER(head); + + if (!(ifp->if_flags & IFF_UP) || (ifp->if_flags & IFF_NOARP)) + return; + /* Take a copy then process to avoid locking issue. */ + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family == AF_INET) - arp_ifinit(ifp, ifa); + if (ifa->ifa_addr->sa_family == AF_INET) { + ann1 = (struct ann*)malloc(sizeof(struct ann), M_TEMP, + M_NOWAIT); + if (ann1 == NULL) { + log(LOG_INFO, + "arp_announce: new ann malloc failed\n"); + continue; + } + + bcopy(IFA_IN(ifa), &ann1->addr, sizeof(ann1->addr)); + bcopy(IF_LLADDR(ifp), &ann1->enaddr, + sizeof(ann1->enaddr)); + STAILQ_INSERT_TAIL(&head, ann1, entries); + } } + IF_ADDR_RUNLOCK(ifp); + + ann1 = STAILQ_FIRST(&head); + while (ann1 != NULL) { + arp_announce_ifaddr(ifp, &ann1->addr, ann1->enaddr); + ann2 = STAILQ_NEXT(ann1, entries); + free(ann1, M_TEMP); + ann1 = ann2; + } +} + +/* + * 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 event. + * 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 +1233,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/in_var.h =================================================================== --- sys/netinet/in_var.h +++ sys/netinet/in_var.h @@ -129,6 +129,9 @@ #define IN_IFADDR_WLOCK_ASSERT() rm_assert(&in_ifaddr_lock, RA_WLOCKED) #define IN_IFADDR_WUNLOCK() rm_wunlock(&in_ifaddr_lock) +#define IFA_IN(ifa) \ + (&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr) + /* * Macro for finding the internet address structure (in_ifaddr) * corresponding to one of our IP addresses (in_addr). 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_IN(ifa), + LLADDR(&sc->sc_addr)); } } @@ -1037,18 +1036,15 @@ 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); - DELAY(1000); /* XXX */ + nd6_announce_ifaddr(sc->sc_carpdev, IFA_IN6(ifa), + IFA_ND_NA_FLAG_OVERRIDE(ifa)); + nd6_announce_delay(ifa); } } 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/in6_var.h =================================================================== --- sys/netinet6/in6_var.h +++ sys/netinet6/in6_var.h @@ -399,6 +399,11 @@ #define IA6_SIN6(ia) (&((ia)->ia_addr)) #define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr)) #define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr) +#define IFA_ND_NA_FLAG_OVERRIDE(x) \ + IFA_IN6_ANYCAST(x) ? 0 : ND_NA_FLAG_OVERRIDE +#define IFA_IN6_ANYCAST(x) \ + (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) ? 1 : 0 +#define IN6_MAX_ANYCAST_DELAY_TIME_MS 1000000 #define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr) #define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr) Index: sys/netinet6/nd6.h =================================================================== --- sys/netinet6/nd6.h +++ sys/netinet6/nd6.h @@ -398,6 +398,9 @@ #ifdef VIMAGE void nd6_destroy(void); #endif +void nd6_announce(struct ifnet *); +void nd6_announce_ifaddr(struct ifnet *, const struct in6_addr *, u_long); +int nd6_announce_delay(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 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,7 @@ #endif static eventhandler_tag lle_event_eh; +static eventhandler_tag ifnet_link_event_eh; /* for debugging? */ #if 0 @@ -196,6 +198,97 @@ 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 __noinline +nd6_announce(struct ifnet *ifp) +{ + struct ifaddr *ifa; + struct ann { + struct in6_addr addr; + u_long flags; + int delay; + STAILQ_ENTRY(ann) entries; + } *ann1, *ann2; + STAILQ_HEAD(, ann) head = STAILQ_HEAD_INITIALIZER(head); + + if (!(ifp->if_flags & IFF_UP)) + return; + + /* Take a copy then process to avoid locking issue. */ + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family == AF_INET6) { + ann1 = (struct ann*)malloc(sizeof(struct ann), M_TEMP, + M_NOWAIT); + if (ann1 == NULL) { + log(LOG_INFO, + "nd6_announce: new ann malloc failed\n"); + continue; + } + + bcopy(IFA_IN6(ifa), &ann1->addr, sizeof(ann1->addr)); + ann1->flags = IFA_ND_NA_FLAG_OVERRIDE(ifa); + ann1->delay = nd6_announce_delay(ifa); + STAILQ_INSERT_TAIL(&head, ann1, entries); + } + } + IF_ADDR_RUNLOCK(ifp); + + ann1 = STAILQ_FIRST(&head); + while (ann1 != NULL) { + nd6_announce_ifaddr(ifp, &ann1->addr, ann1->flags); + ann2 = STAILQ_NEXT(ann1, entries); + if (ann2 != NULL) { + DELAY(ann1->delay); + } + free(ann1, M_TEMP); + ann1 = ann2; + } +} + +/* + * Return the delay required for announcements of the address as per RFC 4861. + */ +int +nd6_announce_delay(struct ifaddr *ifa) +{ + + if (IFA_IN6_ANYCAST(ifa)) { + /* + * Random value between 0 and MAX_ANYCAST_DELAY_TIME + * as per section 7.2.7. + */ + return (random() % IN6_MAX_ANYCAST_DELAY_TIME_MS); + } + + /* Small delay as per section 7.2.6. */ + return (1000); +} + +/* + * Send neighbor advertisement to notify other nodes of changes. + */ +void +nd6_announce_ifaddr(struct ifnet *ifp, const struct in6_addr *addr, u_long flags) +{ + + nd6_na_output(ifp, &in6addr_linklocal_allnodes, addr, flags, 1, NULL); +} + void nd6_init(void) { @@ -211,9 +304,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 +319,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