⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip6_input.c

📁 嵌入式操作系统ECOS的网络开发包
💻 C
📖 第 1 页 / 共 5 页
字号:
				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 + -