ip6_output.c

来自「eCos操作系统源码」· C语言 代码 · 共 2,527 行 · 第 1/5 页

C
2,527
字号
#endif			error = nd6_output(ifp, origifp, m, dst, rt);		} else			m_freem(m);	}	if (error == 0)		ip6stat.ip6s_fragmented++;done:	if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */		RTFREE(ro->ro_rt);	} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {		RTFREE(ro_pmtu->ro_rt);	}#if defined(IPSEC) && !defined(__OpenBSD__)	if (sp != NULL)		key_freesp(sp);#endif /* IPSEC */#ifdef MIP6	mip6_destopt_discard(&mip6opt);#endif /* MIP6 */	return(error);freehdrs:#ifdef MIP6	mip6_destopt_discard(&mip6opt);#endif /* MIP6 */	m_freem(exthdrs.ip6e_hbh);	/* m_freem will check if mbuf is 0 */	m_freem(exthdrs.ip6e_dest1);	m_freem(exthdrs.ip6e_rthdr);#ifdef MIP6	m_freem(exthdrs.ip6e_haddr);#endif /* MIP6 */	m_freem(exthdrs.ip6e_dest2);	/* fall through */bad:	m_freem(m);	goto done;}static intip6_copyexthdr(mp, hdr, hlen)	struct mbuf **mp;	caddr_t hdr;	int hlen;{	struct mbuf *m;	if (hlen > MCLBYTES)		return(ENOBUFS); /* XXX */	MGET(m, M_DONTWAIT, MT_DATA);	if (!m)		return(ENOBUFS);	if (hlen > MLEN) {		MCLGET(m, M_DONTWAIT);		if ((m->m_flags & M_EXT) == 0) {			m_free(m);			return(ENOBUFS);		}	}	m->m_len = hlen;	if (hdr)		bcopy(hdr, mtod(m, caddr_t), hlen);	*mp = m;	return(0);}/* * Insert jumbo payload option. */static intip6_insert_jumboopt(exthdrs, plen)	struct ip6_exthdrs *exthdrs;	u_int32_t plen;{	struct mbuf *mopt;	u_char *optbuf;	u_int32_t v;#define JUMBOOPTLEN	8	/* length of jumbo payload option and padding */	/*	 * If there is no hop-by-hop options header, allocate new one.	 * If there is one but it doesn't have enough space to store the	 * jumbo payload option, allocate a cluster to store the whole options.	 * Otherwise, use it to store the options.	 */	if (exthdrs->ip6e_hbh == 0) {		MGET(mopt, M_DONTWAIT, MT_DATA);		if (mopt == 0)			return(ENOBUFS);		mopt->m_len = JUMBOOPTLEN;		optbuf = mtod(mopt, u_char *);		optbuf[1] = 0;	/* = ((JUMBOOPTLEN) >> 3) - 1 */		exthdrs->ip6e_hbh = mopt;	} else {		struct ip6_hbh *hbh;		mopt = exthdrs->ip6e_hbh;		if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {			/*			 * XXX assumption:			 * - exthdrs->ip6e_hbh is not referenced from places			 *   other than exthdrs.			 * - exthdrs->ip6e_hbh is not an mbuf chain.			 */			int oldoptlen = mopt->m_len;			struct mbuf *n;			/*			 * XXX: give up if the whole (new) hbh header does			 * not fit even in an mbuf cluster.			 */			if (oldoptlen + JUMBOOPTLEN > MCLBYTES)				return(ENOBUFS);			/*			 * As a consequence, we must always prepare a cluster			 * at this point.			 */			MGET(n, M_DONTWAIT, MT_DATA);			if (n) {				MCLGET(n, M_DONTWAIT);				if ((n->m_flags & M_EXT) == 0) {					m_freem(n);					n = NULL;				}			}			if (!n)				return(ENOBUFS);			n->m_len = oldoptlen + JUMBOOPTLEN;			bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t),			      oldoptlen);			optbuf = mtod(n, caddr_t) + oldoptlen;			m_freem(mopt);			mopt = exthdrs->ip6e_hbh = n;		} else {			optbuf = mtod(mopt, u_char *) + mopt->m_len;			mopt->m_len += JUMBOOPTLEN;		}		optbuf[0] = IP6OPT_PADN;		optbuf[1] = 1;		/*		 * Adjust the header length according to the pad and		 * the jumbo payload option.		 */		hbh = mtod(mopt, struct ip6_hbh *);		hbh->ip6h_len += (JUMBOOPTLEN >> 3);	}	/* fill in the option. */	optbuf[2] = IP6OPT_JUMBO;	optbuf[3] = 4;	v = (u_int32_t)htonl(plen + JUMBOOPTLEN);	bcopy(&v, &optbuf[4], sizeof(u_int32_t));	/* finally, adjust the packet header length */	exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;	return(0);#undef JUMBOOPTLEN}/* * Insert fragment header and copy unfragmentable header portions. */static intip6_insertfraghdr(m0, m, hlen, frghdrp)	struct mbuf *m0, *m;	int hlen;	struct ip6_frag **frghdrp;{	struct mbuf *n, *mlast;	if (hlen > sizeof(struct ip6_hdr)) {		n = m_copym(m0, sizeof(struct ip6_hdr),			    hlen - sizeof(struct ip6_hdr), M_DONTWAIT);		if (n == 0)			return(ENOBUFS);		m->m_next = n;	} else		n = m;	/* Search for the last mbuf of unfragmentable part. */	for (mlast = n; mlast->m_next; mlast = mlast->m_next)		;	if ((mlast->m_flags & M_EXT) == 0 &&	    M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) {		/* use the trailing space of the last mbuf for the fragment hdr */		*frghdrp =			(struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len);		mlast->m_len += sizeof(struct ip6_frag);		m->m_pkthdr.len += sizeof(struct ip6_frag);	} else {		/* allocate a new mbuf for the fragment header */		struct mbuf *mfrg;		MGET(mfrg, M_DONTWAIT, MT_DATA);		if (mfrg == 0)			return(ENOBUFS);		mfrg->m_len = sizeof(struct ip6_frag);		*frghdrp = mtod(mfrg, struct ip6_frag *);		mlast->m_next = mfrg;	}	return(0);}static intip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)#ifdef NEW_STRUCT_ROUTE	struct route *ro_pmtu, *ro;#else	struct route_in6 *ro_pmtu, *ro;#endif	struct ifnet *ifp;	struct in6_addr *dst;	/* XXX: should be sockaddr_in6 */	u_long *mtup;{	u_int32_t mtu = 0;	int error = 0;	if (ro_pmtu != ro) {		/* The first hop and the final destination may differ. */		struct sockaddr_in6 *sa6_dst =			(struct sockaddr_in6 *)&ro_pmtu->ro_dst;		if (ro_pmtu->ro_rt && ((ro_pmtu->ro_rt->rt_flags & RTF_UP)				       == 0 ||				       !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr,							   dst))) {			RTFREE(ro_pmtu->ro_rt);			ro_pmtu->ro_rt = (struct rtentry *)NULL;		}		if (ro_pmtu->ro_rt == NULL) {			bzero(sa6_dst, sizeof(*sa6_dst));			sa6_dst->sin6_family = AF_INET6;			sa6_dst->sin6_len = sizeof(struct sockaddr_in6);			sa6_dst->sin6_addr = *dst;#ifdef __bsdi__			/* bsdi needs rtcalloc to clone a route. */			rtcalloc((struct route *)ro_pmtu);#else			rtalloc((struct route *)ro_pmtu);#endif		}	}	if (ro_pmtu->ro_rt) {		u_int32_t ifmtu;		if (ifp == NULL)			ifp = ro_pmtu->ro_rt->rt_ifp;		ifmtu = nd_ifinfo[ifp->if_index].linkmtu;		mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;		if (mtu > ifmtu || mtu == 0) {			/*			 * The MTU on the route is larger than the MTU on			 * the interface!  This shouldn't happen, unless the			 * MTU of the interface has been changed after the			 * interface was brought up.  Change the MTU in the			 * route to match the interface MTU (as long as the			 * field isn't locked).			 *			 * if MTU on the route is 0, we need to fix the MTU.			 * this case happens with path MTU discovery timeouts.			 */			 mtu = ifmtu;			 if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0)				 ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */		}	} else if (ifp) {		mtu = nd_ifinfo[ifp->if_index].linkmtu;	} else		error = EHOSTUNREACH; /* XXX */	*mtup = mtu;	return(error);}/* * IP6 socket option processing. */#if defined(__FreeBSD__) && __FreeBSD__ >= 3intip6_ctloutput(so, sopt)	struct socket *so;	struct sockopt *sopt;#elseintip6_ctloutput(op, so, level, optname, mp)	int op;	struct socket *so;	int level, optname;	struct mbuf **mp;#endif{	int privileged, optdatalen;	void *optdata;	struct ip6_recvpktopts *rcvopts;#if defined(IPSEC) && defined(__OpenBSD__)	struct proc *p = curproc; /* XXX */	struct tdb *tdb;	struct tdb_ident *tdbip, tdbi;	int s;#endif#if defined(__FreeBSD__) && __FreeBSD__ >= 3	struct inpcb *in6p = sotoinpcb(so);	int error, optval;	int level, op, optname;	int optlen;	struct proc *p;	if (!sopt) {		panic("ip6_ctloutput: arg soopt is NULL");	}        level = sopt->sopt_level;        op = sopt->sopt_dir;        optname = sopt->sopt_name;        optlen = sopt->sopt_valsize;        p = sopt->sopt_p;#else#ifdef HAVE_NRL_INPCB	struct inpcb *inp = sotoinpcb(so);#define in6p inp#else  /* !NRL */	struct in6pcb *in6p = sotoin6pcb(so);#endif /* HAVE_NRL_INPCB */	struct mbuf *m = *mp;	int error, optval;	int optlen;#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)	struct proc *p = curproc;	/* XXX */#endif	optlen = m ? m->m_len : 0;#endif /* FreeBSD >= 3 */	error = optval = 0;        privileged = 1;	rcvopts = in6p->in6p_inputopts;	if (level == IPPROTO_IPV6) {		switch (op) {#if defined(__FreeBSD__) && __FreeBSD__ >= 3		case SOPT_SET:#else		case PRCO_SETOPT:#endif			switch (optname) {			case IPV6_2292PKTOPTIONS:#ifdef IPV6_PKTOPTIONS			case IPV6_PKTOPTIONS:#endif			{#if defined(__FreeBSD__) && __FreeBSD__ >= 3				struct mbuf *m;				error = soopt_getm(sopt, &m); /* XXX */				if (error)					break;				error = soopt_mcopyin(sopt, m); /* XXX */				if (error)					break;				error = ip6_pcbopts(&in6p->in6p_outputopts,						    m, so, sopt);				m_freem(m); /* XXX */#else				error = ip6_pcbopts(&in6p->in6p_outputopts,						    m, so);#endif /* FreeBSD >= 3 */				break;			}			/*			 * Use of some Hop-by-Hop options or some			 * Destination options, might require special			 * privilege.  That is, normal applications			 * (without special privilege) might be forbidden			 * from setting certain options in outgoing packets,			 * and might never see certain options in received			 * packets. [RFC 2292 Section 6]			 * KAME specific note:			 *  KAME prevents non-privileged users from sending or			 *  receiving ANY hbh/dst options in order to avoid			 *  overhead of parsing options in the kernel.			 */			case IPV6_RECVHOPOPTS:			case IPV6_RECVDSTOPTS:			case IPV6_RECVRTHDRDSTOPTS:				/* fall through */			case IPV6_UNICAST_HOPS:			case IPV6_HOPLIMIT:			case IPV6_FAITH:			case IPV6_RECVPKTINFO:			case IPV6_RECVHOPLIMIT:			case IPV6_RECVRTHDR:			case IPV6_RECVPATHMTU:			case IPV6_RECVTCLASS:			case IPV6_V6ONLY:			case IPV6_AUTOFLOWLABEL:				if (optlen != sizeof(int)) {					error = EINVAL;					break;				}#if defined(__FreeBSD__) && __FreeBSD__ >= 3				error = sooptcopyin(sopt, &optval,					sizeof optval, sizeof optval);				if (error)					break;#else				optval = *mtod(m, int *);#endif				switch (optname) {				case IPV6_UNICAST_HOPS:					if (optval < -1 || optval >= 256)						error = EINVAL;					else {						/* -1 = kernel default */						in6p->in6p_hops = optval;#if defined(__FreeBSD__) && __FreeBSD__ >= 3						if ((in6p->in6p_vflag &						     INP_IPV4) != 0)							in6p->inp_ip_ttl = optval;#endif					}					break;#define OPTSET(bit) \do { \	if (optval) \		in6p->in6p_flags |= (bit); \	else \		in6p->in6p_flags &= ~(bit); \} while (0)#define OPTSET2292(bit) \do { \	in6p->in6p_flags |= IN6P_RFC2292; \	if (optval) \		in6p->in6p_flags |= (bit); \	else \		in6p->in6p_flags &= ~(bit); \} while (0)#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0)				case IPV6_RECVPKTINFO:					/* cannot mix with RFC2292 */					if (OPTBIT(IN6P_RFC2292)) {						error = EINVAL;						break;					}					OPTSET(IN6P_PKTINFO);					if (OPTBIT(IN6P_PKTINFO) == 0)						ip6_reset_rcvopt(rcvopts, IPV6_RECVPKTINFO);					break;				case IPV6_HOPLIMIT:				{					struct ip6_pktopts **optp;					/* cannot mix with RFC2292 */					if (OPTBIT(IN6P_RFC2292)) {						error = EINVAL;						break;					}					optp = &in6p->in6p_outputopts;					error = ip6_pcbopt(IPV6_HOPLIMIT,							   (u_char *)&optval,							   sizeof(optval),							   optp,							   privileged);					break;				}				case IPV6_RECVHOPLIMIT:					/* cannot mix with RFC2292 */					if (OPTBIT(IN6P_RFC2292)) {						error = EINVAL;						break;					}					OPTSET(IN6P_HOPLIMIT);					if (OPTBIT(IN6P_HOPLIMIT) == 0)						ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPLIMIT);					break;				case IPV6_RECVHOPOPTS:					/* cannot mix with RFC2292 */					if (OPTBIT(IN6P_RFC2292)) {						error = EINVAL;						break;					}					OPTSET(IN6P_HOPOPTS);					if (OPTBIT(IN6P_HOPOPTS) == 0)						ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPOPTS);					break;				case IPV6_RECVDSTOPTS:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?