Index: sys/net/if.c =================================================================== --- sys/net/if.c +++ sys/net/if.c @@ -126,7 +126,7 @@ void (*bridge_linkstate_p)(struct ifnet *ifp); void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); -void (*lagg_linkstate_p)(struct ifnet *ifp, int state); +void (*lagg_linkstate_p)(struct ifnet *ifp); /* These are external hooks for CARP. */ void (*carp_linkstate_p)(struct ifnet *ifp); void (*carp_demote_adj_p)(int, char *); @@ -1984,6 +1984,8 @@ if (ifp->if_carp) (*carp_linkstate_p)(ifp); + if (ifp->if_lagg) + (*lagg_linkstate_p)(ifp); rt_ifmsg(ifp); } @@ -2005,6 +2007,8 @@ pfctlinput(PRC_IFUP, ifa->ifa_addr); if (ifp->if_carp) (*carp_linkstate_p)(ifp); + if (ifp->if_lagg) + (*lagg_linkstate_p)(ifp); rt_ifmsg(ifp); #ifdef INET6 in6_if_up(ifp); @@ -2019,17 +2023,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; @@ -2057,7 +2071,7 @@ if (ifp->if_bridge) (*bridge_linkstate_p)(ifp); if (ifp->if_lagg) - (*lagg_linkstate_p)(ifp, link_state); + (*lagg_linkstate_p)(ifp); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, Index: sys/net/if_lagg.h =================================================================== --- sys/net/if_lagg.h +++ sys/net/if_lagg.h @@ -281,7 +281,7 @@ #define LAGG_UNLOCK_ASSERT(_sc) rm_assert(&(_sc)->sc_mtx, RA_UNLOCKED) extern struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *); -extern void (*lagg_linkstate_p)(struct ifnet *, int ); +extern void (*lagg_linkstate_p)(struct ifnet *); int lagg_enqueue(struct ifnet *, struct mbuf *); Index: sys/net/if_lagg.c =================================================================== --- sys/net/if_lagg.c +++ sys/net/if_lagg.c @@ -106,7 +106,7 @@ static int lagg_port_destroy(struct lagg_port *, int); static struct mbuf *lagg_input(struct ifnet *, struct mbuf *); static void lagg_linkstate(struct lagg_softc *); -static void lagg_port_state(struct ifnet *, int); +static void lagg_port_state(struct ifnet *); static int lagg_port_ioctl(struct ifnet *, u_long, caddr_t); static int lagg_port_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); @@ -1774,7 +1774,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) { @@ -1797,7 +1802,7 @@ } static void -lagg_port_state(struct ifnet *ifp, int state) +lagg_port_state(struct ifnet *ifp) { struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg; struct lagg_softc *sc = NULL; @@ -1813,7 +1818,7 @@ LAGG_WUNLOCK(sc); } -struct lagg_port * +static 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 @@ -500,6 +500,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_addr(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 @@ -107,6 +107,7 @@ #endif /* VIMAGE */ static VNET_DEFINE(int, arp_maxhold) = 1; +static VNET_DEFINE(int, arp_on_link) = 1; #define V_arpt_keep VNET(arpt_keep) #define V_arpt_down VNET(arpt_down) @@ -114,6 +115,7 @@ #define V_arp_maxtries VNET(arp_maxtries) #define V_arp_proxyall VNET(arp_proxyall) #define V_arp_maxhold VNET(arp_maxhold) +#define V_arp_on_link VNET(arp_on_link) SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arpt_keep), 0, @@ -136,6 +138,9 @@ CTLFLAG_RW, &arp_maxpps, 0, "Maximum number of remotely triggered ARP messages that can be " "logged per second"); +SYSCTL_INT(_net_link_ether_inet, OID_AUTO, arp_on_link, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(arp_on_link), 0, + "Send gratuitous ARP's on interface link up events"); #define ARP_LOG(pri, ...) do { \ if (ppsratecheck(&arp_lastlog, &arp_curpps, arp_maxpps)) \ @@ -156,6 +161,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", @@ -1176,43 +1182,96 @@ if (ntohl(dst_in->sin_addr.s_addr) == INADDR_ANY) return; - arp_announce_ifaddr(ifp, dst_in->sin_addr, IF_LLADDR(ifp)); + arp_announce_addr(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) +void __noinline +arp_announce_addr(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) { + int i, cnt, entries; + u_char *lladdr; struct ifaddr *ifa; + struct in_addr *addr, *head; + if (!(ifp->if_flags & IFF_UP) || (ifp->if_flags & IFF_NOARP)) + return; + + entries = 8; + cnt = 0; + head = malloc(sizeof(*addr) * entries, M_TEMP, M_NOWAIT); + if (head == NULL) { + log(LOG_INFO, "arp_announce: malloc %d entries failed\n", + entries); + return; + } + + /* Take a copy then process to avoid locking issues. */ + 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) + continue; + + if (cnt == entries) { + addr = (struct in_addr *)realloc(head, sizeof(*addr) * + (entries + 8), M_TEMP, M_NOWAIT); + if (addr == NULL) { + log(LOG_INFO, "arp_announce: realloc to %d " + "entries failed\n", entries + 8); + /* Process what we have. */ + break; + } + entries += 8; + head = addr; + } + + addr = head + cnt; + bcopy(IFA_IN(ifa), addr, sizeof(*addr)); + cnt++; } + IF_ADDR_RUNLOCK(ifp); + + lladdr = IF_LLADDR(ifp); + for (i = 0; i < cnt; i++) { + arp_announce_addr(ifp, head + i, lladdr); + } + free(head, M_TEMP); +} + +/* + * 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 && V_arp_on_link) + 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 @@ -1220,8 +1279,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_addr(sc->sc_carpdev, IFA_IN(ifa), + LLADDR(&sc->sc_addr)); } } @@ -1037,18 +1036,16 @@ 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) + if (ifa->ifa_addr->sa_family != AF_INET6 || + IFA_ND6_NA_UNSOLICITED_SKIP(ifa)) continue; - in6 = IFA_IN6(ifa); - nd6_na_output(sc->sc_carpdev, &mcast, in6, - ND_NA_FLAG_OVERRIDE, 1, NULL); - DELAY(1000); /* XXX */ + nd6_na_output_unsolicited_addr(sc->sc_carpdev, IFA_IN6(ifa), + IFA_ND6_NA_BASE_FLAGS(sc->sc_carpdev, ifa)); + nd6_na_unsolicited_addr_delay(ifa); } } Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c +++ sys/netinet6/in6.c @@ -114,7 +114,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,16 @@ #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_IN6_FLAGS(ifa) ((struct in6_ifaddr *)ifa)->ia6_flags +#define IFA_ND6_NA_BASE_FLAGS(ifp, ifa) \ + (IFA_IN6_FLAGS(ifa) & IN6_IFF_ANYCAST ? 0 : ND_NA_FLAG_OVERRIDE) | \ + ((V_ip6_forwarding && !(ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && \ + V_ip6_norbit_raif)) ? ND_NA_FLAG_ROUTER : 0) +#define IFA_ND6_NA_UNSOLICITED_SKIP(ifa) \ + (IFA_IN6_FLAGS(ifa) & (IN6_IFF_DUPLICATED | IN6_IFF_DEPRECATED | \ + IN6_IFF_TENTATIVE)) != 0 +#define IN6_MAX_ANYCAST_DELAY_TIME_MS 1000000 +#define IN6_BROADCAST_DELAY_TIME_MS 1000 #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,10 @@ #ifdef VIMAGE void nd6_destroy(void); #endif +void nd6_na_output_unsolicited(struct ifnet *); +void nd6_na_output_unsolicited_addr(struct ifnet *, const struct in6_addr *, + u_long); +int nd6_na_unsolicited_addr_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 @@ -102,8 +103,12 @@ * layer hints */ static VNET_DEFINE(int, nd6_maxqueuelen) = 1; /* max pkts cached in unresolved * ND entries */ + +static VNET_DEFINE(int, nd6_on_link) = 1; /* Send unsolicited ND's on link up */ + #define V_nd6_maxndopt VNET(nd6_maxndopt) #define V_nd6_maxqueuelen VNET(nd6_maxqueuelen) +#define V_nd6_on_link VNET(nd6_on_link) #ifdef ND6_DEBUG VNET_DEFINE(int, nd6_debug) = 1; @@ -112,6 +117,7 @@ #endif static eventhandler_tag lle_event_eh; +static eventhandler_tag ifnet_link_event_eh; /* for debugging? */ #if 0 @@ -196,6 +202,13 @@ 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 && V_nd6_on_link) + nd6_na_output_unsolicited(ifp); +} void nd6_init(void) { @@ -211,9 +224,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 +239,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 @@ -2318,13 +2336,18 @@ SYSCTL_DECL(_net_inet6_icmp6); #endif SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, - CTLFLAG_RD, nd6_sysctl_drlist, ""); + CTLFLAG_RD, nd6_sysctl_drlist, "List default routers"); SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist, - CTLFLAG_RD, nd6_sysctl_prlist, ""); + CTLFLAG_RD, nd6_sysctl_prlist, "List prefixes"); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen, - CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_maxqueuelen), 1, ""); + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_maxqueuelen), 1, + "Max packets cached in unresolved ND entries"); SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_gctimer, - CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_gctimer), (60 * 60 * 24), ""); + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_gctimer), (60 * 60 * 24), + "Interface in seconds between garbage collection passes"); +SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_on_link, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(nd6_on_link), 0, + "Send unsolicited neighbor discovery on interface link up events"); static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) Index: sys/netinet6/nd6_nbr.c =================================================================== --- sys/netinet6/nd6_nbr.c +++ sys/netinet6/nd6_nbr.c @@ -124,20 +124,16 @@ struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; - struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa = NULL; + u_long flags; int lladdrlen = 0; - int anycast = 0, proxy = 0, tentative = 0; + int proxy = 0; int tlladdr; - int rflag; union nd_opts ndopts; struct sockaddr_dl proxydl; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; - rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0; - if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif) - rflag = 0; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); @@ -229,10 +225,7 @@ * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ - if (!IN6_IS_ADDR_MULTICAST(&daddr6)) - tlladdr = 0; - else - tlladdr = 1; + tlladdr = !IN6_IS_ADDR_MULTICAST(&daddr6); /* * Target address (taddr6) must be either: @@ -289,9 +282,6 @@ */ goto freeit; } - myaddr6 = *IFA_IN6(ifa); - anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; - tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; @@ -303,7 +293,7 @@ goto bad; } - if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { + if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(ip6bufs, &saddr6))); goto freeit; @@ -321,7 +311,7 @@ * * The processing is defined in RFC 2462. */ - if (tentative) { + if (IFA_IN6_FLAGS(ifa) & IN6_IFF_TENTATIVE) { /* * If source address is unspecified address, it is for * duplicate address detection. @@ -335,6 +325,10 @@ goto freeit; } + flags = IFA_ND6_NA_BASE_FLAGS(ifp, ifa); + if (proxy || !tlladdr) + flags &= ~ND_NA_FLAG_OVERRIDE; + /* * If the source address is unspecified address, entries must not * be created or updated. @@ -349,20 +343,16 @@ in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; - nd6_na_output_fib(ifp, &in6_all, &taddr6, - ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | - rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, - M_GETFIB(m)); + nd6_na_output_fib(ifp, &in6_all, &taddr6, flags, tlladdr, + proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); - nd6_na_output_fib(ifp, &saddr6, &taddr6, - ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | - rflag | ND_NA_FLAG_SOLICITED, tlladdr, - proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); + nd6_na_output_fib(ifp, &saddr6, &taddr6, flags | ND_NA_FLAG_SOLICITED, + tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); freeit: if (ifa != NULL) ifa_free(ifa); @@ -1589,3 +1579,110 @@ nd6_dad_rele(dp); } } + +/* + * 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_na_output_unsolicited(struct ifnet *ifp) +{ + int i, cnt, entries; + struct ifaddr *ifa; + struct ann { + struct in6_addr addr; + u_long flags; + int delay; + } *ann1, *head; + + if (!(ifp->if_flags & IFF_UP)) + return; + + entries = 8; + cnt = 0; + head = malloc(sizeof(struct ann) * entries, M_TEMP, M_WAITOK); + + /* Take a copy then process to avoid locking issues. */ + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET6 || + IFA_ND6_NA_UNSOLICITED_SKIP(ifa)) + continue; + + if (cnt == entries) { + ann1 = (struct ann*)realloc(head, sizeof(struct ann) * + (entries + 8), M_TEMP, M_NOWAIT); + if (ann1 == NULL) { + log(LOG_INFO, "nd6_announce: realloc to %d " + "entries failed\n", entries + 8); + /* Process what we have. */ + break; + } + entries += 8; + head = ann1; + } + + ann1 = head + cnt; + bcopy(IFA_IN6(ifa), &ann1->addr, sizeof(ann1->addr)); + ann1->flags = IFA_ND6_NA_BASE_FLAGS(ifp, ifa); + ann1->delay = nd6_na_unsolicited_addr_delay(ifa); + cnt++; + } + IF_ADDR_RUNLOCK(ifp); + + for (i = 0; i < cnt;) { + ann1 = head + i; + nd6_na_output_unsolicited_addr(ifp, &ann1->addr, ann1->flags); + i++; + if (i == cnt) + break; + DELAY(ann1->delay); + } + free(head, M_TEMP); +} + +/* + * Return the delay required for announcements of the address as per RFC 4861. + */ +int +nd6_na_unsolicited_addr_delay(struct ifaddr *ifa) +{ + + if (IFA_IN6_FLAGS(ifa) & IN6_IFF_ANYCAST) { + /* + * 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 (IN6_BROADCAST_DELAY_TIME_MS); +} + +/* + * Send an unsolicited neighbor advertisement for an address to notify other + * nodes of changes. + */ +void __noinline +nd6_na_output_unsolicited_addr(struct ifnet *ifp, const struct in6_addr *addr, + u_long flags) +{ + int error; + struct in6_addr mcast; + + mcast = in6addr_linklocal_allnodes; + if ((error = in6_setscope(&mcast, ifp, NULL)) != 0) { + /* + * This shouldn't by possible as the only error is for loopback + * address which we're not using. + */ + log(LOG_INFO, "in6_setscope: on mcast failed: %d\n", error); + return; + } + nd6_na_output(ifp, &mcast, addr, flags, 1, NULL); +} + +