Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Dec 2015 12:02:47 +0300
From:      Alexander V. Chernikov <melifaro@freebsd.org>
To:        Steven Hartland <smh@freebsd.org>, "src-committers@freebsd.org" <src-committers@freebsd.org>, "svn-src-all@freebsd.org" <svn-src-all@freebsd.org>, "svn-src-head@freebsd.org" <svn-src-head@freebsd.org>
Subject:   Re: svn commit: r292275 - in head/sys: net netinet netinet6
Message-ID:  <633021450256567@web13o.yandex.ru>
In-Reply-To: <201512151602.tBFG2BTX089543@repo.freebsd.org>
References:  null <201512151602.tBFG2BTX089543@repo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
15.12.2015, 19:02, "Steven Hartland" <smh@FreeBSD.org>:
> Author: smh
> Date: Tue Dec 15 16:02:11 2015
> New Revision: 292275
> URL: https://svnweb.freebsd.org/changeset/base/292275
>
> Log:
> ššFix lagg failover due to missing notifications
>
> ššWhen using lagg failover mode neither Gratuitous ARP (IPv4) or Unsolicited
> ššNeighbour Advertisements (IPv6) are sent to notify other nodes that the
> ššaddress may have moved.
>
> ššThis results is slow failover, dropped packets and network outages for the
> ššlagg interface when the primary link goes down.
>
> ššWe now use the new if_link_state_change_cond with the force param set to
> ššallow lagg to force through link state changes and hence fire a
> ššifnet_link_event which are now monitored by rip and nd6.
>
> ššUpon receiving these events each protocol trigger the relevant
> ššnotifications:
> šš* inet4 => Gratuitous ARP
> šš* inet6 => Unsolicited Neighbour Announce

Steven, I believe that having DELAY() called inside callout routine is incorrect - you are delaying other consumers for arbitrary amount of time.
If you really want to do it that way you should create separate taskqueue for that.
Also, destroying interface while doing these DELAYs would very likely crash the system
:"#define IN6_MAX_ANYCAST_DELAY_TIME_MS 1000000" is misguiding
There are some style(9) issues like lack of empty line between nd6_init() and nd6_ifnet_link_event()
...

