diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -155,7 +155,8 @@ /* ND6 queue flags */ #define ND6_QUEUE_FLAG_NEWGUA 0x01 /* new global unicast address event */ #define ND6_QUEUE_FLAG_LLADDR 0x02 /* link-layer address change event */ -#define ND6_QUEUE_FLAG_ANYCAST 0x04 /* delay NA for anycast or proxy address */ +#define ND6_QUEUE_FLAG_ANYCAST 0x04 /* delay NA for anycast address */ +#define ND6_QUEUE_FLAG_PROXY 0x08 /* delay NA for proxy address */ /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* 1sec */ @@ -375,7 +376,6 @@ void nd6_dad_stop(struct ifaddr *); void nd6_grand_start(struct ifaddr *, uint32_t); void nd6_queue_stop(struct ifaddr *); -void nd6_delayed_na_start(struct ifaddr *, struct in6_addr *, u_int, uint32_t); /* nd6_rtr.c */ void nd6_rs_input(struct mbuf *, int, int); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -98,6 +98,8 @@ const struct in6_addr *, u_long, int, struct sockaddr *, u_int); static void nd6_ns_output_fib(struct ifnet *, const struct in6_addr *, const struct in6_addr *, const struct in6_addr *, uint8_t *, u_int); +static void nd6_queue_add(struct ifaddr *, struct in6_addr *, + struct in6_addr *, struct sockaddr_dl *, int, uint32_t); static struct ifaddr *nd6_proxy_fill_sdl(struct ifnet *, const struct in6_addr *, struct sockaddr_dl *); @@ -123,10 +125,12 @@ struct nd_queue { TAILQ_ENTRY(nd_queue) ndq_list; - struct ifaddr *ndq_ifa; - struct in6_addr ndq_daddr; - uint32_t ndq_flags; - struct callout ndq_callout; + struct ifaddr *ndq_ifa; + struct in6_addr ndq_daddr; + struct in6_addr ndq_taddr; + struct sockaddr_dl ndq_sdl; + uint32_t ndq_flags; + struct callout ndq_callout; }; /* @@ -147,7 +151,8 @@ union nd_opts ndopts; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; char *lladdr; - int anycast, lladdrlen, proxy, rflag, tentative, tlladdr; + int lladdrlen, rflag, tentative, tlladdr; + uint32_t dflags; ifa = NULL; @@ -275,10 +280,10 @@ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ - proxy = 0; + dflags = 0; if (ifa == NULL) { if ((ifa = nd6_proxy_fill_sdl(ifp, &taddr6, &proxydl)) != NULL) - proxy = 1; + dflags |= ND6_QUEUE_FLAG_PROXY; } if (ifa == NULL) { /* @@ -288,8 +293,9 @@ */ goto freeit; } + if ((((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) != 0) + dflags |= ND6_QUEUE_FLAG_ANYCAST; 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; @@ -340,7 +346,7 @@ * Link-Layer Address option is not included, the Override flag SHOULD * be set to zero. Otherwise, the Override flag SHOULD be set to one. */ - if (anycast == 0 && proxy == 0 && (tlladdr & ND6_NA_OPT_LLA) != 0) + if (dflags == 0 && (tlladdr & ND6_NA_OPT_LLA) != 0) rflag |= ND_NA_FLAG_OVERRIDE; /* * If the source address is unspecified address, entries must not @@ -361,12 +367,11 @@ * be delayed by a random time between 0 and MAX_ANYCAST_DELAY_TIME * to reduce the probability of network congestion. */ - if (anycast == 0) - nd6_na_output_fib(ifp, &saddr6, &taddr6, rflag, tlladdr, - proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); + if (dflags == 0) + nd6_na_output_fib(ifp, &saddr6, &taddr6, rflag, tlladdr, NULL, M_GETFIB(m)); else - nd6_delayed_na_start(ifa, &saddr6, arc4random() % - (MAX_ANYCAST_DELAY_TIME * hz), ND6_QUEUE_FLAG_ANYCAST); + nd6_queue_add(ifa, &saddr6, &taddr6, &proxydl, arc4random() % + (MAX_ANYCAST_DELAY_TIME * hz), dflags); freeit: if (ifa != NULL) ifa_free(ifa); @@ -1671,10 +1676,12 @@ struct ifaddr *ifa = ndq->ndq_ifa; struct ifnet *ifp; struct in6_ifextra *ext; - struct in6_addr daddr; + struct in6_addr daddr, taddr; + struct sockaddr_dl sdl; struct epoch_tracker et; int delay, tlladdr; u_long flags; + bool proxy; KASSERT(ifa != NULL, ("ND6 queue entry %p with no address", ndq)); @@ -1684,7 +1691,10 @@ NET_EPOCH_ENTER(et); daddr = ndq->ndq_daddr; - tlladdr = ND6_NA_OPT_LLA; + taddr = ndq->ndq_taddr; + tlladdr = 0; + if (IN6_IS_ADDR_MULTICAST(&daddr)) + tlladdr |= ND6_NA_OPT_LLA; flags = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0; if ((ext->nd_flags & ND6_IFF_ACCEPT_RTADV) != 0 && V_ip6_norbit_raif) flags &= ~ND_NA_FLAG_ROUTER; @@ -1711,15 +1721,22 @@ /* anycast advertisement delay rule (RFC 4861 7.2.7, SHOULD) */ if ((ndq->ndq_flags & ND6_QUEUE_FLAG_ANYCAST) != 0) flags |= ND_NA_FLAG_SOLICITED; + /* proxy advertisement delay rule (RFC 4861 7.2.8, SHOULD) */ + proxy = false; + if ((ndq->ndq_flags & ND6_QUEUE_FLAG_PROXY) != 0) { + flags |= ND_NA_FLAG_SOLICITED; + sdl = ndq->ndq_sdl; + proxy = true; + } /* Wait at least a RetransTimer before removing from queue */ delay = ext->nd_retrans * hz / 1000; callout_reset(&ndq->ndq_callout, delay, nd6_queue_rel, ndq); - IF_ADDR_WUNLOCK(ifp); + IF_ADDR_WUNLOCK(ifa->ifa_ifp); if (__predict_true(in6_setscope(&daddr, ifp, NULL) == 0)) - nd6_na_output_fib(ifp, &daddr, IFA_IN6(ifa), flags, tlladdr, - NULL, ifp->if_fib); + nd6_na_output_fib(ifp, &daddr, &taddr, flags, tlladdr, + proxy ? (struct sockaddr *)&sdl : NULL, ifp->if_fib); NET_EPOCH_EXIT(et); CURVNET_RESTORE(); @@ -1727,7 +1744,7 @@ static void nd6_queue_add(struct ifaddr *ifa, struct in6_addr *daddr, - int delay, uint32_t flags) + struct in6_addr *taddr, struct sockaddr_dl *sdl, int delay, uint32_t flags) { struct nd_queue *ndq; struct ifnet *ifp = ifa->ifa_ifp; @@ -1755,6 +1772,11 @@ ndq->ndq_ifa = ifa; ifa_ref(ndq->ndq_ifa); memcpy(&ndq->ndq_daddr, daddr, sizeof(struct in6_addr)); + if ((flags & ND6_QUEUE_FLAG_PROXY) != 0) { + memcpy(&ndq->ndq_taddr, taddr, sizeof(struct in6_addr)); + memcpy(&ndq->ndq_sdl, sdl, sizeof(struct sockaddr_dl)); + } else + ndq->ndq_taddr = *IFA_IN6(ifa); ndq->ndq_flags = flags; TAILQ_INSERT_TAIL(&ext->nd_queue, ndq, ndq_list); @@ -1816,7 +1838,7 @@ daddr = in6addr_linklocal_allnodes; delay = ext->nd_retrans * hz / 1000; - nd6_queue_add(ifa, &daddr, count * delay, flags); + nd6_queue_add(ifa, &daddr, NULL, NULL, count * delay, flags); } /* @@ -1838,16 +1860,3 @@ } IF_ADDR_WUNLOCK(ifa->ifa_ifp); } - -/* - * Send delayed NA for specified address. - * Called by nd6_ns_input for anycast or proxy NA - */ -void -nd6_delayed_na_start(struct ifaddr *ifa, struct in6_addr *daddr, - u_int delay, uint32_t flags) -{ - - NET_EPOCH_ASSERT(); - nd6_queue_add(ifa, daddr, delay, flags); -} diff --git a/tests/sys/netinet6/proxy_ndp.sh b/tests/sys/netinet6/proxy_ndp.sh --- a/tests/sys/netinet6/proxy_ndp.sh +++ b/tests/sys/netinet6/proxy_ndp.sh @@ -199,7 +199,7 @@ # jname_b sends a NS before ICMPv6 Echo Request for the proxy address. # jname_a responds with a NA resolving the proxy address. # Then there must be a NDP entry of the proxy address in jname_b. - jexec ${jname_b} ping -c1 -t1 ${proxy_addr} + jexec ${jname_b} ping -c1 -t2 ${proxy_addr} atf_check -o match:"${proxy_addr} +${proxy_mac} +${epair0}b" \ jexec ${jname_b} ndp -an }