diff options
author | itojun <itojun@FreeBSD.org> | 2000-07-04 16:35:15 +0000 |
---|---|---|
committer | itojun <itojun@FreeBSD.org> | 2000-07-04 16:35:15 +0000 |
commit | 5f4e854de19331a53788d6100bbcd42845056bc1 (patch) | |
tree | 3ff8c876a5868b103fb8713055d83e29a3fa38d5 /sys/netinet6/nd6.c | |
parent | bdc16885232d771a99d7dfc247cd27a44cd061f9 (diff) | |
download | FreeBSD-src-5f4e854de19331a53788d6100bbcd42845056bc1.zip FreeBSD-src-5f4e854de19331a53788d6100bbcd42845056bc1.tar.gz |
sync with kame tree as of july00. tons of bug fixes/improvements.
API changes:
- additional IPv6 ioctls
- IPsec PF_KEY API was changed, it is mandatory to upgrade setkey(8).
(also syntax change)
Diffstat (limited to 'sys/netinet6/nd6.c')
-rw-r--r-- | sys/netinet6/nd6.c | 700 |
1 files changed, 579 insertions, 121 deletions
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 31e975c..0a56a91 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: nd6.c,v 1.68 2000/07/02 14:48:02 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -37,6 +38,9 @@ * I left the code mostly as it was in 970310. -- itojun */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> @@ -45,6 +49,7 @@ #include <sys/sockio.h> #include <sys/time.h> #include <sys/kernel.h> +#include <sys/protosw.h> #include <sys/errno.h> #include <sys/syslog.h> #include <sys/queue.h> @@ -59,21 +64,21 @@ #include <netinet/if_ether.h> #include <netinet/if_fddi.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include "loop.h" #include <net/net_osdep.h> -#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ -#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ +#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ +#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ -#define SIN6(s) ((struct sockaddr_in6 *)s) -#define SDL(s) ((struct sockaddr_dl *)s) +#define SIN6(s) ((struct sockaddr_in6 *)s) +#define SDL(s) ((struct sockaddr_dl *)s) /* timer values */ int nd6_prune = 1; /* walk list every 1 seconds */ @@ -81,20 +86,25 @@ int nd6_delay = 5; /* delay first probe time 5 second */ int nd6_umaxtries = 3; /* maximum unicast query */ int nd6_mmaxtries = 3; /* maximum multicast query */ int nd6_useloopback = 1; /* use loopback interface for local traffic */ -int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */ + +/* preventing too many loops in ND option parsing */ +int nd6_maxndopt = 10; /* max # of ND options allowed */ + +int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ /* for debugging? */ -static int nd6_inuse, nd6_allocated; +static int nd6_inuse, nd6_allocated; -struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; -struct nd_ifinfo *nd_ifinfo = NULL; -struct nd_drhead nd_defrouter = { 0 }; -struct nd_prhead nd_prefix = { 0 }; +struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; +static size_t nd_ifinfo_indexlim = 8; +struct nd_ifinfo *nd_ifinfo = NULL; +struct nd_drhead nd_defrouter; +struct nd_prhead nd_prefix = { 0 }; -int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; -static struct sockaddr_in6 all1_sa; +int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; +static struct sockaddr_in6 all1_sa; -static void nd6_slowtimo __P((void *)); +static void nd6_slowtimo __P((void *)); void nd6_init() @@ -112,6 +122,9 @@ nd6_init() for (i = 0; i < sizeof(all1_sa.sin6_addr); i++) all1_sa.sin6_addr.s6_addr[i] = 0xff; + /* initialization of the default router list */ + TAILQ_INIT(&nd_defrouter); + nd6_init_done = 1; /* start timer */ @@ -122,21 +135,20 @@ void nd6_ifattach(ifp) struct ifnet *ifp; { - static size_t if_indexlim = 8; /* * We have some arrays that should be indexed by if_index. * since if_index will grow dynamically, they should grow too. */ - if (nd_ifinfo == NULL || if_index >= if_indexlim) { + if (nd_ifinfo == NULL || if_index >= nd_ifinfo_indexlim) { size_t n; caddr_t q; - while (if_index >= if_indexlim) - if_indexlim <<= 1; + while (if_index >= nd_ifinfo_indexlim) + nd_ifinfo_indexlim <<= 1; /* grow nd_ifinfo */ - n = if_indexlim * sizeof(struct nd_ifinfo); + n = nd_ifinfo_indexlim * sizeof(struct nd_ifinfo); q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK); bzero(q, n); if (nd_ifinfo) { @@ -147,12 +159,18 @@ nd6_ifattach(ifp) } #define ND nd_ifinfo[ifp->if_index] + + /* don't initialize if called twice */ + if (ND.linkmtu) + return; + ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu; ND.chlim = IPV6_DEFHLIM; ND.basereachable = REACHABLE_TIME; ND.reachable = ND_COMPUTE_RTIME(ND.basereachable); ND.retrans = RETRANS_TIMER; ND.receivedra = 0; + ND.flags = ND6_IFF_PERFORMNUD; nd6_setmtu(ifp); #undef ND } @@ -330,14 +348,15 @@ nd6_options(ndopts) * Unknown options must be silently ignored, * to accomodate future extension to the protocol. */ - log(LOG_INFO, + log(LOG_DEBUG, "nd6_options: unsupported option %d - " "option ignored\n", nd_opt->nd_opt_type); } skip1: i++; - if (i > 10) { + if (i > nd6_maxndopt) { + icmp6stat.icp6s_nd_toomanyopt++; printf("too many loop in nd opt\n"); break; } @@ -371,6 +390,8 @@ nd6_timer(ignored_arg) struct ifnet *ifp; 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; @@ -380,6 +401,7 @@ nd6_timer(ignored_arg) ln = next; continue; } + ndi = &nd_ifinfo[ifp->if_index]; dst = (struct sockaddr_in6 *)rt_key(rt); if (ln->ln_expire > time_second) { @@ -390,6 +412,9 @@ nd6_timer(ignored_arg) /* 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); @@ -422,23 +447,26 @@ nd6_timer(ignored_arg) } break; case ND6_LLINFO_REACHABLE: - if (ln->ln_expire) { + if (ln->ln_expire) ln->ln_state = ND6_LLINFO_STALE; - } break; /* * ND6_LLINFO_STALE state requires nothing for timer * routine. */ case ND6_LLINFO_DELAY: - ln->ln_asked = 1; - ln->ln_state = ND6_LLINFO_PROBE; - ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].retrans / 1000; - nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, - ln, 0); + 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 */ break; - case ND6_LLINFO_PROBE: if (ln->ln_asked < nd6_umaxtries) { ln->ln_asked++; @@ -458,17 +486,18 @@ nd6_timer(ignored_arg) } /* expire */ - dr = LIST_FIRST(&nd_defrouter); + dr = TAILQ_FIRST(&nd_defrouter); while (dr) { if (dr->expire && dr->expire < time_second) { struct nd_defrouter *t; - t = LIST_NEXT(dr, dr_entry); + t = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); dr = t; - } else - dr = LIST_NEXT(dr, dr_entry); + } else { + dr = TAILQ_NEXT(dr, dr_entry); + } } - pr = LIST_FIRST(&nd_prefix); + pr = nd_prefix.lh_first; while (pr) { struct in6_ifaddr *ia6; struct in6_addrlifetime *lt6; @@ -503,7 +532,7 @@ nd6_timer(ignored_arg) if (pr->ndpr_expire && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { struct nd_prefix *t; - t = LIST_NEXT(pr, ndpr_entry); + t = pr->ndpr_next; /* * address expiration and prefix expiration are @@ -513,11 +542,104 @@ nd6_timer(ignored_arg) prelist_remove(pr); pr = t; } else - pr = LIST_NEXT(pr, ndpr_entry); + pr = pr->ndpr_next; } splx(s); } +/* + * 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, drany; + struct nd_prefix *pr, *npr; + + /* Nuke default router list entries toward ifp */ + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { + /* + * The first entry of the list may be stored in + * the routing table, so we'll delete it later. + */ + for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) { + ndr = TAILQ_NEXT(dr, dr_entry); + if (dr->ifp == ifp) + defrtrlist_del(dr); + } + dr = TAILQ_FIRST(&nd_defrouter); + 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) { + if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + prelist_remove(pr); + } + } + + /* cancel default outgoing interface setting */ + if (nd6_defifindex == ifp->if_index) + nd6_setdefaultiface(0); + + /* refresh default router list */ + bzero(&drany, sizeof(drany)); + defrouter_delreq(&drany, 0); + 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) + nd6_free(rt); + } + ln = nln; + } + + /* + * Neighbor cache entry for interface route will be retained + * with ND6_LLINFO_WAITDELETE state, by nd6_free(). Nuke it. + */ + 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) { + rtrequest(RTM_DELETE, rt_key(rt), + (struct sockaddr *)0, rt_mask(rt), 0, + (struct rtentry **)0); + } + } + ln = nln; + } +} + struct rtentry * nd6_lookup(addr6, create, ifp) struct in6_addr *addr6; @@ -531,6 +653,9 @@ nd6_lookup(addr6, create, ifp) sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr6; +#ifdef SCOPEDROUTING + sin6.sin6_scope_id = in6_addr2scopeid(ifp, addr6); +#endif rt = rtalloc1((struct sockaddr *)&sin6, create, 0UL); if (rt && (rt->rt_flags & RTF_LLINFO) == 0) { /* @@ -546,6 +671,8 @@ nd6_lookup(addr6, create, ifp) } if (!rt) { if (create && ifp) { + int e; + /* * If no route is available and create is set, * we allocate a host route for the destination @@ -564,15 +691,17 @@ nd6_lookup(addr6, create, ifp) * destination in nd6_rtrequest which will be * called in rtequest via ifa->ifa_rtrequest. */ - if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6, - ifa->ifa_addr, - (struct sockaddr *)&all1_sa, - (ifa->ifa_flags | - RTF_HOST | RTF_LLINFO) & ~RTF_CLONING, - &rt)) + if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6, + ifa->ifa_addr, + (struct sockaddr *)&all1_sa, + (ifa->ifa_flags | + RTF_HOST | RTF_LLINFO) & + ~RTF_CLONING, + &rt)) != 0) log(LOG_ERR, "nd6_lookup: failed to add route for a " - "neighbor(%s)\n", ip6_sprintf(addr6)); + "neighbor(%s), errno=%d\n", + ip6_sprintf(addr6), e); if (rt == NULL) return(NULL); if (rt->rt_llinfo) { @@ -610,7 +739,7 @@ nd6_lookup(addr6, create, ifp) */ int nd6_is_addr_neighbor(addr, ifp) - struct in6_addr *addr; + struct sockaddr_in6 *addr; struct ifnet *ifp; { register struct ifaddr *ifa; @@ -619,21 +748,29 @@ nd6_is_addr_neighbor(addr, ifp) #define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) #define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr) - /* A link-local address is always a neighbor. */ - if (IN6_IS_ADDR_LINKLOCAL(addr)) + /* + * A link-local address is always a neighbor. + * XXX: we should use the sin6_scope_id field rather than the embedded + * interface index. + */ + if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && + ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) return(1); /* * If the address matches one of our addresses, * it should be a neighbor. */ - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family != AF_INET6) next: continue; for (i = 0; i < 4; i++) { - if ((IFADDR6(ifa).s6_addr32[i] ^ addr->s6_addr32[i]) & + if ((IFADDR6(ifa).s6_addr32[i] ^ + addr->sin6_addr.s6_addr32[i]) & IFMASK6(ifa).s6_addr32[i]) goto next; } @@ -644,7 +781,7 @@ nd6_is_addr_neighbor(addr, ifp) * Even if the address matches none of our addresses, it might be * in the neighbor cache. */ - if (nd6_lookup(addr, 0, ifp)) + if (nd6_lookup(&addr->sin6_addr, 0, ifp)) return(1); return(0); @@ -661,40 +798,74 @@ nd6_free(rt) { struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; struct sockaddr_dl *sdl; + struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + struct nd_defrouter *dr; - if (ln->ln_router) { - /* remove from default router list */ - struct nd_defrouter *dr; - struct in6_addr *in6; - int s; - in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + /* + * Clear all destination cache entries for the neighbor. + * XXX: is it better to restrict this to hosts? + */ + pfctlinput(PRC_HOSTDEAD, rt_key(rt)); + if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ + int s; s = splnet(); - dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))-> - sin6_addr, + dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, rt->rt_ifp); - if (dr) - defrtrlist_del(dr); - else if (!ip6_forwarding && ip6_accept_rtadv) { + if (ln->ln_router || dr) { + /* + * rt6_flush must be called whether or not the neighbor + * is in the Default Router List. + * See a corresponding comment in nd6_na_input(). + */ + rt6_flush(&in6, rt->rt_ifp); + } + + if (dr) { /* - * rt6_flush must be called in any case. - * see the comment in nd6_na_input(). + * Unreachablity of a router might affect the default + * router selection and on-link detection of advertised + * prefixes. */ - rt6_flush(in6, rt->rt_ifp); + + /* + * Temporarily fake the state to choose a new default + * router and to perform on-link determination of + * prefixes coreectly. + * Below the state will be set correctly, + * or the entry itself will be deleted. + */ + ln->ln_state = ND6_LLINFO_INCOMPLETE; + + if (dr == TAILQ_FIRST(&nd_defrouter)) { + /* + * It is used as the current default router, + * so we have to move it to the end of the + * list and choose a new one. + * XXX: it is not very efficient if this is + * the only router. + */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry); + + defrouter_select(); + } + pfxlist_onlink_check(); } splx(s); } - + if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && - sdl->sdl_family == AF_LINK) { + sdl->sdl_family == AF_LINK) { sdl->sdl_alen = 0; ln->ln_state = ND6_LLINFO_WAITDELETE; ln->ln_asked = 0; rt->rt_flags &= ~RTF_REJECT; return; } - rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), - 0, (struct rtentry **)0); + + rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, + rt_mask(rt), 0, (struct rtentry **)0); } /* @@ -703,9 +874,10 @@ nd6_free(rt) * XXX cost-effective metods? */ void -nd6_nud_hint(rt, dst6) +nd6_nud_hint(rt, dst6, force) struct rtentry *rt; struct in6_addr *dst6; + int force; { struct llinfo_nd6 *ln; @@ -720,11 +892,10 @@ nd6_nud_hint(rt, dst6) return; } - if ((rt->rt_flags & RTF_GATEWAY) - || (rt->rt_flags & RTF_LLINFO) == 0 - || !rt->rt_llinfo - || !rt->rt_gateway - || rt->rt_gateway->sa_family != AF_LINK) { + if ((rt->rt_flags & RTF_GATEWAY) != 0 || + (rt->rt_flags & RTF_LLINFO) == 0 || + !rt->rt_llinfo || !rt->rt_gateway || + rt->rt_gateway->sa_family != AF_LINK) { /* This is not a host route. */ return; } @@ -733,12 +904,117 @@ nd6_nud_hint(rt, dst6) if (ln->ln_state < ND6_LLINFO_REACHABLE) return; + /* + * if we get upper-layer reachability confirmation many times, + * it is possible we have false information. + */ + if (!force) { + ln->ln_byhint++; + if (ln->ln_byhint > nd6_maxnudhint) + return; + } + ln->ln_state = ND6_LLINFO_REACHABLE; if (ln->ln_expire) ln->ln_expire = time_second + nd_ifinfo[rt->rt_ifp->if_index].reachable; } +#ifdef OLDIP6OUTPUT +/* + * Resolve an IP6 address into an ethernet address. If success, + * desten is filled in. If there is no entry in ndptab, + * set one up and multicast a solicitation for the IP6 address. + * Hold onto this mbuf and resend it once the address + * is finally resolved. A return value of 1 indicates + * that desten has been filled in and the packet should be sent + * normally; a 0 return indicates that the packet has been + * taken over here, either now or for later transmission. + */ +int +nd6_resolve(ifp, rt, m, dst, desten) + struct ifnet *ifp; + struct rtentry *rt; + struct mbuf *m; + struct sockaddr *dst; + u_char *desten; +{ + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; + struct sockaddr_dl *sdl; + + if (m->m_flags & M_MCAST) { + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, + desten); + return(1); + break; + case IFT_ARCNET: + *desten = 0; + return(1); + break; + default: + return(0); + } + } + if (rt && (rt->rt_flags & RTF_LLINFO) != 0) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + else { + if ((rt = nd6_lookup(&(SIN6(dst)->sin6_addr), 1, ifp)) != NULL) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + } + if (!ln || !rt) { + log(LOG_DEBUG, "nd6_resolve: can't allocate llinfo for %s\n", + ip6_sprintf(&(SIN6(dst)->sin6_addr))); + m_freem(m); + return(0); + } + sdl = SDL(rt->rt_gateway); + /* + * Ckeck the address family and length is valid, the address + * is resolved; otherwise, try to resolve. + */ + if (ln->ln_state >= ND6_LLINFO_REACHABLE + && sdl->sdl_family == AF_LINK + && sdl->sdl_alen != 0) { + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + if (ln->ln_state == ND6_LLINFO_STALE) { + ln->ln_asked = 0; + ln->ln_state = ND6_LLINFO_DELAY; + ln->ln_expire = time_second + nd6_delay; + } + return(1); + } + /* + * There is an ndp entry, but no ethernet address + * response yet. Replace the held mbuf with this + * latest one. + * + * XXX Does the code conform to rate-limiting rule? + * (RFC 2461 7.2.2) + */ + if (ln->ln_state == ND6_LLINFO_WAITDELETE || + 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) { + rt->rt_flags &= ~RTF_REJECT; + 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, &(SIN6(dst)->sin6_addr), + ln, 0); + } + } + return(0); +} +#endif /* OLDIP6OUTPUT */ + void nd6_rtrequest(req, rt, sa) int req; @@ -763,7 +1039,7 @@ nd6_rtrequest(req, rt, sa) * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) * rt->rt_flags |= RTF_CLONING; */ - if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) { + if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) { /* * Case 1: This route should come from * a route to interface. RTF_LLINFO flag is set @@ -777,30 +1053,63 @@ nd6_rtrequest(req, rt, sa) SDL(gate)->sdl_index = ifp->if_index; if (ln) ln->ln_expire = time_second; +#if 1 if (ln && ln->ln_expire == 0) { /* cludge for desktops */ +#if 0 + printf("nd6_request: time.tv_sec is zero; " + "treat it as 1\n"); +#endif ln->ln_expire = 1; } +#endif if (rt->rt_flags & RTF_CLONING) break; } - /* Announce a new entry if requested. */ + /* + * In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here. + * We don't do that here since llinfo is not ready yet. + * + * There are also couple of other things to be discussed: + * - unsolicited NA code needs improvement beforehand + * - RFC2461 says we MAY send multicast unsolicited NA + * (7.2.6 paragraph 4), however, it also says that we + * SHOULD provide a mechanism to prevent multicast NA storm. + * we don't have anything like it right now. + * note that the mechanism need a mutual agreement + * between proxies, which means that we need to implement + * a new protocol, or new kludge. + * - from RFC2461 6.2.4, host MUST NOT send unsolicited NA. + * we need to check ip6forwarding before sending it. + * (or should we allow proxy ND configuration only for + * routers? there's no mention about proxy ND from hosts) + */ +#if 0 + /* XXX it does not work */ if (rt->rt_flags & RTF_ANNOUNCE) nd6_na_output(ifp, - &SIN6(rt_key(rt))->sin6_addr, - &SIN6(rt_key(rt))->sin6_addr, - ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + &SIN6(rt_key(rt))->sin6_addr, + &SIN6(rt_key(rt))->sin6_addr, + ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, + 1, NULL); +#endif /* FALLTHROUGH */ case RTM_RESOLVE: - if (gate->sa_family != AF_LINK || - gate->sa_len < sizeof(null_sdl)) { - log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n"); - break; + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + /* + * Address resolution isn't necessary for a point to + * point link, so we can skip this test for a p2p link. + */ + if (gate->sa_family != AF_LINK || + gate->sa_len < sizeof(null_sdl)) { + log(LOG_DEBUG, + "nd6_rtrequest: bad gateway value\n"); + break; + } + SDL(gate)->sdl_type = ifp->if_type; + SDL(gate)->sdl_index = ifp->if_index; } - SDL(gate)->sdl_type = ifp->if_type; - SDL(gate)->sdl_index = ifp->if_index; - if (ln != 0) + if (ln != NULL) break; /* This happens on a route change */ /* * Case 2: This route may come from cloning, or a manual route @@ -824,12 +1133,13 @@ nd6_rtrequest(req, rt, sa) * which is specified by ndp command. */ ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; } else { /* * When req == RTM_RESOLVE, rt is created and * initialized in rtrequest(), so rt_expire is 0. */ - ln->ln_state = ND6_LLINFO_INCOMPLETE; + ln->ln_state = ND6_LLINFO_NOSTATE; ln->ln_expire = time_second; } rt->rt_flags |= RTF_LLINFO; @@ -848,6 +1158,7 @@ nd6_rtrequest(req, rt, sa) caddr_t macp = nd6_ifptomac(ifp); ln->ln_expire = 0; ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (macp) { Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen); SDL(gate)->sdl_alen = ifp->if_addrlen; @@ -863,17 +1174,56 @@ nd6_rtrequest(req, rt, sa) * of the loopback address. */ if (ifa != rt->rt_ifa) { - rt->rt_ifa->ifa_refcnt--; + IFAFREE(rt->rt_ifa); ifa->ifa_refcnt++; rt->rt_ifa = ifa; } } + } else if (rt->rt_flags & RTF_ANNOUNCE) { + ln->ln_expire = 0; + ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; + + /* join solicited node multicast for proxy ND */ + if (ifp->if_flags & IFF_MULTICAST) { + struct in6_addr llsol; + int error; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + (void)in6_addmulti(&llsol, ifp, &error); + if (error) + printf( +"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error); + } } break; case RTM_DELETE: if (!ln) break; + /* leave from solicited node multicast for proxy ND */ + if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && + (ifp->if_flags & IFF_MULTICAST) != 0) { + struct in6_addr llsol; + struct in6_multi *in6m; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } nd6_inuse--; ln->ln_next->ln_prev = ln->ln_prev; ln->ln_prev->ln_next = ln->ln_next; @@ -927,7 +1277,7 @@ nd6_p2p_rtrequest(req, rt, sa) &SIN6(rt_key(rt))->sin6_addr, &SIN6(rt_key(rt))->sin6_addr, ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + 1, NULL); /* FALLTHROUGH */ case RTM_RESOLVE: /* @@ -955,6 +1305,7 @@ nd6_ioctl(cmd, data, ifp) struct in6_prlist *prl = (struct in6_prlist *)data; struct in6_ndireq *ndi = (struct in6_ndireq *)data; struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; + struct in6_ndifreq *ndif = (struct in6_ndifreq *)data; struct nd_defrouter *dr, any; struct nd_prefix *pr; struct rtentry *rt; @@ -965,7 +1316,7 @@ nd6_ioctl(cmd, data, ifp) case SIOCGDRLST_IN6: bzero(drl, sizeof(*drl)); s = splnet(); - dr = LIST_FIRST(&nd_defrouter); + dr = TAILQ_FIRST(&nd_defrouter); while (dr && i < DRLSTSIZ) { drl->defrouter[i].rtaddr = dr->rtaddr; if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) { @@ -982,14 +1333,19 @@ nd6_ioctl(cmd, data, ifp) drl->defrouter[i].expire = dr->expire; drl->defrouter[i].if_index = dr->ifp->if_index; i++; - dr = LIST_NEXT(dr, dr_entry); + dr = TAILQ_NEXT(dr, dr_entry); } splx(s); break; case SIOCGPRLST_IN6: + /* + * XXX meaning of fields, especialy "raflags", is very + * differnet between RA prefix list and RR/static prefix list. + * how about separating ioctls into two? + */ bzero(prl, sizeof(*prl)); s = splnet(); - pr = LIST_FIRST(&nd_prefix); + pr = nd_prefix.lh_first; while (pr && i < PRLSTSIZ) { struct nd_pfxrouter *pfr; int j; @@ -1002,7 +1358,7 @@ nd6_ioctl(cmd, data, ifp) prl->prefix[i].if_index = pr->ndpr_ifp->if_index; prl->prefix[i].expire = pr->ndpr_expire; - pfr = LIST_FIRST(&pr->ndpr_advrtrs); + pfr = pr->ndpr_advrtrs.lh_first; j = 0; while(pfr) { if (j < DRLSTSIZ) { @@ -1020,14 +1376,14 @@ nd6_ioctl(cmd, data, ifp) #undef RTRADDR } j++; - pfr = LIST_NEXT(pfr, pfr_entry); + pfr = pfr->pfr_next; } prl->prefix[i].advrtrs = j; + prl->prefix[i].origin = PR_ORIG_RA; i++; - pr = LIST_NEXT(pr, ndpr_entry); + pr = pr->ndpr_next; } - splx(s); { struct rr_prefix *rpp; @@ -1043,15 +1399,29 @@ nd6_ioctl(cmd, data, ifp) prl->prefix[i].if_index = rpp->rp_ifp->if_index; prl->prefix[i].expire = rpp->rp_expire; prl->prefix[i].advrtrs = 0; + prl->prefix[i].origin = rpp->rp_origin; i++; } } + splx(s); break; case SIOCGIFINFO_IN6: + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } ndi->ndi = nd_ifinfo[ifp->if_index]; break; - case SIOCSNDFLUSH_IN6: + case SIOCSIFINFO_FLAGS: + /* XXX: almost all other fields of ndi->ndi is unused */ + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } + nd_ifinfo[ifp->if_index].flags = ndi->ndi.flags; + break; + case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ /* flush default router list */ /* * xxx sumikawa: should not delete route if default @@ -1059,6 +1429,7 @@ nd6_ioctl(cmd, data, ifp) */ bzero(&any, sizeof(any)); defrouter_delreq(&any, 0); + defrouter_select(); /* xxx sumikawa: flush prefix list */ break; case SIOCSPFXFLUSH_IN6: @@ -1067,8 +1438,8 @@ nd6_ioctl(cmd, data, ifp) struct nd_prefix *pr, *next; s = splnet(); - for (pr = LIST_FIRST(&nd_prefix); pr; pr = next) { - next = LIST_NEXT(pr, ndpr_entry); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); prelist_remove(pr); @@ -1082,16 +1453,16 @@ nd6_ioctl(cmd, data, ifp) struct nd_defrouter *dr, *next; s = splnet(); - if ((dr = LIST_FIRST(&nd_defrouter)) != NULL) { + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { /* * The first entry of the list may be stored in * the routing table, so we'll delete it later. */ - for (dr = LIST_NEXT(dr, dr_entry); dr; dr = next) { - next = LIST_NEXT(dr, dr_entry); + for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) { + next = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); } - defrtrlist_del(LIST_FIRST(&nd_defrouter)); + defrtrlist_del(TAILQ_FIRST(&nd_defrouter)); } splx(s); break; @@ -1116,6 +1487,7 @@ nd6_ioctl(cmd, data, ifp) s = splnet(); if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) { error = EINVAL; + splx(s); break; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; @@ -1127,6 +1499,12 @@ nd6_ioctl(cmd, data, ifp) break; } + case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ + ndif->ifindex = nd6_defifindex; + break; + case SIOCSDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ + return(nd6_setdefaultiface(ndif->ifindex)); + break; } return(error); } @@ -1174,6 +1552,12 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) rt = nd6_lookup(from, 0, ifp); if (!rt) { +#if 0 + /* nothing must be done if there's no lladdr */ + if (!lladdr || !lladdrlen) + return NULL; +#endif + rt = nd6_lookup(from, 1, ifp); is_newentry = 1; } else @@ -1248,9 +1632,18 @@ fail: if (ln->ln_state == ND6_LLINFO_STALE) { rt->rt_flags &= ~RTF_REJECT; if (ln->ln_hold) { - nd6_output(ifp, ln->ln_hold, +#ifdef OLDIP6OUTPUT + (*ifp->if_output)(ifp, ln->ln_hold, + rt_key(rt), rt); +#else + /* + * 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); +#endif ln->ln_hold = 0; } } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { @@ -1301,7 +1694,6 @@ fail: * 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; @@ -1338,6 +1730,8 @@ nd6_slowtimo(ignored_arg) timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); 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) { @@ -1356,14 +1750,16 @@ nd6_slowtimo(ignored_arg) #define senderr(e) { error = (e); goto bad;} int -nd6_output(ifp, m0, dst, rt0) +nd6_output(ifp, origifp, m0, dst, rt0) register struct ifnet *ifp; + struct ifnet *origifp; struct mbuf *m0; struct sockaddr_in6 *dst; struct rtentry *rt0; { register struct mbuf *m = m0; register struct rtentry *rt = rt0; + struct sockaddr_in6 *gw6 = NULL; struct llinfo_nd6 *ln = NULL; int error = 0; @@ -1372,12 +1768,16 @@ nd6_output(ifp, m0, dst, rt0) /* * XXX: we currently do not make neighbor cache on any interface - * other than ARCnet, Ethernet and FDDI. + * other than ARCnet, Ethernet, FDDI and GIF. + * + * draft-ietf-ngtrans-mech-06.txt says: + * - unidirectional tunnels needs no ND */ switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: + case IFT_GIF: /* XXX need more cases? */ break; default: goto sendpkt; @@ -1392,12 +1792,43 @@ nd6_output(ifp, m0, dst, rt0) NULL) { rt->rt_refcnt--; - if (rt->rt_ifp != ifp) - return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */ + 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 the value 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)) { + if (rt->rt_flags & RTF_REJECT) + senderr(EHOSTDOWN); + + /* + * 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) { @@ -1422,16 +1853,32 @@ nd6_output(ifp, m0, dst, rt0) if (rt && (rt->rt_flags & RTF_LLINFO) != 0) ln = (struct llinfo_nd6 *)rt->rt_llinfo; else { - if ((rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL) + /* + * 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) { - 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? */ + 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; /* * The first time we send a packet to a neighbor whose entry is @@ -1481,8 +1928,15 @@ nd6_output(ifp, m0, dst, rt0) return(0); sendpkt: + +#ifdef FAKE_LOOPBACK_IF + if (ifp->if_flags & IFF_LOOPBACK) { + return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, + rt)); + } +#endif return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)); - + bad: if (m) m_freem(m); @@ -1522,8 +1976,12 @@ nd6_storelladdr(ifp, rt, m, dst, desten) return(0); } sdl = SDL(rt->rt_gateway); - if (sdl->sdl_alen != 0) - bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + if (sdl->sdl_alen == 0) { + /* this should be impossible, but we bark here for debugging */ + printf("nd6_storelladdr: sdl_alen == 0\n"); + return(0); + } + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return(1); } |