📄 nd6.c
字号:
*/ 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 voidnd6_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;}intnd6_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 senderrintnd6_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); }}intnd6_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_SYSCTLstatic int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS);static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS);#endif#ifdef SYSCTL_DECLSYSCTL_DECL(_net_inet6_icmp6);#endifSYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, CTLFLAG_RD, nd6_sysctl_drlist, "");SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist, CTLFLAG_RD, nd6_sysctl_prlist, "");#ifdef CYGPKG_NET_FREEBSD_SYSCTLstatic intnd6_sysctl_drlist(SYSCTL_HANDLER_ARGS){ int error; char buf[1024]; struct in6_defrouter *d, *de; struct nd_defrouter *dr; if (req->newptr) return EPERM; error = 0; for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = TAILQ_NEXT(dr, dr_entry)) { d = (struct in6_defrouter *)buf; de = (struct in6_defrouter *)(buf + sizeof(buf)); if (d + 1 <= de) { bzero(d, sizeof(*d)); d->rtaddr.sin6_family = AF_INET6; d->rtaddr.sin6_len = sizeof(d->rtaddr); if (in6_recoverscope(&d->rtaddr, &dr->rtaddr, dr->ifp) != 0) log(LOG_ERR, "scope error in " "default router list (%s)\n", ip6_sprintf(&dr->rtaddr)); d->flags = dr->flags; d->rtlifetime = dr->rtlifetime; d->expire = dr->expire; d->if_index = dr->ifp->if_index; } else panic("buffer too short"); error = SYSCTL_OUT(req, buf, sizeof(*d)); if (error) break; } return error;}static intnd6_sysctl_prlist(SYSCTL_HANDLER_ARGS){ int error; char buf[1024]; struct in6_prefix *p, *pe; struct nd_prefix *pr; if (req->newptr) return EPERM; error = 0; for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { u_short advrtrs=0; size_t advance; struct sockaddr_in6 *sin6, *s6; struct nd_pfxrouter *pfr; p = (struct in6_prefix *)buf; pe = (struct in6_prefix *)(buf + sizeof(buf)); if (p + 1 <= pe) { bzero(p, sizeof(*p)); sin6 = (struct sockaddr_in6 *)(p + 1); p->prefix = pr->ndpr_prefix; if (in6_recoverscope(&p->prefix, &p->prefix.sin6_addr, pr->ndpr_ifp) != 0) log(LOG_ERR, "scope error in prefix list (%s)\n", ip6_sprintf(&p->prefix.sin6_addr)); p->raflags = pr->ndpr_raf; p->prefixlen = pr->ndpr_plen; p->vltime = pr->ndpr_vltime; p->pltime = pr->ndpr_pltime; p->if_index = pr->ndpr_ifp->if_index; p->expire = pr->ndpr_expire; p->refcnt = pr->ndpr_refcnt; p->flags = pr->ndpr_stateflags; p->origin = PR_ORIG_RA; advrtrs = 0; for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = pfr->pfr_next) { if ((void *)&sin6[advrtrs + 1] >
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -