📄 nd6.c
字号:
if (ln->ln_state == ND6_LLINFO_STALE) {
/*
* XXX: since nd6_output() below will cause
* state tansition to DELAY and reset the timer,
* we must set the timer now, although it is actually
* meaningless.
*/
ln->ln_expire = time_second + nd6_gctimer;
if (ln->ln_hold) {
/*
* we assume ifp is not a p2p here, so just
* set the 2nd argument as the 1st one.
*/
nd6_output(ifp, ifp, ln->ln_hold,
(struct sockaddr_in6 *)rt_key(rt),
rt);
ln->ln_hold = NULL;
}
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/* probe right away */
ln->ln_expire = time_second;
}
}
/*
* ICMP6 type dependent behavior.
*
* NS: clear IsRouter if new entry
* RS: clear IsRouter
* RA: set IsRouter if there's lladdr
* redir: clear IsRouter if new entry
*
* RA case, (1):
* The spec says that we must set IsRouter in the following cases:
* - If lladdr exist, set IsRouter. This means (1-5).
* - If it is old entry (!newentry), set IsRouter. This means (7).
* So, based on the spec, in (1-5) and (7) cases we must set IsRouter.
* A quetion arises for (1) case. (1) case has no lladdr in the
* neighbor cache, this is similar to (6).
* This case is rare but we figured that we MUST NOT set IsRouter.
*
* newentry olladdr lladdr llchange NS RS RA redir
* D R
* 0 n n -- (1) c ? s
* 0 y n -- (2) c s s
* 0 n y -- (3) c s s
* 0 y y n (4) c s s
* 0 y y y (5) c s s
* 1 -- n -- (6) c c c s
* 1 -- y -- (7) c c s c s
*
* (c=clear s=set)
*/
switch (type & 0xff) {
case ND_NEIGHBOR_SOLICIT:
/*
* New entry must have is_router flag cleared.
*/
if (is_newentry) /* (6-7) */
ln->ln_router = 0;
break;
case ND_REDIRECT:
/*
* If the icmp is a redirect to a better router, always set the
* is_router flag. Otherwise, if the entry is newly created,
* clear the flag. [RFC 2461, sec 8.3]
*/
if (code == ND_REDIRECT_ROUTER)
ln->ln_router = 1;
else if (is_newentry) /* (6-7) */
ln->ln_router = 0;
break;
case ND_ROUTER_SOLICIT:
/*
* is_router flag must always be cleared.
*/
ln->ln_router = 0;
break;
case ND_ROUTER_ADVERT:
/*
* Mark an entry with lladdr as a router.
*/
if ((!is_newentry && (olladdr || lladdr)) /* (2-5) */
|| (is_newentry && lladdr)) { /* (7) */
ln->ln_router = 1;
}
break;
}
/*
* When the link-layer address of a router changes, select the
* best router again. In particular, when the neighbor entry is newly
* created, it might affect the selection policy.
* Question: can we restrict the first condition to the "is_newentry"
* case?
* XXX: when we hear an RA from a new router with the link-layer
* address option, defrouter_select() is called twice, since
* defrtrlist_update called the function as well. However, I believe
* we can compromise the overhead, since it only happens the first
* time.
* XXX: although defrouter_select() should not have a bad effect
* for those are not autoconfigured hosts, we explicitly avoid such
* cases for safety.
*/
if (do_update && ln->ln_router && !ip6_forwarding && ip6_accept_rtadv)
defrouter_select();
return rt;
}
static void
nd6_slowtimo(ignored_arg)
void *ignored_arg;
{
#ifdef __NetBSD__
int s = splsoftnet();
#else
int s = splnet();
#endif
int i;
struct nd_ifinfo *nd6if;
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
nd6_slowtimo, NULL);
#elif defined(__OpenBSD__)
timeout_set(&nd6_slowtimo_ch, nd6_slowtimo, NULL);
timeout_add(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz);
#else
timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz);
#endif
for (i = 1; i < if_index + 1; i++) {
if (!nd_ifinfo || i >= nd_ifinfo_indexlim)
continue;
nd6if = &nd_ifinfo[i];
if (nd6if->basereachable && /* already initialized */
(nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) {
/*
* Since reachable time rarely changes by router
* advertisements, we SHOULD insure that a new random
* value gets recomputed at least once every few hours.
* (RFC 2461, 6.3.4)
*/
nd6if->recalctm = nd6_recalc_reachtm_interval;
nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable);
}
}
splx(s);
}
#define senderr(e) { error = (e); goto bad;}
int
nd6_output(ifp, origifp, m0, dst, rt0)
struct ifnet *ifp;
struct ifnet *origifp;
struct mbuf *m0;
struct sockaddr_in6 *dst;
struct rtentry *rt0;
{
struct mbuf *m = m0;
struct rtentry *rt = rt0;
struct sockaddr_in6 *gw6 = NULL;
struct llinfo_nd6 *ln = NULL;
int error = 0;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
#if defined(__OpenBSD__) && defined(IPSEC)
struct m_tag *mtag;
#endif /* IPSEC */
if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
goto sendpkt;
if (nd6_need_cache(ifp) == 0)
goto sendpkt;
/*
* next hop determination. This routine is derived from ether_outpout.
*/
if (rt) {
if ((rt->rt_flags & RTF_UP) == 0) {
#ifdef __FreeBSD__
if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL)) !=
NULL)
#else
if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1)) !=
NULL)
#endif
{
rt->rt_refcnt--;
if (rt->rt_ifp != ifp) {
/* XXX: loop care? */
return nd6_output(ifp, origifp, m0,
dst, rt);
}
} else
senderr(EHOSTUNREACH);
}
if (rt->rt_flags & RTF_GATEWAY) {
gw6 = (struct sockaddr_in6 *)rt->rt_gateway;
/*
* We skip link-layer address resolution and NUD
* if the gateway is not a neighbor from ND point
* of view, regardless of the value of nd_ifinfo.flags.
* The second condition is a bit tricky; we skip
* if the gateway is our own address, which is
* sometimes used to install a route to a p2p link.
*/
if (!nd6_is_addr_neighbor(gw6, ifp) ||
in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) {
/*
* We allow this kind of tricky route only
* when the outgoing interface is p2p.
* XXX: we may need a more generic rule here.
*/
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
senderr(EHOSTUNREACH);
goto sendpkt;
}
if (rt->rt_gwroute == 0)
goto lookup;
if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
rtfree(rt); rt = rt0;
lookup:
#ifdef __FreeBSD__
rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL);
#else
rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
#endif
if ((rt = rt->rt_gwroute) == 0)
senderr(EHOSTUNREACH);
#if defined(__bsdi__) || defined(__NetBSD__)
/* the "G" test below also prevents rt == rt0 */
if ((rt->rt_flags & RTF_GATEWAY) ||
(rt->rt_ifp != ifp)) {
rt->rt_refcnt--;
rt0->rt_gwroute = 0;
senderr(EHOSTUNREACH);
}
#endif
}
}
}
/*
* Address resolution or Neighbor Unreachability Detection
* for the next hop.
* At this point, the destination of the packet must be a unicast
* or an anycast address(i.e. not a multicast).
*/
/* Look up the neighbor cache for the nexthop */
if (rt && (rt->rt_flags & RTF_LLINFO) != 0)
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
else {
/*
* Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
* the condition below is not very efficient. But we believe
* it is tolerable, because this should be a rare case.
*/
if (nd6_is_addr_neighbor(dst, ifp) &&
(rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL)
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
}
if (!ln || !rt) {
if ((ifp->if_flags & IFF_POINTOPOINT) == 0 &&
!(nd_ifinfo[ifp->if_index].flags & ND6_IFF_PERFORMNUD)) {
log(LOG_DEBUG,
"nd6_output: can't allocate llinfo for %s "
"(ln=%p, rt=%p)\n",
ip6_sprintf(&dst->sin6_addr), ln, rt);
senderr(EIO); /* XXX: good error? */
}
goto sendpkt; /* send anyway */
}
/* We don't have to do link-layer address resolution on a p2p link. */
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ln->ln_state < ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer;
}
/*
* The first time we send a packet to a neighbor whose entry is
* STALE, we have to change the state to DELAY and a sets a timer to
* expire in DELAY_FIRST_PROBE_TIME seconds to ensure do
* neighbor unreachability detection on expiration.
* (RFC 2461 7.3.3)
*/
if (ln->ln_state == ND6_LLINFO_STALE) {
ln->ln_asked = 0;
ln->ln_state = ND6_LLINFO_DELAY;
ln->ln_expire = time_second + nd6_delay;
}
/*
* If the neighbor cache entry has a state other than INCOMPLETE
* (i.e. its link-layer address is already resolved), just
* send the packet.
*/
if (ln->ln_state > ND6_LLINFO_INCOMPLETE)
goto sendpkt;
/*
* There is a neighbor cache entry, but no ethernet address
* response yet. Replace the held mbuf (if any) with this
* latest one.
* This code conforms to the rate-limiting rule described in Section
* 7.2.2 of RFC 2461, because the timer is set correctly after sending
* an NS below.
*/
if (ln->ln_state == ND6_LLINFO_NOSTATE)
ln->ln_state = ND6_LLINFO_INCOMPLETE;
if (ln->ln_hold)
m_freem(ln->ln_hold);
ln->ln_hold = m;
if (ln->ln_expire) {
if (ln->ln_asked < nd6_mmaxtries &&
ln->ln_expire < time_second) {
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);
}
}
return(0);
sendpkt:
#if defined(__OpenBSD__) && defined(IPSEC)
/*
* If the packet needs outgoing IPsec crypto processing and the
* interface doesn't support it, drop it.
*/
mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED, NULL);
#endif /* IPSEC */
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
#if defined(__OpenBSD__) && defined(IPSEC)
if (mtag != NULL &&
(origifp->if_capabilities & IFCAP_IPSEC) == 0) {
/* Tell IPsec to do its own crypto. */
ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1));
error = EACCES;
goto bad;
}
#endif /* IPSEC */
return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst,
rt));
}
#if defined(__OpenBSD__) && defined(IPSEC)
if (mtag != NULL &&
(ifp->if_capabilities & IFCAP_IPSEC) == 0) {
/* Tell IPsec to do its own crypto. */
ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1));
error = EACCES;
goto bad;
}
#endif /* IPSEC */
return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt));
bad:
if (m)
m_freem(m);
return (error);
}
#undef senderr
int
nd6_need_cache(ifp)
struct ifnet *ifp;
{
/*
* XXX: we currently do not make neighbor cache on any interface
* other than ARCnet, Ethernet, FDDI and GIF.
*
* RFC2893 says:
* - unidirectional tunnels needs no ND
*/
switch (ifp->if_type) {
case IFT_ARCNET:
case IFT_ETHER:
case IFT_FDDI:
case IFT_IEEE1394:
#ifdef IFT_PROPVIRTUAL
case IFT_PROPVIRTUAL:
#endif
#ifdef IFT_L2VLAN
case IFT_L2VLAN:
#endif
#ifdef IFT_IEEE80211
case IFT_IEEE80211:
#endif
case IFT_GIF: /* XXX need more cases? */
return(1);
default:
return(0);
}
}
int
nd6_storelladdr(ifp, rt, m, dst, desten)
struct ifnet *ifp;
struct rtentry *rt;
struct mbuf *m;
struct sockaddr *dst;
u_char *desten;
{
int i;
struct sockaddr_dl *sdl;
if (m->m_flags & M_MCAST) {
switch (ifp->if_type) {
case IFT_ETHER:
case IFT_FDDI:
#ifdef IFT_PROPVIRTUAL
case IFT_PROPVIRTUAL:
#endif
#ifdef IFT_L2VLAN
case IFT_L2VLAN:
#endif
#ifdef IFT_IEEE80211
case IFT_IEEE80211:
#endif
ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr,
desten);
return(1);
case IFT_IEEE1394:
/*
* netbsd can use if_broadcastaddr, but we don't do so
* to reduce # of ifdef.
*/
for (i = 0; i < ifp->if_addrlen; i++)
desten[i] = ~0;
return(1);
case IFT_ARCNET:
*desten = 0;
return(1);
default:
m_freem(m);
return(0);
}
}
if (rt == NULL) {
/* this could happen, if we could not allocate memory */
m_freem(m);
return(0);
}
if (rt->rt_gateway->sa_family != AF_LINK) {
printf("nd6_storelladdr: something odd happens\n");
m_freem(m);
return(0);
}
sdl = SDL(rt->rt_gateway);
if (sdl->sdl_alen == 0) {
/* this should be impossible, but we bark here for debugging */
printf("nd6_storelladdr: sdl_alen == 0, dst=%s, if=%s\n",
ip6_sprintf(&SIN6(dst)->sin6_addr), if_name(ifp));
m_freem(m);
return(0);
}
bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
return(1);
}
#ifdef CYGPKG_NET_FREEBSD_SYSCTL
static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS);
static int nd6_sysctl_prlist(SYSCTL_HANDLER_A
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -