diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2011-02-24 01:25:18 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2011-02-24 01:25:18 +0000 |
commit | 56ae1aefb7377b83438be70b2e89493b0c1c6c4f (patch) | |
tree | 33a61d2d465090855b271f9b3f1c25da5bd8d59d /sys | |
parent | 884f2d70183a62e3f2fbb08712b7e86153a3c195 (diff) |
Prevent the nd6_addr_add() work queue task from adding multiple addresses
for the same prefix. Tested by giovanni@, steven@, Dennis den Brok.
ok dlg miod claudio
Diffstat (limited to 'sys')
-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; |