>
> ššThis also fixes the carp IPv6 NA's that stopped working after r251584 which
> ššadded the ipv6_route__llma route.
>
> ššThe new behavour can be controlled using the sysctls:
> šš* net.link.ether.inet.arp_on_link
> šš* net.inet6.icmp6.nd6_on_link
>
> ššAlso removed unused param from lagg_port_state and added descriptions for the
> ššsysctls while here.
>
> ššPR: 156226
> ššMFC after: 1 month
> ššSponsored by: Multiplay
> ššDifferential Revision: https://reviews.freebsd.org/D4111
>
> Modified:
> ššhead/sys/net/if.c
> ššhead/sys/net/if_lagg.c
> ššhead/sys/net/if_lagg.h
> ššhead/sys/net/if_var.h
> ššhead/sys/netinet/if_ether.c
> ššhead/sys/netinet/if_ether.h
> ššhead/sys/netinet/in_var.h
> ššhead/sys/netinet/ip_carp.c
> ššhead/sys/netinet6/in6.c
> ššhead/sys/netinet6/in6_var.h
> ššhead/sys/netinet6/nd6.c
> ššhead/sys/netinet6/nd6.h
> ššhead/sys/netinet6/nd6_nbr.c
>
> Modified: head/sys/net/if.c
> ==============================================================================
> --- head/sys/net/if.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/net/if.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -126,7 +126,7 @@ SX_SYSINIT(ifdescr_sx, &ifdescr_sx, "ifn
>
> š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_unroute(struct ifnet *ifp, int flag,
>
> šššššššššif (ifp->if_carp)
> ššššššššššššššššš(*carp_linkstate_p)(ifp);
> + if (ifp->if_lagg)
> + (*lagg_linkstate_p)(ifp);
> šššššššššrt_ifmsg(ifp);
> š}
>
> @@ -2005,6 +2007,8 @@ if_route(struct ifnet *ifp, int flag, in
> ššššššššššššššššššššššššš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_tag_p)(struct ifnet *, uint16
> š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 @@ do_link_state_change(void *arg, int pend
> ššššššššš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,
>
> Modified: head/sys/net/if_lagg.c
> ==============================================================================
> --- head/sys/net/if_lagg.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/net/if_lagg.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -106,7 +106,7 @@ static int lagg_port_create(struct lagg_
> š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 @@ lagg_linkstate(struct lagg_softc *sc)
> ššššššššššššššššššššššššš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 @@ lagg_linkstate(struct lagg_softc *sc)
> š}
>
> š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_port_state(struct ifnet *ifp, int s
> ššššššššš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;
>
> Modified: head/sys/net/if_lagg.h
> ==============================================================================
> --- head/sys/net/if_lagg.h Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/net/if_lagg.h Tue Dec 15 16:02:11 2015 (r292275)
> @@ -281,7 +281,7 @@ struct lagg_port {
> š#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 *);
>
> Modified: head/sys/net/if_var.h
> ==============================================================================
> --- head/sys/net/if_var.h Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/net/if_var.h Tue Dec 15 16:02:11 2015 (r292275)
> @@ -500,6 +500,7 @@ struct ifmultiaddr *
> š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 *);
>
> Modified: head/sys/netinet/if_ether.c
> ==============================================================================
> --- head/sys/netinet/if_ether.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet/if_ether.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -107,6 +107,7 @@ VNET_PCPUSTAT_SYSUNINIT(arpstat);
> š#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 @@ static VNET_DEFINE(int, arp_maxhold) = 1
> š#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 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUT
> ššššššššš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_mark_lle_reachable(struc
> š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 @@ arp_ifinit(struct ifnet *ifp, struct ifa
>
> ššššššššš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 @@ arp_init(void)
> š{
>
> ššššššššš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);
>
> Modified: head/sys/netinet/if_ether.h
> ==============================================================================
> --- head/sys/netinet/if_ether.h Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet/if_ether.h Tue Dec 15 16:02:11 2015 (r292275)
> @@ -119,7 +119,8 @@ int arpresolve(struct ifnet *ifp, int is
> š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
>
> Modified: head/sys/netinet/in_var.h
> ==============================================================================
> --- head/sys/netinet/in_var.h Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet/in_var.h Tue Dec 15 16:02:11 2015 (r292275)
> @@ -129,6 +129,9 @@ extern struct rmlock in_ifaddr_lock;
> š#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).
>
> Modified: head/sys/netinet/ip_carp.c
> ==============================================================================
> --- head/sys/netinet/ip_carp.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet/ip_carp.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -1009,13 +1009,12 @@ static void
> š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 @@ carp_iamatch(struct ifaddr *ifa, uint8_t
> š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);
> ššššššššš}
> š}
>
> Modified: head/sys/netinet6/in6.c
> ==============================================================================
> --- head/sys/netinet6/in6.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet6/in6.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -114,7 +114,7 @@ VNET_DECLARE(int, icmp6_nodeinfo_oldmcpr
> š#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;
>
> Modified: head/sys/netinet6/in6_var.h
> ==============================================================================
> --- head/sys/netinet6/in6_var.h Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet6/in6_var.h Tue Dec 15 16:02:11 2015 (r292275)
> @@ -399,6 +399,16 @@ struct in6_rrenumreq {
> š#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)
>
> Modified: head/sys/netinet6/nd6.c
> ==============================================================================
> --- head/sys/netinet6/nd6.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet6/nd6.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
> š#include <sys/param.h>
> š#include <sys/systm.h>
> š#include <sys/callout.h>
> +#include <sys/random.h>
> š#include <sys/malloc.h>
> š#include <sys/mbuf.h>
> š#include <sys/socket.h>
> @@ -102,8 +103,12 @@ VNET_DEFINE(int, nd6_maxnudhint) = 0; /*
> šššššššššššššššššššššššššššššššššššššššššš* 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 @@ VNET_DEFINE(int, nd6_debug) = 0;
> š#endif
>
> šstatic eventhandler_tag lle_event_eh;
> +static eventhandler_tag ifnet_link_event_eh;
>
> š/* for debugging? */
> š#if 0
> @@ -196,6 +202,13 @@ nd6_lle_event(void *arg __unused, struct
> ššššššššššššš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_init(void)
> ššššššššššššš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 @@ nd6_destroy()
>
> ššššššššš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
>
> @@ -2457,13 +2475,18 @@ static int nd6_sysctl_prlist(SYSCTL_HAND
> š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)
>
> Modified: head/sys/netinet6/nd6.h
> ==============================================================================
> --- head/sys/netinet6/nd6.h Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet6/nd6.h Tue Dec 15 16:02:11 2015 (r292275)
> @@ -398,6 +398,10 @@ void nd6_init(void);
> š#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 *);
>
> Modified: head/sys/netinet6/nd6_nbr.c
> ==============================================================================
> --- head/sys/netinet6/nd6_nbr.c Tue Dec 15 15:48:03 2015 (r292274)
> +++ head/sys/netinet6/nd6_nbr.c Tue Dec 15 16:02:11 2015 (r292275)
> @@ -124,20 +124,16 @@ nd6_ns_input(struct mbuf *m, int off, in
> ššššššššš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 @@ nd6_ns_input(struct mbuf *m, int off, in
> šššššššššš* 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 @@ nd6_ns_input(struct mbuf *m, int off, in
> šššššššššššššššššš*/
> ššššššššššššššššš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 @@ nd6_ns_input(struct mbuf *m, int off, in
> ššššššššššššššššš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 @@ nd6_ns_input(struct mbuf *m, int off, in
> šššššššššš*
> šššššššššš* 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 @@ nd6_ns_input(struct mbuf *m, int off, in
> ššššššššššššššššš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 @@ nd6_ns_input(struct mbuf *m, int off, in
> ššššššššššššššššš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);
> @@ -1597,3 +1587,110 @@ nd6_dad_na_input(struct ifaddr *ifa)
> ššššššššššššššššš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);
> +}
> +
> +



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?633021450256567>