ip_output.c

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

C
1,761
字号
		ip->ip_len = htons((u_short) ip->ip_len);		ip->ip_off = htons((u_short) ip->ip_off);		ip->ip_sum = 0;		/*		 * Now we check if this tdb has all the transforms which		 * are requried by the socket or our default policy.		 */		SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb);		if (sa_require & ~sa_have)			goto no_encap;		if (tdb == NULL) {			splx(s);			if (gw->sen_type == SENT_IPSP)			  DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet_ntoa4(gw->sen_ipsp_dst), ntohl(gw->sen_ipsp_spi), gw->sen_ipsp_sproto));#ifdef INET6			else			  DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet6_ntoa4(gw->sen_ipsp6_dst), ntohl(gw->sen_ipsp6_spi), gw->sen_ipsp6_sproto));#endif /* INET6 */	  			if (re->re_rt)                        	RTFREE(re->re_rt);			error = EHOSTUNREACH;			m_freem(m);			goto done;		}		for (t = tdb; t != NULL; t = t->tdb_onext)		    if ((t->tdb_sproto == IPPROTO_ESP && !esp_enable) ||			(t->tdb_sproto == IPPROTO_AH && !ah_enable)) {		        DPRINTF(("ip_output(): IPSec outbound packet dropped due to policy\n"));			if (re->re_rt)                        	RTFREE(re->re_rt);			error = EHOSTUNREACH;			m_freem(m);			goto done;		    }		while (tdb && tdb->tdb_xform) {			/* Check if the SPI is invalid */			if (tdb->tdb_flags & TDBF_INVALID) {				splx(s);			        DPRINTF(("ip_output(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto));				m_freem(m);				if (re->re_rt)					RTFREE(re->re_rt);				return ENXIO;			}#ifndef INET6			/* Sanity check */			if (tdb->tdb_dst.sa.sa_family != AF_INET) {			    splx(s);			        DPRINTF(("ip_output(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family));				m_freem(m);				if (re->re_rt)					RTFREE(re->re_rt);				return ENXIO;			}#endif /* INET6 */			/* Register first use, setup expiration timer */			if (tdb->tdb_first_use == 0) {				tdb->tdb_first_use = time.tv_sec;				tdb_expiration(tdb, TDBEXP_TIMEOUT);			}			/* Check for tunneling */			if (((tdb->tdb_dst.sa.sa_family == AF_INET) &&			     (tdb->tdb_dst.sin.sin_addr.s_addr != 			      INADDR_ANY) &&			     (tdb->tdb_dst.sin.sin_addr.s_addr !=			      ip->ip_dst.s_addr)) ||			    (tdb->tdb_dst.sa.sa_family == AF_INET6) ||			    ((tdb->tdb_flags & TDBF_TUNNELING) &&			     (tdb->tdb_xform->xf_type != XF_IP4))) {			        /* Fix length and checksum */			        ip->ip_len = htons(m->m_pkthdr.len);			        ip->ip_sum = in_cksum(m, ip->ip_hl << 2);				error = ipe4_output(m, tdb, &mp,						    ip->ip_hl << 2,						    offsetof(struct ip, ip_p));				if (mp == NULL)					error = EFAULT;				if (error) {					splx(s);					if (re->re_rt)						RTFREE(re->re_rt);					return error;				}				if (tdb->tdb_dst.sa.sa_family == AF_INET)				        ip6flag = 0;#ifdef INET6				if (tdb->tdb_dst.sa.sa_family == AF_INET6)				        ip6flag = 1;#endif /* INET6 */				m = mp;				mp = NULL;			}			if ((tdb->tdb_xform->xf_type == XF_IP4) &&			    (tdb->tdb_dst.sa.sa_family == AF_INET)) {			        ip = mtod(m, struct ip *);				ip->ip_len = htons(m->m_pkthdr.len);				ip->ip_sum = in_cksum(m, ip->ip_hl << 2);			}#ifdef INET6			if ((tdb->tdb_xform->xf_type == XF_IP4) &&			    (tdb->tdb_dst.sa.sa_family == AF_INET6)) {			    ip6 = mtod(m, struct ip6_hdr *);			    ip6->ip6_plen = htons(m->m_pkthdr.len);			}#endif /* INET6 */#ifdef INET6			/*			 * This assumes that there is only just an IPv6			 * header prepended.			 */			if (ip6flag)			  error = (*(tdb->tdb_xform->xf_output))(m, tdb, &mp, sizeof(struct ip6_hdr), offsetof(struct ip6_hdr, ip6_nxt));#endif /* INET6 */			if (!ip6flag)			  error = (*(tdb->tdb_xform->xf_output))(m, tdb, &mp, ip->ip_hl << 2, offsetof(struct ip, ip_p));			if (!error && mp == NULL)				error = EFAULT;			if (error) {				splx(s);				if (mp != NULL)					m_freem(mp);				if (re->re_rt)					RTFREE(re->re_rt);				return error;			}			m = mp;			mp = NULL;			if (!ip6flag) {			    ip = mtod(m, struct ip *);			    ip->ip_len = htons(m->m_pkthdr.len);			}#ifdef INET6			if (ip6flag) {			    ip6 = mtod(m, struct ip6_hdr *);			    ip6->ip6_plen = htons(m->m_pkthdr.len);			}#endif /* INET6 */			tdb = tdb->tdb_onext;		}		splx(s);		if (!ip6flag)		  ip->ip_sum = in_cksum(m, ip->ip_hl << 2);		/*		 * At this point, m is pointing to an mbuf chain with the		 * processed packet. Call ourselves recursively, but		 * bypass the encap code.		 */		if (re->re_rt)			RTFREE(re->re_rt);		if (!ip6flag) {		    ip = mtod(m, struct ip *);		    NTOHS(ip->ip_len);		    NTOHS(ip->ip_off);		    return ip_output(m, NULL, NULL,				     IP_ENCAPSULATED | IP_RAWOUTPUT,				     NULL, NULL);		}#ifdef INET6		if (ip6flag) {		    ip6 = mtod(m, struct ip6_hdr *);		    NTOHS(ip6->ip6_plen);		    /* Naturally, ip6_output() has to honor those two flags */		    return ip6_output(m, NULL, NULL,				     IP_ENCAPSULATED | IP_RAWOUTPUT,				     NULL, NULL);		}#endif /* INET6 */no_encap:		/* This is for possible future use, don't move or delete */		if (re->re_rt)			RTFREE(re->re_rt);		/* No IPSec processing though it was required, drop packet */		if (sa_require) {			error = EHOSTUNREACH;			m_freem(m);			goto done;		}	}#endif /* IPSEC */#if defined(IPFILTER) || defined(IPFILTER_LKM)	/*	 * looks like most checking has been done now...do a filter check	 */	{		struct mbuf *m0 = m;		if (fr_checkp && (*fr_checkp)(ip, hlen, ifp, 1, &m0)) {			error = EHOSTUNREACH;			goto done;		} else			ip = mtod(m = m0, struct ip *);	}#endif	/*	 * If small enough for interface, can just send directly.	 */	if ((u_int16_t)ip->ip_len <= ifp->if_mtu) {		ip->ip_len = htons((u_int16_t)ip->ip_len);		ip->ip_off = htons((u_int16_t)ip->ip_off);		ip->ip_sum = 0;		ip->ip_sum = in_cksum(m, hlen);		error = (*ifp->if_output)(ifp, m, sintosa(dst), ro->ro_rt);		goto done;	}	/*	 * Too large for interface; fragment if possible.	 * Must be able to put at least 8 bytes per fragment.	 */#if 0	/*	 * If IPsec packet is too big for the interface, try fragment it.	 * XXX This really is a quickhack.  May be inappropriate.	 * XXX fails if somebody is sending AH'ed packet, with:	 *	sizeof(packet without AH) < mtu < sizeof(packet with AH)	 */	if (sab && ip->ip_p != IPPROTO_AH && (flags & IP_FORWARDING) == 0)		ip->ip_off &= ~IP_DF;#endif /*IPSEC*/	if (ip->ip_off & IP_DF) {		error = EMSGSIZE;		ipstat.ips_cantfrag++;		goto bad;	}	len = (ifp->if_mtu - hlen) &~ 7;	if (len < 8) {		error = EMSGSIZE;		goto bad;	}    {	int mhlen, firstlen = len;	struct mbuf **mnext = &m->m_nextpkt;	/*	 * Loop through length of segment after first fragment,	 * make new header and copy data of each part and link onto chain.	 */	m0 = m;	mhlen = sizeof (struct ip);	for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {		MGETHDR(m, M_DONTWAIT, MT_HEADER);		if (m == 0) {			error = ENOBUFS;			ipstat.ips_odropped++;			goto sendorfree;		}		*mnext = m;		mnext = &m->m_nextpkt;		m->m_data += max_linkhdr;		mhip = mtod(m, struct ip *);		*mhip = *ip;		if (hlen > sizeof (struct ip)) {			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);			mhip->ip_hl = mhlen >> 2;		}		m->m_len = mhlen;		mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);		if (ip->ip_off & IP_MF)			mhip->ip_off |= IP_MF;		if (off + len >= (u_int16_t)ip->ip_len)			len = (u_int16_t)ip->ip_len - off;		else			mhip->ip_off |= IP_MF;		mhip->ip_len = htons((u_int16_t)(len + mhlen));		m->m_next = m_copy(m0, off, len);		if (m->m_next == 0) {			error = ENOBUFS;	/* ??? */			ipstat.ips_odropped++;			goto sendorfree;		}		m->m_pkthdr.len = mhlen + len;		m->m_pkthdr.rcvif = (struct ifnet *)0;		mhip->ip_off = htons((u_int16_t)mhip->ip_off);		mhip->ip_sum = 0;		mhip->ip_sum = in_cksum(m, mhlen);		ipstat.ips_ofragments++;	}	/*	 * Update first fragment by trimming what's been copied out	 * and updating header, then send each fragment (in order).	 */	m = m0;	m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len);	m->m_pkthdr.len = hlen + firstlen;	ip->ip_len = htons((u_int16_t)m->m_pkthdr.len);	ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));	ip->ip_sum = 0;	ip->ip_sum = in_cksum(m, hlen);sendorfree:	for (m = m0; m; m = m0) {		m0 = m->m_nextpkt;		m->m_nextpkt = 0;		if (error == 0)			error = (*ifp->if_output)(ifp, m, sintosa(dst),			    ro->ro_rt);		else			m_freem(m);	}	if (error == 0)		ipstat.ips_fragmented++;    }done:	if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)		RTFREE(ro->ro_rt);	return (error);bad:	m_freem(m0);	goto done;}/* * Insert IP options into preformed packet. * Adjust IP destination as required for IP source routing, * as indicated by a non-zero in_addr at the start of the options. */static struct mbuf *ip_insertoptions(m, opt, phlen)	register struct mbuf *m;	struct mbuf *opt;	int *phlen;{	register struct ipoption *p = mtod(opt, struct ipoption *);	struct mbuf *n;	register struct ip *ip = mtod(m, struct ip *);	unsigned optlen;	optlen = opt->m_len - sizeof(p->ipopt_dst);	if (optlen + (u_int16_t)ip->ip_len > IP_MAXPACKET)		return (m);		/* XXX should fail */	if (p->ipopt_dst.s_addr)		ip->ip_dst = p->ipopt_dst;	if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) {		MGETHDR(n, M_DONTWAIT, MT_HEADER);		if (n == 0)			return (m);		n->m_pkthdr.len = m->m_pkthdr.len + optlen;		m->m_len -= sizeof(struct ip);		m->m_data += sizeof(struct ip);		n->m_next = m;		m = n;		m->m_len = optlen + sizeof(struct ip);		m->m_data += max_linkhdr;		bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));	} else {		m->m_data -= optlen;		m->m_len += optlen;		m->m_pkthdr.len += optlen;		ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));	}	ip = mtod(m, struct ip *);	bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen);	*phlen = sizeof(struct ip) + optlen;	ip->ip_len += optlen;	return (m);}/* * Copy options from ip to jp, * omitting those not copied during fragmentation. */intip_optcopy(ip, jp)	struct ip *ip, *jp;{	register u_char *cp, *dp;	int opt, optlen, cnt;	cp = (u_char *)(ip + 1);	dp = (u_char *)(jp + 1);	cnt = (ip->ip_hl << 2) - sizeof (struct ip);	for (; cnt > 0; cnt -= optlen, cp += optlen) {		opt = cp[0];		if (opt == IPOPT_EOL)			break;		if (opt == IPOPT_NOP) {			/* Preserve for IP mcast tunnel's LSRR alignment. */			*dp++ = IPOPT_NOP;			optlen = 1;			continue;		} else			optlen = cp[IPOPT_OLEN];		/* bogus lengths should have been caught by ip_dooptions */		if (optlen > cnt)			optlen = cnt;		if (IPOPT_COPIED(opt)) {			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);			dp += optlen;		}	}	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)		*dp++ = IPOPT_EOL;	return (optlen);}/* * IP socket option processing. */intip_ctloutput(op, so, level, optname, mp)	int op;	struct socket *so;	int level, optname;	struct mbuf **mp;{	register struct inpcb *inp = sotoinpcb(so);	register struct mbuf *m = *mp;	register int optval = 0;#ifdef IPSEC	struct proc *p = curproc; /* XXX */	struct tdb *tdb;	struct tdb_ident *tdbip, tdbi;	int s;#endif	int error = 0;	if (level != IPPROTO_IP) {		error = EINVAL;		if (op == PRCO_SETOPT && *mp)			(void) m_free(*mp);	} else switch (op) {	case PRCO_SETOPT:		switch (optname) {		case IP_OPTIONS:#ifdef notyet		case IP_RETOPTS:			return (ip_pcbopts(optname, &inp->inp_options, m));#else			return (ip_pcbopts(&inp->inp_options, m));#endif		case IP_TOS:		case IP_TTL:		case IP_RECVOPTS:		case IP_RECVRETOPTS:		case IP_RECVDSTADDR:			if (m == NULL || m->m_len != sizeof(int))				error = EINVAL;			else {				optval = *mtod(m, int *);				switch (optname) {				case IP_TOS:					inp->inp_ip.ip_tos = optval;					break;				case IP_TTL:					inp->inp_ip.ip_ttl = optval;					break;#define	OPTSET(bit) \	if (optval) \		inp->inp_flags |= bit; \	else \		inp->inp_flags &= ~bit;				case IP_RECVOPTS:					OPTSET(INP_RECVOPTS);					break;				case IP_RECVRETOPTS:					OPTSET(INP_RECVRETOPTS);					break;				case IP_RECVDSTADDR:					OPTSET(INP_RECVDSTADDR);					break;				}			}			break;#undef OPTSET		case IP_MULTICAST_IF:		case IP_MULTICAST_TTL:		case IP_MULTICAST_LOOP:		case IP_ADD_MEMBERSHIP:		case IP_DROP_MEMBERSHIP:			error = ip_setmoptions(optname, &inp->inp_moptions, m);			break;		case IP_PORTRANGE:			if (m == 0 || m->m_len != sizeof(int))				error = EINVAL;			else {				optval = *mtod(m, int *);				switch (optval) {				case IP_PORTRANGE_DEFAULT:					inp->inp_flags &= ~(INP_LOWPORT);					inp->inp_flags &= ~(INP_HIGHPORT);					break;				case IP_PORTRANGE_HIGH:					inp->inp_flags &= ~(INP_LOWPORT);					inp->inp_flags |= INP_HIGHPORT;					break;				case IP_PORTRANGE_LOW:					inp->inp_flags &= ~(INP_HIGHPORT);					inp->inp_flags |= INP_LOWPORT;					break;				default:					error = EINVAL;					break;				}			}			break;		case IPSEC_OUTSA:#ifndef IPSEC			error = EINVAL;#else			s = spltdb();			if (m == 0 || m->m_len != sizeof(struct tdb_ident)) {				error = EINVAL;			} else {				tdbip = mtod(m, struct tdb_ident *);				tdb = gettdb(tdbip->spi, &tdbip->dst,				    tdbip->proto);				if (tdb == NULL)					error = ESRCH;				else					tdb_add_inp(tdb, inp);			}			splx(s);#endif /* IPSEC */			break;		case IP_AUTH_LEVEL:		case IP_ESP_TRANS_LEVEL:		case IP_ESP_NETWORK_LEVEL:#ifndef IPSEC			error = EINVAL;#else			if (m == 0 || m->m_len != sizeof(int)) {				error = EINVAL;				break;			}			optval = *mtod(m, u_char *);			if (optval < IPSEC_LEVEL_BYPASS || 			    optval > IPSEC_LEVEL_UNIQUE) {				error = EINVAL;				break;			}							switch (optname) {			case IP_AUTH_LEVEL:			        if (optval < ipsec_auth_default_level &&				    suser(p->p_ucred, &p->p_acflag)) {					error = EACCES;					break;				}				inp->inp_seclevel[SL_AUTH] = optval;				break;			case IP_ESP_TRANS_LEVEL:			        if (optval < ipsec_esp_trans_default_level &&				    suser(p->p_ucred, &p->p_acflag)) {					error = EACCES;

⌨️ 快捷键说明

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