diff options
Diffstat (limited to 'sys/netinet6/nd6_nbr.c')
-rw-r--r-- | sys/netinet6/nd6_nbr.c | 143 |
1 files changed, 120 insertions, 23 deletions
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index bf43fb6..0717e11 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -124,20 +124,16 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) 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, int icmp6len) * 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, int icmp6len) */ 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, int icmp6len) 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, int icmp6len) * * 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, int icmp6len) 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, int icmp6len) 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); +} + + |