split ND6 cache timer management to per-entry. increased accuracy,

no O(N) loop.   sync w/ kame
This commit is contained in:
itojun 2003-06-27 08:41:08 +00:00
parent 1d910e61f7
commit 2cadb8ca7a
3 changed files with 192 additions and 156 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: nd6.c,v 1.85 2003/06/24 07:54:48 itojun Exp $ */
/* $NetBSD: nd6.c,v 1.86 2003/06/27 08:41:08 itojun Exp $ */
/* $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $ */
/*
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.85 2003/06/24 07:54:48 itojun Exp $");
__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.86 2003/06/27 08:41:08 itojun Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -106,6 +106,7 @@ static struct sockaddr_in6 all1_sa;
static void nd6_setmtu0 __P((struct ifnet *, struct nd_ifinfo *));
static void nd6_slowtimo __P((void *));
static struct llinfo_nd6 *nd6_free __P((struct rtentry *, int));
static void nd6_llinfo_timer __P((void *));
struct callout nd6_slowtimo_ch = CALLOUT_INITIALIZER;
struct callout nd6_timer_ch = CALLOUT_INITIALIZER;
@ -372,6 +373,149 @@ skip1:
return 0;
}
/*
* ND6 timer routine to handle ND6 entries
*/
void
nd6_llinfo_settimer(ln, tick)
struct llinfo_nd6 *ln;
long tick;
{
int s;
s = splsoftnet();
if (tick < 0) {
ln->ln_expire = 0;
ln->ln_ntick = 0;
callout_stop(&ln->ln_timer_ch);
} else {
ln->ln_expire = time.tv_sec + tick / hz;
if (tick > INT_MAX) {
ln->ln_ntick = tick - INT_MAX;
callout_reset(&ln->ln_timer_ch, INT_MAX,
nd6_llinfo_timer, ln);
} else {
ln->ln_ntick = 0;
callout_reset(&ln->ln_timer_ch, tick,
nd6_llinfo_timer, ln);
}
}
splx(s);
}
static void
nd6_llinfo_timer(arg)
void *arg;
{
int s;
struct llinfo_nd6 *ln;
struct rtentry *rt;
const struct sockaddr_in6 *dst;
struct ifnet *ifp;
struct nd_ifinfo *ndi = NULL;
s = splsoftnet();
ln = (struct llinfo_nd6 *)arg;
if (ln->ln_ntick > 0) {
if (ln->ln_ntick > INT_MAX) {
ln->ln_ntick -= INT_MAX;
nd6_llinfo_settimer(ln, INT_MAX);
} else {
ln->ln_ntick = 0;
nd6_llinfo_settimer(ln, ln->ln_ntick);
}
splx(s);
return;
}
if ((rt = ln->ln_rt) == NULL)
panic("ln->ln_rt == NULL");
if ((ifp = rt->rt_ifp) == NULL)
panic("ln->ln_rt->rt_ifp == NULL");
ndi = ND_IFINFO(ifp);
dst = (struct sockaddr_in6 *)rt_key(rt);
/* sanity check */
if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
panic("rt_llinfo(%p) is not equal to ln(%p)",
rt->rt_llinfo, ln);
if (!dst)
panic("dst=0 in nd6_timer(ln=%p)", ln);
switch (ln->ln_state) {
case ND6_LLINFO_INCOMPLETE:
if (ln->ln_asked < nd6_mmaxtries) {
ln->ln_asked++;
nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
} else {
struct mbuf *m = ln->ln_hold;
if (m) {
ln->ln_hold = NULL;
/*
* Fake rcvif to make the ICMP error
* more helpful in diagnosing for the
* receiver.
* XXX: should we consider
* older rcvif?
*/
m->m_pkthdr.rcvif = rt->rt_ifp;
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0);
}
(void)nd6_free(rt, 0);
ln = NULL;
}
break;
case ND6_LLINFO_REACHABLE:
if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_state = ND6_LLINFO_STALE;
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
break;
case ND6_LLINFO_STALE:
/* Garbage Collection(RFC 2461 5.3) */
if (!ND6_LLINFO_PERMANENT(ln)) {
(void)nd6_free(rt, 1);
ln = NULL;
}
break;
case ND6_LLINFO_DELAY:
if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
/* We need NUD */
ln->ln_asked = 1;
ln->ln_state = ND6_LLINFO_PROBE;
nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
} else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
break;
case ND6_LLINFO_PROBE:
if (ln->ln_asked < nd6_umaxtries) {
ln->ln_asked++;
nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
} else {
(void)nd6_free(rt, 0);
ln = NULL;
}
break;
}
splx(s);
}
/*
* ND6 timer routine to expire default route list and prefix list
*/
@ -380,10 +524,8 @@ nd6_timer(ignored_arg)
void *ignored_arg;
{
int s;
struct llinfo_nd6 *ln;
struct nd_defrouter *dr;
struct nd_prefix *pr;
struct ifnet *ifp;
struct in6_ifaddr *ia6, *nia6;
struct in6_addrlifetime *lt6;
@ -391,108 +533,6 @@ nd6_timer(ignored_arg)
callout_reset(&nd6_timer_ch, nd6_prune * hz,
nd6_timer, NULL);
ln = llinfo_nd6.ln_next;
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt;
struct sockaddr_in6 *dst;
struct llinfo_nd6 *next = ln->ln_next;
/* XXX: used for the DELAY case only: */
struct nd_ifinfo *ndi = NULL;
if ((rt = ln->ln_rt) == NULL) {
ln = next;
continue;
}
if ((ifp = rt->rt_ifp) == NULL) {
ln = next;
continue;
}
ndi = ND_IFINFO(ifp);
dst = (struct sockaddr_in6 *)rt_key(rt);
if (ln->ln_expire > time.tv_sec) {
ln = next;
continue;
}
/* sanity check */
if (!rt)
panic("rt=0 in nd6_timer(ln=%p)", ln);
if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
panic("rt_llinfo(%p) is not equal to ln(%p)",
rt->rt_llinfo, ln);
if (!dst)
panic("dst=0 in nd6_timer(ln=%p)", ln);
switch (ln->ln_state) {
case ND6_LLINFO_INCOMPLETE:
if (ln->ln_asked < nd6_mmaxtries) {
ln->ln_asked++;
ln->ln_expire = time.tv_sec +
ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans);
nd6_ns_output(ifp, NULL, &dst->sin6_addr,
ln, 0);
} else {
struct mbuf *m = ln->ln_hold;
if (m) {
ln->ln_hold = NULL;
/*
* Fake rcvif to make the ICMP error
* more helpful in diagnosing for the
* receiver.
* XXX: should we consider
* older rcvif?
*/
m->m_pkthdr.rcvif = rt->rt_ifp;
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0);
}
next = nd6_free(rt, 0);
}
break;
case ND6_LLINFO_REACHABLE:
if (ln->ln_expire) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time.tv_sec + nd6_gctimer;
}
break;
case ND6_LLINFO_STALE:
/* Garbage Collection(RFC 2461 5.3) */
if (ln->ln_expire)
next = nd6_free(rt, 1);
break;
case ND6_LLINFO_DELAY:
if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
/* We need NUD */
ln->ln_asked = 1;
ln->ln_state = ND6_LLINFO_PROBE;
ln->ln_expire = time.tv_sec +
ND6_RETRANS_SEC(ndi->retrans);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
} else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */
ln->ln_expire = time.tv_sec + nd6_gctimer;
}
break;
case ND6_LLINFO_PROBE:
if (ln->ln_asked < nd6_umaxtries) {
ln->ln_asked++;
ln->ln_expire = time.tv_sec +
ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
} else {
next = nd6_free(rt, 0);
}
break;
}
ln = next;
}
/* expire default router list */
dr = TAILQ_FIRST(&nd_defrouter);
while (dr) {
@ -815,6 +855,9 @@ nd6_free(rt, gc)
* even though it is not harmful, it was not really necessary.
*/
/* cancel timer */
nd6_llinfo_settimer(ln, -1);
if (!ip6_forwarding) {
int s;
s = splsoftnet();
@ -835,7 +878,11 @@ nd6_free(rt, gc)
* XXX: the check for ln_state would be redundant,
* but we intentionally keep it just in case.
*/
ln->ln_expire = dr->expire;
if (dr->expire > time.tv_sec * hz)
nd6_llinfo_settimer(ln,
dr->expire - time.tv_sec * hz);
else
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
splx(s);
return (ln->ln_next);
}
@ -947,8 +994,10 @@ nd6_nud_hint(rt, dst6, force)
}
ln->ln_state = ND6_LLINFO_REACHABLE;
if (ln->ln_expire)
ln->ln_expire = time.tv_sec + ND_IFINFO(rt->rt_ifp)->reachable;
if (!ND6_LLINFO_PERMANENT(ln)) {
nd6_llinfo_settimer(ln,
(long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
}
}
void
@ -1022,17 +1071,7 @@ nd6_rtrequest(req, rt, info)
SDL(gate)->sdl_type = ifp->if_type;
SDL(gate)->sdl_index = ifp->if_index;
if (ln)
ln->ln_expire = time.tv_sec;
#if 1
if (ln && ln->ln_expire == 0) {
/* kludge for desktops */
#if 0
printf("nd6_rtrequest: time.tv_sec is zero; "
"treat it as 1\n");
#endif
ln->ln_expire = 1;
}
#endif
nd6_llinfo_settimer(ln, 0);
if ((rt->rt_flags & RTF_CLONING) != 0)
break;
}
@ -1096,6 +1135,7 @@ nd6_rtrequest(req, rt, info)
nd6_allocated++;
Bzero(ln, sizeof(*ln));
ln->ln_rt = rt;
callout_init(&ln->ln_timer_ch);
/* this is required for "ndp" command. - shin */
if (req == RTM_ADD) {
/*
@ -1111,7 +1151,7 @@ nd6_rtrequest(req, rt, info)
* initialized in rtrequest(), so rt_expire is 0.
*/
ln->ln_state = ND6_LLINFO_NOSTATE;
ln->ln_expire = time.tv_sec;
nd6_llinfo_settimer(ln, 0);
}
rt->rt_flags |= RTF_LLINFO;
ln->ln_next = llinfo_nd6.ln_next;
@ -1127,7 +1167,7 @@ nd6_rtrequest(req, rt, info)
&SIN6(rt_key(rt))->sin6_addr);
if (ifa) {
caddr_t macp = nd6_ifptomac(ifp);
ln->ln_expire = 0;
nd6_llinfo_settimer(ln, -1);
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
mine = 1;
@ -1152,7 +1192,7 @@ nd6_rtrequest(req, rt, info)
}
}
} else if (rt->rt_flags & RTF_ANNOUNCE) {
ln->ln_expire = 0;
nd6_llinfo_settimer(ln, -1);
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
@ -1201,6 +1241,7 @@ nd6_rtrequest(req, rt, info)
ln->ln_next->ln_prev = ln->ln_prev;
ln->ln_prev->ln_next = ln->ln_next;
ln->ln_prev = NULL;
nd6_llinfo_settimer(ln, -1);
rt->rt_llinfo = 0;
rt->rt_flags &= ~RTF_LLINFO;
if (ln->ln_hold)
@ -1551,7 +1592,7 @@ fail:
* we must set the timer now, although it is actually
* meaningless.
*/
ln->ln_expire = time.tv_sec + nd6_gctimer;
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
if (ln->ln_hold) {
/*
@ -1564,7 +1605,7 @@ fail:
}
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/* probe right away */
ln->ln_expire = time.tv_sec;
nd6_llinfo_settimer((void *)ln, 0);
}
}
@ -1801,7 +1842,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ln->ln_state < ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time.tv_sec + nd6_gctimer;
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
/*
@ -1814,7 +1855,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
if (ln->ln_state == ND6_LLINFO_STALE) {
ln->ln_asked = 0;
ln->ln_state = ND6_LLINFO_DELAY;
ln->ln_expire = time.tv_sec + nd6_delay;
nd6_llinfo_settimer(ln, nd6_delay * hz);
}
/*
@ -1838,17 +1879,11 @@ nd6_output(ifp, origifp, m0, dst, rt0)
/*
* If there has been no NS for the neighbor after entering the
* INCOMPLETE state, send the first solicitation.
* Technically this can be against the rate-limiting rule described in
* Section 7.2.2 of RFC 2461 because the interval to the next scheduled
* solicitation issued in nd6_timer() may be less than the specified
* retransmission time. This should not be a problem from a practical
* point of view, because we'll typically see an immediate response
* from the neighbor, which suppresses the succeeding solicitations.
*/
if (ln->ln_expire && ln->ln_asked == 0) {
if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) {
ln->ln_asked++;
ln->ln_expire = time.tv_sec +
ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans);
nd6_llinfo_settimer(ln,
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
}
return (0);

