diff options
-rw-r--r-- | sys/netinet6/nd6_rtr.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index e96d15a5a31..f783f0c4489 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6_rtr.c,v 1.54 2010/09/24 14:10:52 jsing Exp $ */ +/* $OpenBSD: nd6_rtr.c,v 1.55 2011/02/24 01:25:17 stsp Exp $ */ /* $KAME: nd6_rtr.c,v 1.97 2001/02/07 11:09:13 itojun Exp $ */ /* @@ -1297,6 +1297,42 @@ nd6_addr_add(void *prptr, void *arg2) { struct nd_prefix *pr = (struct nd_prefix *)prptr; struct in6_ifaddr *ia6 = NULL; + struct ifaddr *ifa; + int ifa_plen; + + /* Because prelist_update() runs in interrupt context it may run + * again before this work queue task is run, causing multiple work + * queue tasks to be scheduled all of which add addresses for the + * same prefix. So check again if a non-deprecated address has already + * been autoconfigured for this prefix. */ + TAILQ_FOREACH(ifa, &pr->ndpr_ifp->if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ia6 = (struct in6_ifaddr *)ifa; + + /* + * Spec is not clear here, but I believe we should concentrate + * on unicast (i.e. not anycast) addresses. + * XXX: other ia6_flags? detached or duplicated? + */ + if ((ia6->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + + if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if ((ia6->ia6_flags & IN6_IFF_DEPRECATED) != 0) + continue; + + ifa_plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); + if (ifa_plen == pr->ndpr_plen && + in6_are_prefix_equal(&ia6->ia_addr.sin6_addr, + &pr->ndpr_prefix.sin6_addr, ifa_plen)) { + pr->ndpr_refcnt--; + return; + } + } if ((ia6 = in6_ifadd(pr)) != NULL) { ia6->ia6_ndpr = pr; |