📄 icmp6.c
字号:
}
nd6_rs_input(n, off, icmp6len);
/* m stays. */
break;
case ND_ROUTER_ADVERT:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_router_advert))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_ra_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_ra_input(n, off, icmp6len);
/* m stays. */
break;
case ND_NEIGHBOR_SOLICIT:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_neighbor_solicit))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_ns_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_ns_input(n, off, icmp6len);
/* m stays. */
break;
case ND_NEIGHBOR_ADVERT:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_neighbor_advert))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_na_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_na_input(n, off, icmp6len);
/* m stays. */
break;
case ND_REDIRECT:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_redirect))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
icmp6_redirect_input(m, off);
m = NULL;
goto freeit;
}
icmp6_redirect_input(n, off);
/* m stays. */
break;
case ICMP6_ROUTER_RENUMBERING:
if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
code != ICMP6_ROUTER_RENUMBERING_RESULT)
goto badcode;
if (icmp6len < sizeof(struct icmp6_router_renum))
goto badlen;
break;
default:
nd6log((LOG_DEBUG,
"icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst),
m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0));
if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
/* ICMPv6 error: MUST deliver it by spec... */
code = PRC_NCMDS;
/* deliver */
} else {
/* ICMPv6 informational: MUST not deliver */
break;
}
deliver:
if (icmp6_notify_error(m, off, icmp6len, code)) {
/* In this case, m should've been freed. */
return(IPPROTO_DONE);
}
break;
badcode:
icmp6stat.icp6s_badcode++;
break;
badlen:
icmp6stat.icp6s_badlen++;
break;
}
/* deliver the packet to appropriate sockets */
icmp6_rip6_input(&m, *offp);
return IPPROTO_DONE;
freeit:
m_freem(m);
return IPPROTO_DONE;
}
static int
icmp6_notify_error(m, off, icmp6len, code)
struct mbuf *m;
int off, icmp6len;
{
struct icmp6_hdr *icmp6;
struct ip6_hdr *eip6;
u_int32_t notifymtu;
int64_t zoneid;
struct sockaddr_in6 icmp6src, icmp6dst;
if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
icmp6stat.icp6s_tooshort++;
goto freeit;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off,
sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
-1);
icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
#else
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (icmp6 == NULL) {
icmp6stat.icp6s_tooshort++;
return(-1);
}
#endif
eip6 = (struct ip6_hdr *)(icmp6 + 1);
/* Detect the upper level protocol */
{
void (*ctlfunc) __P((int, struct sockaddr *, void *));
u_int8_t nxt = eip6->ip6_nxt;
int eoff = off + sizeof(struct icmp6_hdr) +
sizeof(struct ip6_hdr);
struct ip6ctlparam ip6cp;
struct in6_addr *finaldst = NULL;
int icmp6type = icmp6->icmp6_type;
struct ip6_frag *fh;
struct ip6_rthdr *rth;
struct ip6_rthdr0 *rth0;
int rthlen;
while (1) { /* XXX: should avoid infinite loop explicitly? */
struct ip6_ext *eh;
switch (nxt) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
case IPPROTO_AH:
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff +
sizeof(struct ip6_ext),
-1);
eh = (struct ip6_ext *)(mtod(m, caddr_t)
+ eoff);
#else
IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
eoff, sizeof(*eh));
if (eh == NULL) {
icmp6stat.icp6s_tooshort++;
return(-1);
}
#endif
if (nxt == IPPROTO_AH)
eoff += (eh->ip6e_len + 2) << 2;
else
eoff += (eh->ip6e_len + 1) << 3;
nxt = eh->ip6e_nxt;
break;
case IPPROTO_ROUTING:
/*
* When the erroneous packet contains a
* routing header, we should examine the
* header to determine the final destination.
* Otherwise, we can't properly update
* information that depends on the final
* destination (e.g. path MTU).
*/
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth),
-1);
rth = (struct ip6_rthdr *)(mtod(m, caddr_t)
+ eoff);
#else
IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
eoff, sizeof(*rth));
if (rth == NULL) {
icmp6stat.icp6s_tooshort++;
return(-1);
}
#endif
rthlen = (rth->ip6r_len + 1) << 3;
/*
* XXX: currently there is no
* officially defined type other
* than type-0.
* Note that if the segment left field
* is 0, all intermediate hops must
* have been passed.
*/
if (rth->ip6r_segleft &&
rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
int hops;
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff + rthlen,
-1);
rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff);
#else
IP6_EXTHDR_GET(rth0,
struct ip6_rthdr0 *, m,
eoff, rthlen);
if (rth0 == NULL) {
icmp6stat.icp6s_tooshort++;
return(-1);
}
#endif
/* just ignore a bogus header */
if ((rth0->ip6r0_len % 2) == 0 &&
(hops = rth0->ip6r0_len/2))
finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
}
eoff += rthlen;
nxt = rth->ip6r_nxt;
break;
case IPPROTO_FRAGMENT:
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, 0, eoff +
sizeof(struct ip6_frag),
-1);
fh = (struct ip6_frag *)(mtod(m, caddr_t)
+ eoff);
#else
IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
eoff, sizeof(*fh));
if (fh == NULL) {
icmp6stat.icp6s_tooshort++;
return(-1);
}
#endif
/*
* Data after a fragment header is meaningless
* unless it is the first fragment, but
* we'll go to the notify label for path MTU
* discovery.
*/
if (fh->ip6f_offlg & IP6F_OFF_MASK)
goto notify;
eoff += sizeof(struct ip6_frag);
nxt = fh->ip6f_nxt;
break;
default:
/*
* This case includes ESP and the No Next
* Header. In such cases going to the notify
* label does not have any meaning
* (i.e. ctlfunc will be NULL), but we go
* anyway since we might have to update
* path MTU information.
*/
goto notify;
}
}
notify:
#ifndef PULLDOWN_TEST
icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
#else
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (icmp6 == NULL) {
icmp6stat.icp6s_tooshort++;
return(-1);
}
#endif
eip6 = (struct ip6_hdr *)(icmp6 + 1);
bzero(&icmp6dst, sizeof(icmp6dst));
icmp6dst.sin6_len = sizeof(struct sockaddr_in6);
icmp6dst.sin6_family = AF_INET6;
if (finaldst == NULL)
icmp6dst.sin6_addr = eip6->ip6_dst;
else
icmp6dst.sin6_addr = *finaldst;
if ((zoneid = in6_addr2zoneid(m->m_pkthdr.rcvif,
&icmp6dst.sin6_addr)) < 0)
goto freeit;
icmp6dst.sin6_scope_id = zoneid;
if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst)) {
/* should be impossbile */
nd6log((LOG_DEBUG,
"icmp6_notify_error: in6_embedscope failed\n"));
goto freeit;
}
/*
* retrieve parameters from the inner IPv6 header, and convert
* them into sockaddr structures.
*/
bzero(&icmp6src, sizeof(icmp6src));
icmp6src.sin6_len = sizeof(struct sockaddr_in6);
icmp6src.sin6_family = AF_INET6;
icmp6src.sin6_addr = eip6->ip6_src;
if ((zoneid = in6_addr2zoneid(m->m_pkthdr.rcvif,
&icmp6src.sin6_addr)) < 0)
goto freeit;
icmp6src.sin6_scope_id = zoneid;
if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src)) {
/* should be impossbile */
nd6log((LOG_DEBUG,
"icmp6_notify_error: in6_embedscope failed\n"));
goto freeit;
}
icmp6src.sin6_flowinfo =
(eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
if (finaldst == NULL)
finaldst = &eip6->ip6_dst;
ip6cp.ip6c_m = m;
ip6cp.ip6c_icmp6 = icmp6;
ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
ip6cp.ip6c_off = eoff;
ip6cp.ip6c_finaldst = finaldst;
ip6cp.ip6c_src = &icmp6src;
ip6cp.ip6c_nxt = nxt;
if (icmp6type == ICMP6_PACKET_TOO_BIG) {
notifymtu = ntohl(icmp6->icmp6_mtu);
ip6cp.ip6c_cmdarg = (void *)¬ifymtu;
#if !(defined(__NetBSD__) || defined(__OpenBSD__))
icmp6_mtudisc_update(&ip6cp, 1); /* XXX */
#endif
}
ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
(inet6sw[ip6_protox[nxt]].pr_ctlinput);
if (ctlfunc) {
(void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
&ip6cp);
}
}
return(0);
freeit:
m_freem(m);
return(-1);
}
void
icmp6_mtudisc_update(ip6cp, validated)
struct ip6ctlparam *ip6cp;
int validated;
{
#if defined(__NetBSD__) || defined(__OpenBSD__)
unsigned long rtcount;
struct icmp6_mtudisc_callback *mc;
#endif
struct in6_addr *dst = ip6cp->ip6c_finaldst;
struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */
u_int mtu = ntohl(icmp6->icmp6_mtu);
struct rtentry *rt = NULL;
struct sockaddr_in6 sin6;
#ifdef __bsdi__
#ifdef NEW_STRUCT_ROUTE
struct route ro6;
#else
struct route_in6 ro6;
#endif
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
/*
* allow non-validated cases if memory is plenty, to make traffic
* from non-connected pcb happy.
*/
rtcount = rt_timer_count(icmp6_mtudisc_timeout_q);
if (validated) {
if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat)
return;
else if (0 <= icmp6_mtudisc_lowat &&
rtcount > icmp6_mtudisc_lowat) {
/*
* XXX nuke a victim, install the new one.
*/
}
} else {
if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat)
return;
}
#else
if (!validated)
return;
#endif
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = PF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = *dst;
/* XXX normally, this won't happen */
if (IN6_IS_ADDR_LINKLOCAL(dst)) {
sin6.sin6_addr.s6_addr16[1] =
htons(m->m_pkthdr.rcvif->if_index);
}
/* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
#if defined(__NetBSD__) || defined(__OpenBSD__)
rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
#else
#ifdef __FreeBSD__
rt = rtalloc1((struct sockaddr *)&sin6, 0,
RTF_CLONING | RTF_PRCLONING);
#else
#ifdef __bsdi__
bcopy(&sin6, &ro6.ro_dst, sizeof(struct sockaddr_in6));
ro6.ro_rt = 0;
/* rtcalloc((struct route *)&ro6); */
rtalloc((struct route *)&ro6);
rt = ro6.ro_rt;
#else
#error no case for this particular operating system
#endif
#endif
#endif
if (rt && (rt->rt_flags & RTF_HOST)
&& !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
if (mtu < IPV6_MMTU) {
/* xxx */
rt->rt_rmx.rmx_locks |= RTV_MTU;
} else if (mtu < rt->rt_ifp->if_mtu &&
rt->rt_rmx.rmx_mtu > mtu) {
icmp6stat.icp6s_pmtuchg++;
rt->rt_rmx.rmx_mtu = mtu;
}
}
if (rt) { /* XXX: need braces to avoid conflict with else in RTFREE. */
RTFREE(rt);
}
#if defined(__NetBSD__) || defined(__OpenBSD__)
/*
* Notify protocols that the MTU for this destination
* has changed.
*/
for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
mc = LIST_NEXT(mc, mc_list))
(*mc->mc_func)(&sin6.sin6_addr);
#endif
}
/*
* Process a Node Information Query packet, based on
* draft-ietf-ipngwg-icmp-name-lookups-07.
*
* Spec incompatibilities:
* - IPv6 Subject address handling
* - IPv4 Subject address handling support missing
* - Proxy reply (answer even if it's not for me)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -