summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2011-02-24 01:25:18 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2011-02-24 01:25:18 +0000
commit56ae1aefb7377b83438be70b2e89493b0c1c6c4f (patch)
tree33a61d2d465090855b271f9b3f1c25da5bd8d59d /sys
parent884f2d70183a62e3f2fbb08712b7e86153a3c195 (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.c38
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;