📄 ip6_input.c
字号:
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 1 - opthead);
return(-1);
}
optlen = IP6OPT_RTALERT_LEN;
bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2);
*rtalertp = ntohs(rtalert_val);
break;
case IP6OPT_JUMBO:
/* XXX may need check for alignment */
if (hbhlen < IP6OPT_JUMBO_LEN) {
ip6stat.ip6s_toosmall++;
goto bad;
}
if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) {
/* XXX stat */
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 1 - opthead);
return(-1);
}
optlen = IP6OPT_JUMBO_LEN;
/*
* IPv6 packets that have non 0 payload length
* must not contain a jumbo payload option.
*/
ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_plen) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt - opthead);
return(-1);
}
/*
* We may see jumbolen in unaligned location, so
* we'd need to perform bcopy().
*/
bcopy(opt + 2, &jumboplen, sizeof(jumboplen));
jumboplen = (u_int32_t)htonl(jumboplen);
#if 1
/*
* if there are multiple jumbo payload options,
* *plenp will be non-zero and the packet will be
* rejected.
* the behavior may need some debate in ipngwg -
* multiple options does not make sense, however,
* there's no explicit mention in specification.
*/
if (*plenp != 0) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 2 - opthead);
return(-1);
}
#endif
/*
* jumbo payload length must be larger than 65535.
*/
if (jumboplen <= IPV6_MAXPACKET) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 2 - opthead);
return(-1);
}
*plenp = jumboplen;
break;
default: /* unknown option */
if (hbhlen < IP6OPT_MINLEN) {
ip6stat.ip6s_toosmall++;
goto bad;
}
optlen = ip6_unknown_opt(opt, m,
erroff + opt - opthead);
if (optlen == -1)
return(-1);
optlen += 2;
break;
}
}
return(0);
bad:
m_freem(m);
return(-1);
}
/*
* Unknown option processing.
* The third argument `off' is the offset from the IPv6 header to the option,
* which is necessary if the IPv6 header the and option header and IPv6 header
* is not continuous in order to return an ICMPv6 error.
*/
int
ip6_unknown_opt(optp, m, off)
u_int8_t *optp;
struct mbuf *m;
int off;
{
struct ip6_hdr *ip6;
switch (IP6OPT_TYPE(*optp)) {
case IP6OPT_TYPE_SKIP: /* ignore the option */
return((int)*(optp + 1));
case IP6OPT_TYPE_DISCARD: /* silently discard */
m_freem(m);
return(-1);
case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off);
return(-1);
case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */
ip6stat.ip6s_badoptions++;
ip6 = mtod(m, struct ip6_hdr *);
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
(m->m_flags & (M_BCAST|M_MCAST)))
m_freem(m);
else
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_OPTION, off);
return(-1);
}
m_freem(m); /* XXX: NOTREACHED */
return(-1);
}
/*
* Create the "control" list for this pcb.
* The function will not modify mbuf chain at all.
*
* with KAME mbuf chain restriction:
* The routine will be called from upper layer handlers like tcp6_input().
* Thus the routine assumes that the caller (tcp6_input) have already
* called IP6_EXTHDR_CHECK() and all the extension headers are located in the
* very first mbuf on the mbuf chain.
*/
void
ip6_savecontrol(in6p, ip6, m, ctl, prevctlp)
#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(HAVE_NRL_INPCB)
struct inpcb *in6p;
#else
struct in6pcb *in6p;
#endif
struct ip6_hdr *ip6;
struct mbuf *m;
struct ip6_recvpktopts *ctl, **prevctlp;
{
#define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y))
struct mbuf **mp;
struct cmsghdr *cm = NULL;
struct ip6_recvpktopts *prevctl = NULL;
#ifdef HAVE_NRL_INPCB
# define in6p_flags inp_flags
#endif
#ifdef __bsdi__
# define sbcreatecontrol so_cmsg
#endif
int privileged = 1;
if (ctl == NULL) /* validity check */
return;
bzero(ctl, sizeof(*ctl)); /* XXX is it really OK? */
mp = &ctl->head;
/*
* If caller wanted to keep history, allocate space to store the
* history at the first time.
*/
if (prevctlp) {
if (*prevctlp == NULL) {
MALLOC(prevctl, struct ip6_recvpktopts *,
sizeof(*prevctl), M_IP6OPT, M_NOWAIT);
if (prevctl == NULL) {
printf("ip6_savecontrol: can't allocate "
" enough space for history\n");
return;
}
bzero(prevctl, sizeof(*prevctl));
*prevctlp = prevctl;
}
else
prevctl = *prevctlp;
}
#ifdef SO_TIMESTAMP
if ((in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0) {
struct timeval tv;
microtime(&tv);
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
SCM_TIMESTAMP, SOL_SOCKET);
if (*mp) {
/* always set regradless of the previous value */
ctl->timestamp = *mp;
mp = &(*mp)->m_next;
}
}
#endif
/* RFC 2292 sec. 5 */
if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) {
struct in6_pktinfo pi6, *prevpi = NULL;
bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr));
if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr) ||
IN6_IS_ADDR_MC_INTFACELOCAL(&pi6.ipi6_addr))
pi6.ipi6_addr.s6_addr16[1] = 0;
pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif)
? m->m_pkthdr.rcvif->if_index
: 0;
if (prevctl && prevctl->pktinfo) {
cm = mtod(prevctl->pktinfo, struct cmsghdr *);
prevpi = (struct in6_pktinfo *)CMSG_DATA(cm);
}
/*
* Make a new option only if this is the first time or if the
* option value is chaned from last time.
*/
if (prevpi == NULL || bcmp(prevpi, &pi6, sizeof(pi6))) {
*mp = sbcreatecontrol((caddr_t) &pi6,
sizeof(struct in6_pktinfo),
IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO),
IPPROTO_IPV6);
if (*mp) {
ctl->pktinfo = *mp;
mp = &(*mp)->m_next;
}
}
}
if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) {
int hlim = ip6->ip6_hlim & 0xff, oldhlim = -1;
if (prevctl && prevctl->hlim) {
cm = mtod(prevctl->hlim, struct cmsghdr *);
bcopy(CMSG_DATA(cm), &oldhlim, sizeof(oldhlim));
oldhlim &= 0xff;
}
if (oldhlim < 0 || hlim != oldhlim) {
*mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int),
IS2292(IPV6_2292HOPLIMIT, IPV6_HOPLIMIT),
IPPROTO_IPV6);
if (*mp) {
ctl->hlim = *mp;
mp = &(*mp)->m_next;
}
}
}
if ((in6p->in6p_flags & IN6P_TCLASS) != 0) {
u_int32_t flowinfo;
int oflowinfo = -1;
int v;
flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
flowinfo >>= 20;
if (prevctl && prevctl->hlim) {
cm = mtod(prevctl->hlim, struct cmsghdr *);
bcopy(CMSG_DATA(cm), &v, sizeof(v));
oflowinfo = v & 0xff;
}
if (oflowinfo < 0 || flowinfo != oflowinfo) {
v = flowinfo & 0xff;
*mp = sbcreatecontrol((caddr_t) &v, sizeof(v),
IPV6_TCLASS, IPPROTO_IPV6);
if (*mp) {
ctl->hlim = *mp;
mp = &(*mp)->m_next;
}
}
}
/*
* IPV6_HOPOPTS socket option. We require super-user privilege
* for the option, but it might be too strict, since there might
* be some hop-by-hop options which can be returned to normal user.
* See RFC 2292 section 6.
*/
if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0 && privileged) {
/*
* Check if a hop-by-hop options header is contatined in the
* received packet, and if so, store the options as ancillary
* data. Note that a hop-by-hop options header must be
* just after the IPv6 header, which fact is assured through
* the IPv6 input processing.
*/
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
struct ip6_hbh *hbh, *prevhbh = NULL;
int hbhlen = 0, prevhbhlen = 0;
#ifdef PULLDOWN_TEST
struct mbuf *ext;
#endif
#ifndef PULLDOWN_TEST
hbh = (struct ip6_hbh *)(ip6 + 1);
hbhlen = (hbh->ip6h_len + 1) << 3;
#else
ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr),
ip6->ip6_nxt);
if (ext == NULL) {
ip6stat.ip6s_tooshort++;
return;
}
hbh = mtod(ext, struct ip6_hbh *);
hbhlen = (hbh->ip6h_len + 1) << 3;
if (hbhlen != ext->m_len) {
m_freem(ext);
ip6stat.ip6s_tooshort++;
return;
}
#endif
if (prevctl && prevctl->hbh) {
cm = mtod(prevctl->hbh, struct cmsghdr *);
prevhbh = (struct ip6_hbh *)CMSG_DATA(cm);
prevhbhlen = (prevhbh->ip6h_len + 1) << 3;
}
/*
* Check if there's difference between the current
* and previous HbH headers.
* XXX: should the next header field be ignored?
*/
if (prevhbh == NULL || hbhlen != prevhbhlen ||
bcmp(prevhbh, hbh, hbhlen)) {
/*
* XXX: We copy whole the header even if a
* jumbo payload option is included, which
* option is to be removed before returning
* in the RFC 2292.
* Note: this constraint is removed in
* 2292bis.
*/
*mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS),
IPPROTO_IPV6);
if (*mp) {
ctl->hbh = *mp;
mp = &(*mp)->m_next;
}
}
#ifdef PULLDOWN_TEST
m_freem(ext);
#endif
}
}
if ((in6p->in6p_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) {
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);
/*
* Search for destination options headers or routing
* header(s) through the header chain, and stores each
* header as ancillary data.
* Note that the order of the headers remains in
* the chain of ancillary data.
*/
while (1) { /* is explicit loop prevention necessary? */
struct ip6_ext *ip6e = NULL;
int elen;
#ifdef PULLDOWN_TEST
struct mbuf *ext = NULL;
#endif
/*
* if it is not an extension header, don't try to
* pull it from the chain.
*/
switch (nxt) {
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
case IPPROTO_HOPOPTS:
case IPPROTO_AH: /* is it possible? */
break;
default:
goto loopend;
}
#ifndef PULLDOWN_TEST
if (off + sizeof(*ip6e) > m->m_len)
goto loopend;
ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
if (nxt == IPPROTO_AH)
elen = (ip6e->ip6e_len + 2) << 2;
else
elen = (ip6e->ip6e_len + 1) << 3;
if (off + elen > m->m_len)
goto loopend;
#else
ext = ip6_pullexthdr(m, off, nxt);
if (ext == NULL) {
ip6stat.ip6s_tooshort++;
return;
}
ip6e = mtod(ext, struct ip6_ext *);
if (nxt == IPPROTO_AH)
elen = (ip6e->ip6e_len + 2) << 2;
else
elen = (ip6e->ip6e_len + 1) << 3;
if (elen != ext->m_len) {
m_freem(ext);
ip6stat.ip6s_tooshort++;
return;
}
#endif
switch (nxt) {
case IPPROTO_DSTOPTS:
{
struct ip6_dest *prevdest = NULL;
int prevdestlen = 0;
if (!(in6p->in6p_flags & IN6P_DSTOPTS))
break;
/*
* We also require super-user privilege for
* the option. See comments on IN6_HOPOPTS.
*/
if (prevctl && prevctl->dest) {
cm = mtod(prevctl->dest,
struct cmsghdr *);
prevdest = (struct ip6_dest *)CMSG_DATA(cm);
prevdestlen =
(prevdest->ip6d_len + 1) << 3;
}
/*
* If this is the 1st dst opt header
* we enconter and this header is
* not different from the previous one,
* simply ignore the header.
*/
if (ctl->dest == NULL && prevdest &&
prevdestlen == elen &&
bcmp(ip6e, prevdest, elen) == 0)
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
IS2292(IPV6_2292DSTOPTS,
IPV6_DSTOPTS),
IPPROTO_IPV6);
if (ctl->dest == NULL)
ctl->dest = *mp;
if (*mp)
mp = &(*mp)->m_next;
break;
}
case IPPROTO_ROUTING:
{
struct ip6_rthdr *prevrth = NULL;
int prevrhlen = 0;
if (!in6p->in6p_flags & IN6P_RTHDR)
break;
if (prevctl && prevctl->rthdr) {
cm = mtod(prevctl->rthdr,
struct cmsghdr *);
prevrth = (struct ip6_rthdr *)CMSG_DATA(cm);
prevrhlen =
(prevrth->ip6r_len + 1) << 3;
}
/*
* Check if the rthdr should be passed to
* a user. See the comments for dstopt hdr.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -