📄 nd6_nbr.c
字号:
else {
ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
if (ia == NULL) {
m_freem(m);
return;
}
ip6->ip6_src = ia->ia_addr.sin6_addr;
}
} else {
/*
* Source address for DAD packet must always be IPv6
* unspecified address. (0::0)
*/
bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
}
nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
nd_ns->nd_ns_code = 0;
nd_ns->nd_ns_reserved = 0;
nd_ns->nd_ns_target = *taddr6;
if (IN6_IS_SCOPE_LINKLOCAL(&nd_ns->nd_ns_target))
nd_ns->nd_ns_target.s6_addr16[1] = 0;
/*
* Add source link-layer address option.
*
* spec implementation
* --- ---
* DAD packet MUST NOT do not add the option
* there's no link layer address:
* impossible do not add the option
* there's link layer address:
* Multicast NS MUST add one add the option
* Unicast NS SHOULD add one add the option
*/
if (!dad && (mac = nd6_ifptomac(ifp))) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
/* 8 byte alignments... */
optlen = (optlen + 7) & ~7;
m->m_pkthdr.len += optlen;
m->m_len += optlen;
icmp6len += optlen;
bzero((caddr_t)nd_opt, optlen);
nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
nd_opt->nd_opt_len = optlen >> 3;
bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
}
ip6->ip6_plen = htons((u_short)icmp6len);
nd_ns->nd_ns_cksum = 0;
nd_ns->nd_ns_cksum
= in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
#ifdef IPSEC
/* Don't lookup socket */
(void)ipsec_setsocket(m, NULL);
#endif
ip6_output(m, NULL, NULL, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL);
icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
return;
bad:
m_freem(m);
return;
}
/*
* Neighbor advertisement input handling.
*
* Based on RFC 2461
* Based on RFC 2462 (duplicated address detection)
*
* the following items are not implemented yet:
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/
void
nd6_na_input(m, off, icmp6len)
struct mbuf *m;
int off, icmp6len;
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_neighbor_advert *nd_na;
#if 0
struct in6_addr saddr6 = ip6->ip6_src;
#endif
struct in6_addr daddr6 = ip6->ip6_dst;
struct in6_addr taddr6;
int flags;
int is_router;
int is_solicited;
int is_override;
char *lladdr = NULL;
int lladdrlen = 0;
struct ifaddr *ifa;
struct llinfo_nd6 *ln;
struct rtentry *rt;
struct sockaddr_dl *sdl;
union nd_opts ndopts;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
"nd6_na_input: invalid hlim (%d) from %s to %s on %s\n",
ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
goto bad;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);
if (nd_na == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
taddr6 = nd_na->nd_na_target;
flags = nd_na->nd_na_flags_reserved;
is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
taddr6.s6_addr16[1] = htons(ifp->if_index);
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
nd6log((LOG_ERR,
"nd6_na_input: invalid target address %s\n",
ip6_sprintf(&taddr6)));
goto bad;
}
if (IN6_IS_ADDR_MULTICAST(&daddr6))
if (is_solicited) {
nd6log((LOG_ERR,
"nd6_na_input: a solicited adv is multicasted\n"));
goto bad;
}
icmp6len -= sizeof(*nd_na);
nd6_option_init(nd_na + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
nd6log((LOG_INFO,
"nd6_na_input: invalid ND option, ignored\n"));
/* nd6_options have incremented stats */
goto freeit;
}
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
}
ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
#ifdef MIP6
if (!ifa) {
ifa = mip6_dad_find(&taddr6, ifp);
}
#endif
/*
* Target address matches one of my interface address.
*
* If my address is tentative, this means that there's somebody
* already using the same address as mine. This indicates DAD failure.
* This is defined in RFC 2462.
*
* Otherwise, process as defined in RFC 2461.
*/
if (ifa
&& (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
nd6_dad_na_input(ifa);
goto freeit;
}
/* Just for safety, maybe unnecessary. */
if (ifa) {
log(LOG_ERR,
"nd6_na_input: duplicate IP6 address %s\n",
ip6_sprintf(&taddr6));
goto freeit;
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
nd6log((LOG_INFO,
"nd6_na_input: lladdrlen mismatch for %s "
"(if %d, NA packet %d)\n",
ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
/*
* If no neighbor cache entry is found, NA SHOULD silently be discarded.
*/
rt = nd6_lookup(&taddr6, 0, ifp);
if ((rt == NULL) ||
((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
((sdl = SDL(rt->rt_gateway)) == NULL))
goto freeit;
if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/*
* If the link-layer has address, and no lladdr option came,
* discard the packet.
*/
if (ifp->if_addrlen && !lladdr)
goto freeit;
/*
* Record link-layer address, and update the state.
*/
sdl->sdl_alen = ifp->if_addrlen;
bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (ln->ln_expire)
ln->ln_expire = time_second +
nd_ifinfo[rt->rt_ifp->if_index].reachable;
} else {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer;
}
if ((ln->ln_router = is_router) != 0) {
/*
* This means a router's state has changed from
* non-reachable to probably reachable, and might
* affect the status of associated prefixes..
*/
pfxlist_onlink_check();
}
} else {
int llchange;
/*
* Check if the link-layer address has changed or not.
*/
if (!lladdr)
llchange = 0;
else {
if (sdl->sdl_alen) {
if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
llchange = 1;
else
llchange = 0;
} else
llchange = 1;
}
/*
* This is VERY complex. Look at it with care.
*
* override solicit lladdr llchange action
* (L: record lladdr)
*
* 0 0 n -- (2c)
* 0 0 y n (2b) L
* 0 0 y y (1) REACHABLE->STALE
* 0 1 n -- (2c) *->REACHABLE
* 0 1 y n (2b) L *->REACHABLE
* 0 1 y y (1) REACHABLE->STALE
* 1 0 n -- (2a)
* 1 0 y n (2a) L
* 1 0 y y (2a) L *->STALE
* 1 1 n -- (2a) *->REACHABLE
* 1 1 y n (2a) L *->REACHABLE
* 1 1 y y (2a) L *->REACHABLE
*/
if (!is_override && (lladdr && llchange)) { /* (1) */
/*
* If state is REACHABLE, make it STALE.
* no other updates should be done.
*/
if (ln->ln_state == ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer;
}
goto freeit;
} else if (is_override /* (2a) */
|| (!is_override && (lladdr && !llchange)) /* (2b) */
|| !lladdr) { /* (2c) */
/*
* Update link-local address, if any.
*/
if (lladdr) {
sdl->sdl_alen = ifp->if_addrlen;
bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
}
/*
* If solicited, make the state REACHABLE.
* If not solicited and the link-layer address was
* changed, make it STALE.
*/
if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (ln->ln_expire) {
ln->ln_expire = time_second +
nd_ifinfo[ifp->if_index].reachable;
}
} else {
if (lladdr && llchange) {
ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer;
}
}
}
if (ln->ln_router && !is_router) {
/*
* The peer dropped the router flag.
* Remove the sender from the Default Router List and
* update the Destination Cache entries.
*/
struct nd_defrouter *dr;
struct in6_addr *in6;
int s;
in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
/*
* Lock to protect the default router list.
* XXX: this might be unnecessary, since this function
* is only called under the network software interrupt
* context. However, we keep it just for safety.
*/
#ifdef __NetBSD__
s = splsoftnet();
#else
s = splnet();
#endif
dr = defrouter_lookup(in6, rt->rt_ifp);
if (dr)
defrtrlist_del(dr);
else if (!ip6_forwarding && ip6_accept_rtadv) {
/*
* Even if the neighbor is not in the default
* router list, the neighbor may be used
* as a next hop for some destinations
* (e.g. redirect case). So we must
* call rt6_flush explicitly.
*/
rt6_flush(&ip6->ip6_src, rt->rt_ifp);
}
splx(s);
}
ln->ln_router = is_router;
}
rt->rt_flags &= ~RTF_REJECT;
ln->ln_asked = 0;
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 = 0;
}
freeit:
m_freem(m);
return;
bad:
icmp6stat.icp6s_badna++;
m_freem(m);
}
/*
* Neighbor advertisement output handling.
*
* Based on RFC 2461
*
* the following items are not implemented yet:
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/
void
nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
struct ifnet *ifp;
const struct in6_addr *daddr6, *taddr6;
u_long flags;
int tlladdr; /* 1 if include target link-layer address */
struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */
{
struct mbuf *m;
struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na;
struct in6_ifaddr *ia = NULL;
struct ip6_moptions im6o;
int icmp6len;
int maxlen;
caddr_t mac = NULL;
/* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_na);
maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
#ifdef DIAGNOSTIC
if (max_linkhdr + maxlen >= MCLBYTES) {
printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES "
"(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
panic("nd6_na_output: insufficient MCLBYTES");
/* NOTREACHED */
}
#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && max_linkhdr + maxlen >= MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
m = NULL;
}
}
if (m == NULL)
return;
m->m_pkthdr.rcvif = NULL;
if (IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
im6o.im6o_multicast_ifp = ifp;
im6o.im6o_multicast_hlim = 255;
im6o.im6o_multicast_loop = 0;
}
icmp6len = sizeof(*nd_na);
m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */
/* fill neighbor advertisement packet */
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
/* reply to DAD */
ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
ip6->ip6_dst.s6_addr32[1] = 0;
ip6->ip6_dst.s6_addr32[2] = 0;
ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
flags &= ~ND_NA_FLAG_SOLICITED;
} else
ip6->ip6_dst = *daddr6;
/*
* Select a source whose scope is the same as that of the dest.
*/
ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
if (ia == NULL) {
m_freem(m);
return;
}
ip6->ip6_src = ia->ia_addr.sin6_addr;
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
nd_na->nd_na_target = *taddr6;
if (IN6_IS_SCOPE_LINKLOCAL(&nd_na->nd_na_target))
nd_na->nd_na_target.s6_addr16[1] = 0;
/*
* "tlladdr" indicates NS's condition for adding tlladdr or not.
* see nd6_ns_input() for details.
* Basically, if NS packet is sent to unicast/anycast addr,
* target lladdr option SHOULD NOT be included.
*/
if (tlladdr) {
mac = NULL;
/*
* sdl0 != NULL indicates proxy NA. If we do proxy, use
* lladdr in sdl0. If we are not proxying (sending NA for
* my address) use lladdr configured for the interface.
*/
if (sdl0 == NULL)
mac = nd6_ifptomac(ifp);
else if (sdl0->sa_family == AF_LINK) {
struct sockaddr_dl *sdl;
sdl = (struct sockaddr_dl *)sdl0;
if (sdl->sdl_alen == ifp->if_addrlen)
mac = LLADDR(sdl);
}
}
if (tlladdr && mac) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
/* roundup to 8 bytes alignment! */
optlen = (optlen + 7) & ~7;
m->m_pkthdr.len += optlen;
m->m_len += optlen;
icmp6len += optlen;
bzero((caddr_t)nd_opt, optlen);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -