-/* $FreeBSD: src/sys/netinet6/nd6.c,v 1.2.2.9 2001/07/11 09:39:04 ume Exp $ */
+/* $FreeBSD: src/sys/netinet6/nd6.c,v 1.20 2002/08/02 20:49:14 rwatson Exp $ */
/* $KAME: nd6.c,v 1.144 2001/05/24 07:44:00 itojun Exp $ */
/*
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
- struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
- u_long oldmaxmtu = ndi->maxmtu;
- u_long oldlinkmtu = ndi->linkmtu;
+
+ struct nd_ifinfo *ndi;
+ u_long oldmaxmtu, oldlinkmtu, dl_tag;
+
+ /*
+ * Make sure IPv6 is enabled for the interface first,
+ * because this can be called directly from SIOCSIFMTU for IPv4
+ */
+
+ if (ifp->if_index >= nd_ifinfo_indexlim) {
+ if (dlil_find_dltag(ifp->if_family, ifp->if_unit, PF_INET6, &dl_tag) != EPROTONOSUPPORT)
+ nd6log((LOG_INFO, "setmtu for ifp=% but nd6 is not attached\n", if_name(ifp)));
+ return; /* we're out of bound for nd_ifinfo */
+ }
+
+ ndi = &nd_ifinfo[ifp->if_index];
+ oldmaxmtu = ndi->maxmtu;
+ oldlinkmtu = ndi->linkmtu;
switch (ifp->if_type) {
case IFT_ARCNET: /* XXX MTU handling needs more work */
timeout(nd6_timer_funneled, (caddr_t)0, nd6_prune * hz);
ln = llinfo_nd6.ln_next;
- /* XXX BSD/OS separates this code -- itojun */
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt;
struct sockaddr_in6 *dst;
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",
+ if (!rt) {
+ printf("rt=0 in nd6_timer(ln=%p)\n", ln);
+ ln = next;
+ continue;
+ }
+ if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) {
+ printf("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);
+ ln = next;
+ continue;
+ }
+ if (!dst) {
+ printf("dst=0 in nd6_timer(ln=%p)\n", ln);
+ ln = next;
+ continue;
+ }
switch (ln->ln_state) {
case ND6_LLINFO_INCOMPLETE:
ln, 0);
} else {
struct mbuf *m = ln->ln_hold;
+ ln->ln_hold = NULL;
if (m) {
if (rt->rt_ifp) {
/*
/*
* If the expiring address is temporary, try
* regenerating a new one. This would be useful when
- * we suspended a laptop PC, then turned on after a
+ * 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
if (regen)
goto addrloop; /* XXX: see below */
- } else if (IFA6_IS_DEPRECATED(ia6)) {
+ }
+ if (IFA6_IS_DEPRECATED(ia6)) {
int oldflags = ia6->ia6_flags;
ia6->ia6_flags |= IN6_IFF_DEPRECATED;
* has changed while we are still in
* the loop. Although the change
* would not cause disaster (because
- * it's not an addition, but a
- * deletion,) we'd rather restart the
+ * 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 if (IFA6_IS_DEPRECATED(ia6)) {
+ } else {
/*
* A new RA might have made a deprecated address
* preferred.
* check prefix lifetime.
* since pltime is just for autoconf, pltime processing for
* prefix is not necessary.
- *
- * we offset expire time by NDPR_KEEP_EXPIRE, so that we
- * can use the old prefix information to validate the
- * next prefix information to come. See prelist_update()
- * for actual validation.
- *
- * I don't think such an offset is necessary.
- * (jinmei@kame.net, 20010130).
*/
if (pr->ndpr_expire && pr->ndpr_expire < time_second) {
struct nd_prefix *t;
if (nd6_defifindex == ifp->if_index)
nd6_setdefaultiface(0);
- if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
+ if (!ip6_forwarding && (ip6_accept_rtadv || (ifp->if_eflags & IFEF_ACCEPT_RTADVD))) {
/* refresh default router list */
bzero(&drany, sizeof(drany));
defrouter_delreq(&drany, 0);
return(NULL);
/*
- * Create a new route. RTF_LLINFO is necessary
+ * Create a new route. RTF_LLINFO is necessary
* to create a Neighbor Cache entry for the
* destination in nd6_rtrequest which will be
- * called in rtequest via ifa->ifa_rtrequest.
+ * called in rtrequest via ifa->ifa_rtrequest.
*/
if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
ifa->ifa_addr,
rtunref(rt);
/*
* Validation for the entry.
+ * Note that the check for rt_llinfo is necessary because a cloned
+ * route from a parent route that has the L flag (e.g. the default
+ * route to a p2p interface) may have the flag, too, while the
+ * destination is not actually a neighbor.
* XXX: we can't use rt->rt_ifp to check for the interface, since
* it might be the loopback interface if the entry is for our
* own address on a non-loopback interface. Instead, we should
- * use rt->rt_ifa->ifa_ifp, which would specify the REAL interface.
+ * use rt->rt_ifa->ifa_ifp, which would specify the REAL
+ * interface.
*/
- if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
- rt->rt_gateway->sa_family != AF_LINK ||
- (ifp && rt->rt_ifa->ifa_ifp != ifp)) {
+ if ((ifp->if_type !=IFT_PPP) && ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
+ rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL ||
+
+ (ifp && rt->rt_ifa->ifa_ifp != ifp))) {
if (create) {
log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n",
ip6_sprintf(addr6), ifp ? if_name(ifp) : "unspec");
/* xxx more logs... kazu */
}
- return(0);
+ return(NULL);
}
return(rt);
}
* Even if the address matches none of our addresses, it might be
* in the neighbor cache.
*/
- if (nd6_lookup(&addr->sin6_addr, 0, ifp))
+ if (nd6_lookup(&addr->sin6_addr, 0, ifp) != NULL)
return(1);
return(0);
* even though it is not harmful, it was not really necessary.
*/
- if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
+ if (!ip6_forwarding && (ip6_accept_rtadv || (rt->rt_ifp->if_eflags & IFEF_ACCEPT_RTADVD))) {
int s;
s = splnet();
dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
rt->rt_ifp);
- if (ln->ln_router || dr) {
+ if (ln && ln->ln_router || dr) {
/*
* rt6_flush must be called whether or not the neighbor
* is in the Default Router List.
/*
* Temporarily fake the state to choose a new default
* router and to perform on-link determination of
- * prefixes coreectly.
+ * prefixes correctly.
* Below the state will be set correctly,
* or the entry itself will be deleted.
*/
* Before deleting the entry, remember the next entry as the
* return value. We need this because pfxlist_onlink_check() above
* might have freed other entries (particularly the old next entry) as
- * a side effect (XXX).
+ * a side effect (XXX).
*/
- next = ln->ln_next;
+ if (ln)
+ next = ln->ln_next;
+ else
+ next = 0;
/*
* Detach the route from the routing tree and the list of neighbor
struct ifnet *ifp = rt->rt_ifp;
struct ifaddr *ifa;
- if (rt->rt_flags & RTF_GATEWAY)
+ if ((rt->rt_flags & RTF_GATEWAY))
return;
if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) {
return;
}
+ if (req == RTM_RESOLVE &&
+ (nd6_need_cache(ifp) == 0 || /* stf case */
+ !nd6_is_addr_neighbor((struct sockaddr_in6 *)rt_key(rt), ifp))) {
+ /*
+ * FreeBSD and BSD/OS often make a cloned host route based
+ * on a less-specific route (e.g. the default route).
+ * If the less specific route does not have a "gateway"
+ * (this is the case when the route just goes to a p2p or an
+ * stf interface), we'll mistakenly make a neighbor cache for
+ * the host route, and will see strange neighbor solicitation
+ * for the corresponding destination. In order to avoid the
+ * confusion, we check if the destination of the route is
+ * a neighbor in terms of neighbor discovery, and stop the
+ * process if not. Additionally, we remove the LLINFO flag
+ * so that ndp(8) will not try to get the neighbor information
+ * of the destination.
+ */
+ rt->rt_flags &= ~RTF_LLINFO;
+ return;
+ }
+
switch (req) {
case RTM_ADD:
/*
if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) {
/*
* Case 1: This route should come from
- * a route to interface. RTF_LLINFO flag is set
+ * a route to interface. RTF_LLINFO flag is set
* for a host route whose destination should be
* treated as on-link.
*/
if (ln && ln->ln_expire == 0) {
/* kludge for desktops */
#if 0
- printf("nd6_request: time.tv_sec is zero; "
+ printf("nd6_rtequest: time.tv_sec is zero; "
"treat it as 1\n");
#endif
ln->ln_expire = 1;
}
#endif
- if (rt->rt_flags & RTF_CLONING)
+ if ((rt->rt_flags & RTF_CLONING))
break;
}
/*
SDL(gate)->sdl_alen = ifp->if_addrlen;
}
if (nd6_useloopback) {
- rt->rt_ifp = &loif[0]; /*XXX*/
+ rt->rt_ifp = &loif[0]; /* XXX */
/*
* Make sure rt_ifa be equal to the ifaddr
* corresponding to the address.
rt->rt_flags &= ~RTF_LLINFO;
if (ln->ln_hold)
m_freem(ln->ln_hold);
+ ln->ln_hold = NULL;
Free((caddr_t)ln);
}
}
/* do we really have to remove addresses as well? */
for (ia = in6_ifaddr; ia; ia = ia_next) {
- /* ia might be removed. keep the next ptr. */
+ /* ia might be removed. keep the next ptr. */
ia_next = ia->ia_next;
if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0)
* 1 -- y -- (7) * STALE
*/
- if (lladdr) { /*(3-5) and (7)*/
+ if (lladdr) { /* (3-5) and (7) */
/*
* Record source link-layer address
* XXX is it dependent to ifp->if_type?
}
if (!is_newentry) {
- if ((!olladdr && lladdr) /*(3)*/
- || (olladdr && lladdr && llchange)) { /*(5)*/
+ if ((!olladdr && lladdr) /* (3) */
+ || (olladdr && lladdr && llchange)) { /* (5) */
do_update = 1;
newstate = ND6_LLINFO_STALE;
- } else /*(1-2,4)*/
+ } else /* (1-2,4) */
do_update = 0;
} else {
do_update = 1;
- if (!lladdr) /*(6)*/
+ if (!lladdr) /* (6) */
newstate = ND6_LLINFO_NOSTATE;
- else /*(7)*/
+ else /* (7) */
newstate = ND6_LLINFO_STALE;
}
/*
* New entry must have is_router flag cleared.
*/
- if (is_newentry) /*(6-7)*/
+ if (is_newentry) /* (6-7) */
ln->ln_router = 0;
break;
case ND_REDIRECT:
*/
if (code == ND_REDIRECT_ROUTER)
ln->ln_router = 1;
- else if (is_newentry) /*(6-7)*/
+ else if (is_newentry) /* (6-7) */
ln->ln_router = 0;
break;
case ND_ROUTER_SOLICIT:
/*
* Mark an entry with lladdr as a router.
*/
- if ((!is_newentry && (olladdr || lladdr)) /*(2-5)*/
- || (is_newentry && lladdr)) { /*(7)*/
+ if ((!is_newentry && (olladdr || lladdr)) /* (2-5) */
+ || (is_newentry && lladdr)) { /* (7) */
ln->ln_router = 1;
}
break;
* for those are not autoconfigured hosts, we explicitly avoid such
* cases for safety.
*/
- if (do_update && ln->ln_router && !ip6_forwarding && ip6_accept_rtadv)
+ if (do_update && ln->ln_router && !ip6_forwarding && (ip6_accept_rtadv || (ifp->if_eflags & IFEF_ACCEPT_RTADVD)))
defrouter_select();
return rt;
}
-
static void
nd6_slowtimo(ignored_arg)
void *ignored_arg;
goto sendpkt;
/*
- * next hop determination. This routine is derived from ether_outpout.
+ * next hop determination. This routine is derived from ether_outpout.
*/
if (rt) {
if ((rt->rt_flags & RTF_UP) == 0) {
/*
* We skip link-layer address resolution and NUD
* if the gateway is not a neighbor from ND point
- * of view, regardless the value of the
- * nd_ifinfo.flags.
- * The second condition is a bit tricky: we skip
+ * 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.
*/
else {
/*
* Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
- * the condition below is not very efficient. But we believe
+ * 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) &&
/*
* If the neighbor cache entry has a state other than INCOMPLETE
- * (i.e. its link-layer address is already reloved), just
+ * (i.e. its link-layer address is already resolved), just
* send the packet.
*/
if (ln->ln_state > ND6_LLINFO_INCOMPLETE)
/*
* There is a neighbor cache entry, but no ethernet address
- * response yet. Replace the held mbuf (if any) with this
+ * response yet. Replace the held mbuf (if any) with this
* latest one.
*
- * XXX Does the code conform to rate-limiting rule?
- * (RFC 2461 7.2.2)
+ * 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;
/* Make sure the HW checksum flags are cleaned before sending the packet */
- m->m_pkthdr.rcvif = (struct ifnet *)0;
m->m_pkthdr.csum_data = 0;
m->m_pkthdr.csum_flags = 0;
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ m->m_pkthdr.rcvif = origifp; /* forwarding rules require the original scope_id */
return (dlil_output(ifptodlt(origifp, PF_INET6), m, (caddr_t)rt, (struct sockaddr *)dst,0));
}
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
return (dlil_output(ifptodlt(ifp, PF_INET6), m, (caddr_t)rt, (struct sockaddr *)dst, 0));
#else
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
*desten = 0;
return(1);
default:
- m_freem(m);
- return(0);
+ return(0); /* caller will free mbuf */
}
}
if (rt == NULL) {
/* this could happen, if we could not allocate memory */
- m_freem(m);
- return(0);
+ return(0); /* caller will free mbuf */
}
if (rt->rt_gateway->sa_family != AF_LINK) {
printf("nd6_storelladdr: something odd happens\n");
- m_freem(m);
- return(0);
+ return(0); /* caller will free mbuf */
}
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\n");
- m_freem(m);
- return(0);
+ return(0); /* caller will free mbuf */
}
bcopy(LLADDR(sdl), desten, sdl->sdl_alen);