View File

@ -1,4 +1,4 @@
/* $NetBSD: nd6.h,v 1.34 2003/02/01 06:23:47 thorpej Exp $ */
/* $NetBSD: nd6.h,v 1.35 2003/06/27 08:41:08 itojun Exp $ */
/* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */
/*
@ -51,6 +51,9 @@ struct llinfo_nd6 {
short ln_state; /* reachability state */
short ln_router; /* 2^0: ND6 router bit */
int ln_byhint; /* # of times we made it reachable by UL hint */
long ln_ntick;
struct callout ln_timer_ch;
};
#define ND6_LLINFO_NOSTATE -2
@ -69,12 +72,7 @@ struct llinfo_nd6 {
#define ND6_LLINFO_PROBE 4
#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE)
/*
* Since the granularity of our retransmission timer is seconds, we should
* ensure that a positive timer value will be mapped to at least one second.
*/
#define ND6_RETRANS_SEC(r) (((r) + 999) / 1000)
#define ND6_LLINFO_PERMANENT(n) ((n)->ln_expire == 0)
struct nd_ifinfo {
u_int32_t linkmtu; /* LinkMTU */
@ -366,6 +364,7 @@ struct nd_opt_hdr *nd6_option __P((union nd_opts *));
int nd6_options __P((union nd_opts *));
struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *));
void nd6_setmtu __P((struct ifnet *));
void nd6_llinfo_settimer __P((struct llinfo_nd6 *, long));
void nd6_timer __P((void *));
void nd6_purge __P((struct ifnet *));
void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int));

