summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6.c4
-rw-r--r--sys/netinet6/nd6.c102
-rw-r--r--sys/netinet6/nd6_nbr.c42
3 files changed, 90 insertions, 58 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 00ad6cc..ba3ceae9 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -2349,10 +2349,12 @@ in6_lltable_new(const struct sockaddr *l3addr, u_int flags)
if (lle == NULL) /* NB: caller generates msg */
return NULL;
- callout_init(&lle->base.ln_timer_ch, CALLOUT_MPSAFE);
lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
lle->base.lle_refcnt = 1;
LLE_LOCK_INIT(&lle->base);
+ callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
+ CALLOUT_RETURNUNLOCKED);
+
return &lle->base;
}
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index b22a373..c4ff308 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -411,6 +411,8 @@ nd6_llinfo_settimer_locked(struct llentry *ln, long tick)
{
int canceled;
+ LLE_WLOCK_ASSERT(ln);
+
if (tick < 0) {
ln->la_expire = 0;
ln->ln_ntick = 0;
@@ -451,6 +453,7 @@ nd6_llinfo_timer(void *arg)
KASSERT(arg != NULL, ("%s: arg NULL", __func__));
ln = (struct llentry *)arg;
+ LLE_WLOCK_ASSERT(ln);
ifp = ln->lle_tbl->llt_ifp;
CURVNET_SET(ifp->if_vnet);
@@ -458,10 +461,10 @@ nd6_llinfo_timer(void *arg)
if (ln->ln_ntick > 0) {
if (ln->ln_ntick > INT_MAX) {
ln->ln_ntick -= INT_MAX;
- nd6_llinfo_settimer(ln, INT_MAX);
+ nd6_llinfo_settimer_locked(ln, INT_MAX);
} else {
ln->ln_ntick = 0;
- nd6_llinfo_settimer(ln, ln->ln_ntick);
+ nd6_llinfo_settimer_locked(ln, ln->ln_ntick);
}
goto done;
}
@@ -482,8 +485,10 @@ nd6_llinfo_timer(void *arg)
case ND6_LLINFO_INCOMPLETE:
if (ln->la_asked < V_nd6_mmaxtries) {
ln->la_asked++;
- nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+ nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
+ LLE_WUNLOCK(ln);
nd6_ns_output(ifp, NULL, dst, ln, 0);
+ LLE_WLOCK(ln);
} else {
struct mbuf *m = ln->la_hold;
if (m) {
@@ -491,24 +496,24 @@ nd6_llinfo_timer(void *arg)
/*
* assuming every packet in la_hold has the
- * same IP header
+ * same IP header. Send error after unlock.
*/
m0 = m->m_nextpkt;
m->m_nextpkt = NULL;
- icmp6_error2(m, ICMP6_DST_UNREACH,
- ICMP6_DST_UNREACH_ADDR, 0, ifp);
-
ln->la_hold = m0;
clear_llinfo_pqueue(ln);
}
(void)nd6_free(ln, 0);
ln = NULL;
+ if (m != NULL)
+ icmp6_error2(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_ADDR, 0, ifp);
}
break;
case ND6_LLINFO_REACHABLE:
if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_state = ND6_LLINFO_STALE;
- nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
+ nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
}
break;
@@ -525,27 +530,34 @@ nd6_llinfo_timer(void *arg)
/* We need NUD */
ln->la_asked = 1;
ln->ln_state = ND6_LLINFO_PROBE;
- nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+ nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
+ LLE_WUNLOCK(ln);
nd6_ns_output(ifp, dst, dst, ln, 0);
+ LLE_WLOCK(ln);
} else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */
- nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
+ nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
}
break;
case ND6_LLINFO_PROBE:
if (ln->la_asked < V_nd6_umaxtries) {
ln->la_asked++;
- nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+ nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
+ LLE_WUNLOCK(ln);
nd6_ns_output(ifp, dst, dst, ln, 0);
+ LLE_WLOCK(ln);
} else {
(void)nd6_free(ln, 0);
ln = NULL;
}
break;
+ default:
+ panic("%s: paths in a dark night can be confusing: %d",
+ __func__, ln->ln_state);
}
done:
if (ln != NULL)
- LLE_FREE(ln);
+ LLE_FREE_LOCKED(ln);
CURVNET_RESTORE();
}
@@ -996,7 +1008,9 @@ nd6_free(struct llentry *ln, int gc)
{
struct llentry *next;
struct nd_defrouter *dr;
- struct ifnet *ifp=NULL;
+ struct ifnet *ifp;
+
+ LLE_WLOCK_ASSERT(ln);
/*
* we used to have pfctlinput(PRC_HOSTDEAD) here.
@@ -1004,12 +1018,13 @@ nd6_free(struct llentry *ln, int gc)
*/
/* cancel timer */
- nd6_llinfo_settimer(ln, -1);
+ nd6_llinfo_settimer_locked(ln, -1);
+
+ ifp = ln->lle_tbl->llt_ifp;
if (!V_ip6_forwarding) {
- int s;
- s = splnet();
- dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
+
+ dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
if (dr != NULL && dr->expire &&
ln->ln_state == ND6_LLINFO_STALE && gc) {
@@ -1026,15 +1041,16 @@ nd6_free(struct llentry *ln, int gc)
* but we intentionally keep it just in case.
*/
if (dr->expire > time_second)
- nd6_llinfo_settimer(ln,
+ nd6_llinfo_settimer_locked(ln,
(dr->expire - time_second) * hz);
else
- nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
- splx(s);
- LLE_WLOCK(ln);
+ nd6_llinfo_settimer_locked(ln,
+ (long)V_nd6_gctimer * hz);
+
+ next = LIST_NEXT(ln, lle_next);
LLE_REMREF(ln);
LLE_WUNLOCK(ln);
- return (LIST_NEXT(ln, lle_next));
+ return (next);
}
if (ln->ln_router || dr) {
@@ -1043,7 +1059,7 @@ nd6_free(struct llentry *ln, int gc)
* is in the Default Router List.
* See a corresponding comment in nd6_na_input().
*/
- rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
+ rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
}
if (dr) {
@@ -1071,11 +1087,13 @@ nd6_free(struct llentry *ln, int gc)
pfxlist_onlink_check();
/*
- * refresh default router list
+ * Refresh default router list. Have to unlock as
+ * it calls into nd6_lookup(), still holding a ref.
*/
+ LLE_WUNLOCK(ln);
defrouter_select();
+ LLE_WLOCK(ln);
}
- splx(s);
}
/*
@@ -1086,7 +1104,11 @@ nd6_free(struct llentry *ln, int gc)
*/
next = LIST_NEXT(ln, lle_next);
- ifp = ln->lle_tbl->llt_ifp;
+ /*
+ * Save to unlock. We still hold an extra reference and will not
+ * free(9) in llentry_free() if someone else holds one as well.
+ */
+ LLE_WUNLOCK(ln);
IF_AFDATA_LOCK(ifp);
LLE_WLOCK(ln);
LLE_REMREF(ln);
@@ -1875,6 +1897,9 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0,
LLE_RUNLOCK(ln);
goto retry;
}
+
+ LLE_WLOCK_ASSERT(ln);
+
if (ln->la_hold) {
struct mbuf *m_hold;
int i;
@@ -1896,17 +1921,7 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0,
} else {
ln->la_hold = m;
}
- /*
- * We did the lookup (no lle arg) so we
- * need to do the unlock here
- */
- if (lle == NULL) {
- if (flags & LLE_EXCLUSIVE)
- LLE_WUNLOCK(ln);
- else
- LLE_RUNLOCK(ln);
- }
-
+
/*
* If there has been no NS for the neighbor after entering the
* INCOMPLETE state, send the first solicitation.
@@ -1914,10 +1929,21 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0,
if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) {
ln->la_asked++;
- nd6_llinfo_settimer(ln,
+ nd6_llinfo_settimer_locked(ln,
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
+ LLE_WUNLOCK(ln);
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
+ if (lle != NULL && ln == lle)
+ LLE_WLOCK(lle);
+
+ } else if (lle == NULL || ln != lle) {
+ /*
+ * We did the lookup (no lle arg) so we
+ * need to do the unlock here.
+ */
+ LLE_WUNLOCK(ln);
}
+
return (0);
sendpkt:
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index 548cc8c..a100eb6 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -383,7 +383,6 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
struct m_tag *mtag;
struct ip6_hdr *ip6;
struct nd_neighbor_solicit *nd_ns;
- struct in6_addr *src, src_in;
struct ip6_moptions im6o;
int icmp6len;
int maxlen;
@@ -467,28 +466,35 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
* - saddr6 belongs to the outgoing interface.
* Otherwise, we perform the source address selection as usual.
*/
- struct ip6_hdr *hip6; /* hold ip6 */
- struct in6_addr *hsrc = NULL;
+ struct in6_addr *hsrc;
- if ((ln != NULL) && ln->la_hold) {
- /*
- * assuming every packet in la_hold has the same IP
- * header
- */
- hip6 = mtod(ln->la_hold, struct ip6_hdr *);
- /* XXX pullup? */
- if (sizeof(*hip6) < ln->la_hold->m_len)
- hsrc = &hip6->ip6_src;
- else
- hsrc = NULL;
+ hsrc = NULL;
+ if (ln != NULL) {
+ LLE_RLOCK(ln);
+ if (ln->la_hold != NULL) {
+ struct ip6_hdr *hip6; /* hold ip6 */
+
+ /*
+ * assuming every packet in la_hold has the same IP
+ * header
+ */
+ hip6 = mtod(ln->la_hold, struct ip6_hdr *);
+ /* XXX pullup? */
+ if (sizeof(*hip6) < ln->la_hold->m_len) {
+ ip6->ip6_src = hip6->ip6_src;
+ hsrc = &hip6->ip6_src;
+ }
+ }
+ LLE_RUNLOCK(ln);
}
if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp,
hsrc)) != NULL) {
- src = hsrc;
+ /* ip6_src set already. */
ifa_free(ifa);
} else {
int error;
struct sockaddr_in6 dst_sa;
+ struct in6_addr src_in;
bzero(&dst_sa, sizeof(dst_sa));
dst_sa.sin6_family = AF_INET6;
@@ -506,7 +512,7 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
error));
goto bad;
}
- src = &src_in;
+ ip6->ip6_src = src_in;
}
} else {
/*
@@ -516,10 +522,8 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
* above), but we do so here explicitly to make the intention
* clearer.
*/
- bzero(&src_in, sizeof(src_in));
- src = &src_in;
+ bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
}
- ip6->ip6_src = *src;
nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
nd_ns->nd_ns_code = 0;
OpenPOWER on IntegriCloud