📄 nd6.c
字号:
return 0;
}
/*
* ND6 timer routine to expire default route list and prefix list
*/
void
nd6_timer(ignored_arg)
void *ignored_arg;
{
int s;
struct llinfo_nd6 *ln;
struct nd_defrouter *dr;
struct nd_prefix *pr;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
struct ifnet *ifp;
struct in6_ifaddr *ia6, *nia6;
struct in6_addrlifetime *lt6;
#ifdef __NetBSD__
s = splsoftnet();
#else
s = splnet();
#endif
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
callout_reset(&nd6_timer_ch, nd6_prune * hz,
nd6_timer, NULL);
#elif defined(__OpenBSD__)
timeout_set(&nd6_timer_ch, nd6_timer, NULL);
timeout_add(&nd6_timer_ch, nd6_prune * hz);
#else
timeout(nd6_timer, (caddr_t)0, nd6_prune * hz);
#endif
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->if_index];
dst = (struct sockaddr_in6 *)rt_key(rt);
if (ln->ln_expire > time_second) {
ln = next;
continue;
}
/* sanity check */
if (!rt)
panic("rt=0 in nd6_timer(ln=%p)\n", ln);
if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
panic("rt_llinfo(%p) is not equal to ln(%p)\n",
rt->rt_llinfo, ln);
if (!dst)
panic("dst=0 in nd6_timer(ln=%p)\n", ln);
switch (ln->ln_state) {
case ND6_LLINFO_INCOMPLETE:
if (ln->ln_asked < nd6_mmaxtries) {
ln->ln_asked++;
ln->ln_expire = time_second +
nd_ifinfo[ifp->if_index].retrans / 1000;
nd6_ns_output(ifp, NULL, &dst->sin6_addr,
ln, 0);
} else {
struct mbuf *m = ln->ln_hold;
if (m) {
struct ip6_hdr *ip6_in;
struct sockaddr_in6 sin6_in;
int64_t szoneid, dzoneid;
/*
* 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;
/*
* XXX: for scoped addresses, we should
* disambiguate the zone. We should
* perhaps hang sockaddr_in6 as aux
* data in the mbuf.
*/
ip6_in = mtod(m, struct ip6_hdr *);
szoneid = in6_addr2zoneid(rt->rt_ifp,
&ip6_in->ip6_src);
dzoneid = in6_addr2zoneid(rt->rt_ifp,
&ip6_in->ip6_dst);
if (szoneid < 0 || dzoneid < 0) {
/* impossible... */
m_freem(m);
} else {
bzero(&sin6_in,
sizeof(sin6_in));
sin6_in.sin6_addr = ip6_in->ip6_src;
sin6_in.sin6_scope_id = szoneid;
in6_embedscope(&ip6_in->ip6_src,
&sin6_in);
bzero(&sin6_in,
sizeof(sin6_in));
sin6_in.sin6_addr = ip6_in->ip6_dst;
sin6_in.sin6_scope_id = dzoneid;
in6_embedscope(&ip6_in->ip6_dst,
&sin6_in);
icmp6_error(m,
ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0);
}
ln->ln_hold = NULL;
}
next = nd6_free(rt, 0);
}
break;
case ND6_LLINFO_REACHABLE:
if (ln->ln_expire) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + 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_second +
ndi->retrans / 1000;
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr,
ln, 0);
} else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */
ln->ln_expire = time_second + nd6_gctimer;
}
break;
case ND6_LLINFO_PROBE:
if (ln->ln_asked < nd6_umaxtries) {
ln->ln_asked++;
ln->ln_expire = time_second +
nd_ifinfo[ifp->if_index].retrans / 1000;
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) {
if (dr->expire && dr->expire < time_second) {
struct nd_defrouter *t;
t = TAILQ_NEXT(dr, dr_entry);
defrtrlist_del(dr);
dr = t;
} else {
dr = TAILQ_NEXT(dr, dr_entry);
}
}
/*
* expire interface addresses.
* in the past the loop was inside prefix expiry processing.
* However, from a stricter speci-confrmance standpoint, we should
* rather separate address lifetimes and prefix lifetimes.
*/
addrloop:
for (ia6 = in6_ifaddr; ia6; ia6 = nia6) {
nia6 = ia6->ia_next;
/* check address lifetime */
lt6 = &ia6->ia6_lifetime;
if (IFA6_IS_INVALID(ia6)) {
int regen = 0;
/*
* If the expiring address is temporary, try
* regenerating a new one. This would be useful when
* we suspended a laptop PC, then turned it on after a
* period that could invalidate all temporary
* addresses. Although we may have to restart the
* loop (see below), it must be after purging the
* address. Otherwise, we'd see an infinite loop of
* regeneration.
*/
if (ip6_use_tempaddr &&
(ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0) {
if (regen_tmpaddr(ia6) == 0)
regen = 1;
}
in6_purgeaddr(&ia6->ia_ifa);
if (regen)
goto addrloop; /* XXX: see below */
}
if (IFA6_IS_DEPRECATED(ia6)) {
int oldflags = ia6->ia6_flags;
ia6->ia6_flags |= IN6_IFF_DEPRECATED;
/*
* If a temporary address has just become deprecated,
* regenerate a new one if possible.
*/
if (ip6_use_tempaddr &&
(ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
(oldflags & IN6_IFF_DEPRECATED) == 0) {
if (regen_tmpaddr(ia6) == 0) {
/*
* A new temporary address is
* generated.
* XXX: this means the address chain
* has changed while we are still in
* the loop. Although the change
* would not cause disaster (because
* it's not a deletion, but an
* addition,) we'd rather restart the
* loop just for safety. Or does this
* significantly reduce performance??
*/
goto addrloop;
}
}
} else {
/*
* A new RA might have made a deprecated address
* preferred.
*/
ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
}
}
/* expire prefix list */
pr = nd_prefix.lh_first;
while (pr) {
/*
* check prefix lifetime.
* since pltime is just for autoconf, pltime processing for
* prefix is not necessary.
*/
if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME &&
time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) {
struct nd_prefix *t;
t = pr->ndpr_next;
/*
* address expiration and prefix expiration are
* separate. NEVER perform in6_purgeaddr here.
*/
prelist_remove(pr);
pr = t;
} else
pr = pr->ndpr_next;
}
splx(s);
}
static int
regen_tmpaddr(ia6)
struct in6_ifaddr *ia6; /* deprecated/invalidated temporary address */
{
struct ifaddr *ifa;
struct ifnet *ifp;
struct in6_ifaddr *public_ifa6 = NULL;
ifp = ia6->ia_ifa.ifa_ifp;
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa;
ifa = ifa->ifa_list.tqe_next)
#endif
{
struct in6_ifaddr *it6;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
it6 = (struct in6_ifaddr *)ifa;
/* ignore no autoconf addresses. */
if ((it6->ia6_flags & IN6_IFF_AUTOCONF) == 0)
continue;
/* ignore autoconf addresses with different prefixes. */
if (it6->ia6_ndpr == NULL || it6->ia6_ndpr != ia6->ia6_ndpr)
continue;
/*
* Now we are looking at an autoconf address with the same
* prefix as ours. If the address is temporary and is still
* preferred, do not create another one. It would be rare, but
* could happen, for example, when we resume a laptop PC after
* a long period.
*/
if ((it6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
!IFA6_IS_DEPRECATED(it6)) {
public_ifa6 = NULL;
break;
}
/*
* This is a public autoconf address that has the same prefix
* as ours. If it is preferred, keep it. We can't break the
* loop here, because there may be a still-preferred temporary
* address with the prefix.
*/
if (!IFA6_IS_DEPRECATED(it6))
public_ifa6 = it6;
}
if (public_ifa6 != NULL) {
int e;
if ((e = in6_tmpifadd(public_ifa6, 0)) != 0) {
log(LOG_NOTICE, "regen_tmpaddr: failed to create a new"
" tmp addr,errno=%d\n", e);
return(-1);
}
return(0);
}
return(-1);
}
/*
* Nuke neighbor cache/prefix/default router management table, right before
* ifp goes away.
*/
void
nd6_purge(ifp)
struct ifnet *ifp;
{
struct llinfo_nd6 *ln, *nln;
struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
/*
* Nuke default router list entries toward ifp.
* We defer removal of default router list entries that is installed
* in the routing table, in order to keep additional side effects as
* small as possible.
*/
for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
ndr = TAILQ_NEXT(dr, dr_entry);
if (dr->installed)
continue;
if (dr->ifp == ifp)
defrtrlist_del(dr);
}
for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
ndr = TAILQ_NEXT(dr, dr_entry);
if (!dr->installed)
continue;
if (dr->ifp == ifp)
defrtrlist_del(dr);
}
/* Nuke prefix list entries toward ifp */
for (pr = nd_prefix.lh_first; pr; pr = npr) {
npr = pr->ndpr_next;
if (pr->ndpr_ifp == ifp) {
/*
* Previously, pr->ndpr_addr is removed as well,
* but I strongly believe we don't have to do it.
* nd6_purge() is only called from in6_ifdetach(),
* which removes all the associated interface addresses
* by itself.
* (jinmei@kame.net 20010129)
*/
prelist_remove(pr);
}
}
/* cancel default outgoing interface setting */
if (nd6_defifindex == ifp->if_index)
nd6_setdefaultiface(0);
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
/* refresh default router list */
defrouter_select();
}
/*
* Nuke neighbor cache entries for the ifp.
* Note that rt->rt_ifp may not be the same as ifp,
* due to KAME goto ours hack. See RTM_RESOLVE case in
* nd6_rtrequest(), and ip6_input().
*/
ln = llinfo_nd6.ln_next;
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt;
struct sockaddr_dl *sdl;
nln = ln->ln_next;
rt = ln->ln_rt;
if (rt && rt->rt_gateway &&
rt->rt_gateway->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)rt->rt_gateway;
if (sdl->sdl_index == ifp->if_index)
nln = nd6_free(rt, 0);
}
ln = nln;
}
}
struct rtentry *
nd6_lookup(addr6, create, ifp)
struct in6_addr *addr6;
int create;
struct ifnet *ifp;
{
struct rtentry *rt;
struct sockaddr_in6 sin6;
#ifdef SCOPEDROUTING
int64_t zoneid;
#endif
bzero(&sin6, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr6;
#ifdef SCOPEDROUTING
if ((zoneid = in6_addr2zoneid(ifp, addr6)) < 0)
return(NULL);
sin6.sin6_scope_id = zoneid;
#endif
rt = rtalloc1((struct sockaddr *)&sin6, create
#ifdef __FreeBSD__
, 0UL
#endif /* __FreeBSD__ */
);
if (rt && (rt->rt_flags & RTF_LLINFO) == 0) {
/*
* This is the case for the default route.
* If we want to create a neighbor cache for the address, we
* should free the route for the destination and allocate an
* interface route.
*/
if (create) {
RTFREE(rt);
rt = 0;
}
}
if (!rt) {
if (create && ifp) {
int e;
/*
* If no route is available and create is set,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -