summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/nd6.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/nd6.c')
-rw-r--r--sys/netinet6/nd6.c104
1 files changed, 65 insertions, 39 deletions
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index cf97302..7ede15e 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -539,6 +539,44 @@ nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src)
return (src);
}
+/*
+ * Switch @lle state to new state optionally arming timers.
+ */
+void
+nd6_llinfo_setstate(struct llentry *lle, int newstate)
+{
+ struct ifnet *ifp;
+ long delay;
+
+ delay = 0;
+
+ switch (newstate) {
+ case ND6_LLINFO_INCOMPLETE:
+ ifp = lle->lle_tbl->llt_ifp;
+ delay = (long)ND_IFINFO(ifp)->retrans * hz / 1000;
+ break;
+ case ND6_LLINFO_REACHABLE:
+ if (!ND6_LLINFO_PERMANENT(lle)) {
+ ifp = lle->lle_tbl->llt_ifp;
+ delay = (long)ND_IFINFO(ifp)->reachable * hz;
+ }
+ break;
+ case ND6_LLINFO_STALE:
+ delay = (long)V_nd6_gctimer * hz;
+ break;
+ case ND6_LLINFO_DELAY:
+ lle->la_asked = 0;
+ delay = (long)V_nd6_delay * hz;
+ break;
+ }
+
+ if (delay > 0)
+ nd6_llinfo_settimer_locked(lle, delay);
+
+ lle->ln_state = newstate;
+}
+
+
void
nd6_llinfo_settimer(struct llentry *ln, long tick)
{
@@ -638,10 +676,8 @@ nd6_llinfo_timer(void *arg)
}
break;
case ND6_LLINFO_REACHABLE:
- if (!ND6_LLINFO_PERMANENT(ln)) {
- ln->ln_state = ND6_LLINFO_STALE;
- nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
- }
+ if (!ND6_LLINFO_PERMANENT(ln))
+ nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
break;
case ND6_LLINFO_STALE:
@@ -657,12 +693,10 @@ nd6_llinfo_timer(void *arg)
if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
/* We need NUD */
ln->la_asked = 1;
- ln->ln_state = ND6_LLINFO_PROBE;
+ nd6_llinfo_setstate(ln, ND6_LLINFO_PROBE);
send_ns = 1;
- } else {
- ln->ln_state = ND6_LLINFO_STALE; /* XXX */
- nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
- }
+ } else
+ nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); /* XXX */
break;
case ND6_LLINFO_PROBE:
if (ln->la_asked < V_nd6_umaxtries) {
@@ -1316,11 +1350,7 @@ nd6_nud_hint(struct rtentry *rt, struct in6_addr *dst6, int force)
}
}
- ln->ln_state = ND6_LLINFO_REACHABLE;
- if (!ND6_LLINFO_PERMANENT(ln)) {
- nd6_llinfo_settimer_locked(ln,
- (long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
- }
+ nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE);
done:
LLE_WUNLOCK(ln);
}
@@ -1729,7 +1759,6 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
if (lladdr != NULL) {
bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen);
ln->la_flags |= LLE_VALID;
- ln->ln_state = ND6_LLINFO_STALE;
}
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(ln);
@@ -1738,10 +1767,11 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
if (ln_tmp == NULL)
lltable_link_entry(LLTABLE6(ifp), ln);
IF_AFDATA_WUNLOCK(ifp);
- if (ln_tmp == NULL)
+ if (ln_tmp == NULL) {
/* No existing lle, mark as new entry */
is_newentry = 1;
- else {
+ nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
+ } else {
lltable_free_entry(LLTABLE6(ifp), ln);
ln = ln_tmp;
ln_tmp = NULL;
@@ -1787,7 +1817,7 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
*/
bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen);
ln->la_flags |= LLE_VALID;
- ln->ln_state = ND6_LLINFO_STALE;
+ nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
@@ -1890,9 +1920,7 @@ nd6_grab_holdchain(struct llentry *ln, struct mbuf **chain,
* detection on expiration.
* (RFC 2461 7.3.3)
*/
- ln->la_asked = 0;
- ln->ln_state = ND6_LLINFO_DELAY;
- nd6_llinfo_settimer_locked(ln, (long)V_nd6_delay * hz);
+ nd6_llinfo_setstate(ln, ND6_LLINFO_DELAY);
}
}
@@ -2032,6 +2060,8 @@ nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m,
const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags)
{
struct llentry *lle = NULL, *lle_tmp;
+ struct in6_addr *psrc, src;
+ int send_ns;
/*
* Address resolution or Neighbor Unreachability Detection
@@ -2059,7 +2089,6 @@ nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m,
m_freem(m);
return (ENOBUFS);
}
- lle->ln_state = ND6_LLINFO_NOSTATE;
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(lle);
@@ -2095,11 +2124,8 @@ nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m,
* neighbor unreachability detection on expiration.
* (RFC 2461 7.3.3)
*/
- if (lle->ln_state == ND6_LLINFO_STALE) {
- lle->la_asked = 0;
- lle->ln_state = ND6_LLINFO_DELAY;
- nd6_llinfo_settimer_locked(lle, (long)V_nd6_delay * hz);
- }
+ if (lle->ln_state == ND6_LLINFO_STALE)
+ nd6_llinfo_setstate(lle, ND6_LLINFO_DELAY);
/*
* If the neighbor cache entry has a state other than INCOMPLETE
@@ -2121,8 +2147,6 @@ nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m,
* does not exceed nd6_maxqueuelen. When it exceeds nd6_maxqueuelen,
* the oldest packet in the queue will be removed.
*/
- if (lle->ln_state == ND6_LLINFO_NOSTATE)
- lle->ln_state = ND6_LLINFO_INCOMPLETE;
if (lle->la_hold != NULL) {
struct mbuf *m_hold;
@@ -2149,20 +2173,22 @@ nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m,
/*
* If there has been no NS for the neighbor after entering the
* INCOMPLETE state, send the first solicitation.
+ * Note that for newly-created lle la_asked will be 0,
+ * so we will transition from ND6_LLINFO_NOSTATE to
+ * ND6_LLINFO_INCOMPLETE state here.
*/
- if (!ND6_LLINFO_PERMANENT(lle) && lle->la_asked == 0) {
- struct in6_addr src, *psrc;
+ psrc = NULL;
+ send_ns = 0;
+ if (lle->la_asked == 0) {
lle->la_asked++;
-
- nd6_llinfo_settimer_locked(lle,
- (long)ND_IFINFO(ifp)->retrans * hz / 1000);
+ send_ns = 1;
psrc = nd6_llinfo_get_holdsrc(lle, &src);
- LLE_WUNLOCK(lle);
- nd6_ns_output(ifp, psrc, NULL, &dst->sin6_addr, NULL);
- } else {
- /* We did the lookup so we need to do the unlock here. */
- LLE_WUNLOCK(lle);
+
+ nd6_llinfo_setstate(lle, ND6_LLINFO_INCOMPLETE);
}
+ LLE_WUNLOCK(lle);
+ if (send_ns != 0)
+ nd6_ns_output(ifp, psrc, NULL, &dst->sin6_addr, NULL);
return (EWOULDBLOCK);
}
OpenPOWER on IntegriCloud