View File

@ -1,4 +1,4 @@
/* $NetBSD: nd6_nbr.c,v 1.46 2003/06/24 07:54:48 itojun Exp $ */
/* $NetBSD: nd6_nbr.c,v 1.47 2003/06/27 08:41:09 itojun Exp $ */
/* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */
/*
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.46 2003/06/24 07:54:48 itojun Exp $");
__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.47 2003/06/27 08:41:09 itojun Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
@ -666,12 +666,13 @@ nd6_na_input(m, off, icmp6len)
if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (ln->ln_expire)
ln->ln_expire = time.tv_sec +
ND_IFINFO(rt->rt_ifp)->reachable;
if (!ND6_LLINFO_PERMANENT(ln)) {
nd6_llinfo_settimer(ln,
(long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
}
} else {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time.tv_sec + nd6_gctimer;
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
if ((ln->ln_router = is_router) != 0) {
/*
@ -725,7 +726,7 @@ nd6_na_input(m, off, icmp6len)
*/
if (ln->ln_state == ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time.tv_sec + nd6_gctimer;
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
}
goto freeit;
} else if (is_override /* (2a) */
@ -747,14 +748,15 @@ nd6_na_input(m, off, icmp6len)
if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (ln->ln_expire) {
ln->ln_expire = time.tv_sec +
ND_IFINFO(ifp)->reachable;
if (!ND6_LLINFO_PERMANENT(ln)) {
nd6_llinfo_settimer(ln,
(long)ND_IFINFO(ifp)->reachable * hz);
}
} else {
if (lladdr && llchange) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time.tv_sec + nd6_gctimer;
nd6_llinfo_settimer(ln,
(long)nd6_gctimer * hz);
}
}
}
@ -1132,7 +1134,7 @@ nd6_dad_start(ifa, tick)
if (tick == NULL) {
nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp,
ND6_RETRANS_SEC(ND_IFINFO(ifa->ifa_ifp)->retrans) * hz);
(long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
} else {
int ntick;
@ -1224,7 +1226,7 @@ nd6_dad_timer(ifa)
*/
nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp,
ND6_RETRANS_SEC(ND_IFINFO(ifa->ifa_ifp)->retrans) * hz);
(long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
} else {
/*
* We have transmitted sufficient number of DAD packets.