From 2cadb8ca7a1ddbd04e85434b891ff48e4ac83049 Mon Sep 17 00:00:00 2001 From: itojun Date: Fri, 27 Jun 2003 08:41:08 +0000 Subject: [PATCH] split ND6 cache timer management to per-entry. increased accuracy, no O(N) loop. sync w/ kame --- sys/netinet6/nd6.c | 307 +++++++++++++++++++++++------------------ sys/netinet6/nd6.h | 13 +- sys/netinet6/nd6_nbr.c | 28 ++-- 3 files changed, 192 insertions(+), 156 deletions(-) diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 446065d91493..df3b56b0b0bc 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -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 -__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 #include @@ -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); diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 23706208301d..966e6361cf85 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -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)); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 4d75d9731960..1b1c945212fd 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -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 -__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.