ip_output.c

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

C
1,972
字号
	struct ip *ip;	u_short csum, offset;	ip = mtod(m, struct ip *);	offset = IP_VHL_HL(ip->ip_vhl) << 2 ;	csum = in_cksum_skip(m, ip->ip_len, offset);	if (m->m_pkthdr.csum_flags & CSUM_UDP && csum == 0)		csum = 0xffff;	offset += m->m_pkthdr.csum_data;	/* checksum offset */	if (offset + sizeof(u_short) > m->m_len) {		printf("delayed m_pullup, m->len: %d  off: %d  p: %d\n",		    m->m_len, offset, ip->ip_p);		/*		 * XXX		 * this shouldn't happen, but if it does, the		 * correct behavior may be to insert the checksum		 * in the existing chain instead of rearranging it.		 */		m = m_pullup(m, offset + sizeof(u_short));	}	*(u_short *)(m->m_data + offset) = csum;}/* * 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. * * XXX This routine assumes that the packet has no options in place. */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_short)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.rcvif = (struct ifnet *)0;		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;		(void)memcpy(mtod(m, void *), ip, 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(p->ipopt_list, ip + 1, optlen);	*phlen = sizeof(struct ip) + optlen;	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, *phlen >> 2);	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_VHL_HL(ip->ip_vhl) << 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;		}#ifdef DIAGNOSTIC		if (cnt < IPOPT_OLEN + sizeof(*cp))			panic("malformed IPv4 option passed to ip_optcopy");#endif		optlen = cp[IPOPT_OLEN];#ifdef DIAGNOSTIC		if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)			panic("malformed IPv4 option passed to ip_optcopy");#endif		/* bogus lengths should have been caught by ip_dooptions */		if (optlen > cnt)			optlen = cnt;		if (IPOPT_COPIED(opt)) {			bcopy(cp, dp, optlen);			dp += optlen;		}	}	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)		*dp++ = IPOPT_EOL;	return (optlen);}/* * IP socket option processing. */intip_ctloutput(so, sopt)	struct socket *so;	struct sockopt *sopt;{	struct	inpcb *inp = sotoinpcb(so);	int	error, optval;	error = optval = 0;	if (sopt->sopt_level != IPPROTO_IP) {		return (EINVAL);	}	switch (sopt->sopt_dir) {	case SOPT_SET:		switch (sopt->sopt_name) {		case IP_OPTIONS:#ifdef notyet		case IP_RETOPTS:#endif		{			struct mbuf *m;			if (sopt->sopt_valsize > MLEN) {				error = EMSGSIZE;				break;			}			MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER);			if (m == 0) {				error = ENOBUFS;				break;			}			m->m_len = sopt->sopt_valsize;			error = sooptcopyin(sopt, mtod(m, char *), m->m_len,					    m->m_len);						return (ip_pcbopts(sopt->sopt_name, &inp->inp_options,					   m));		}		case IP_TOS:		case IP_TTL:		case IP_RECVOPTS:		case IP_RECVRETOPTS:		case IP_RECVDSTADDR:		case IP_RECVIF:#if defined(NFAITH) && NFAITH > 0		case IP_FAITH:#endif			error = sooptcopyin(sopt, &optval, sizeof optval,					    sizeof optval);			if (error)				break;			switch (sopt->sopt_name) {			case IP_TOS:				inp->inp_ip_tos = optval;				break;			case IP_TTL:				inp->inp_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;			case IP_RECVIF:				OPTSET(INP_RECVIF);				break;#if defined(NFAITH) && NFAITH > 0			case IP_FAITH:				OPTSET(INP_FAITH);				break;#endif			}			break;#undef OPTSET		case IP_MULTICAST_IF:		case IP_MULTICAST_VIF:		case IP_MULTICAST_TTL:		case IP_MULTICAST_LOOP:		case IP_ADD_MEMBERSHIP:		case IP_DROP_MEMBERSHIP:			error = ip_setmoptions(sopt, &inp->inp_moptions);			break;		case IP_PORTRANGE:			error = sooptcopyin(sopt, &optval, sizeof optval,					    sizeof optval);			if (error)				break;			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;#ifdef IPSEC		case IP_IPSEC_POLICY:		{			caddr_t req;			size_t len = 0;			int priv;			struct mbuf *m;			int optname;			if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */				break;			if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */				break;			priv = 1;			req = mtod(m, caddr_t);			len = m->m_len;			optname = sopt->sopt_name;			error = ipsec4_set_policy(inp, optname, req, len, priv);			m_freem(m);			break;		}#endif /*IPSEC*/		default:			error = ENOPROTOOPT;			break;		}		break;	case SOPT_GET:		switch (sopt->sopt_name) {		case IP_OPTIONS:		case IP_RETOPTS:			if (inp->inp_options)				error = sooptcopyout(sopt, 						     mtod(inp->inp_options,							  char *),						     inp->inp_options->m_len);			else				sopt->sopt_valsize = 0;			break;		case IP_TOS:		case IP_TTL:		case IP_RECVOPTS:		case IP_RECVRETOPTS:		case IP_RECVDSTADDR:		case IP_RECVIF:		case IP_PORTRANGE:#if defined(NFAITH) && NFAITH > 0		case IP_FAITH:#endif			switch (sopt->sopt_name) {			case IP_TOS:				optval = inp->inp_ip_tos;				break;			case IP_TTL:				optval = inp->inp_ip_ttl;				break;#define	OPTBIT(bit)	(inp->inp_flags & bit ? 1 : 0)			case IP_RECVOPTS:				optval = OPTBIT(INP_RECVOPTS);				break;			case IP_RECVRETOPTS:				optval = OPTBIT(INP_RECVRETOPTS);				break;			case IP_RECVDSTADDR:				optval = OPTBIT(INP_RECVDSTADDR);				break;			case IP_RECVIF:				optval = OPTBIT(INP_RECVIF);				break;			case IP_PORTRANGE:				if (inp->inp_flags & INP_HIGHPORT)					optval = IP_PORTRANGE_HIGH;				else if (inp->inp_flags & INP_LOWPORT)					optval = IP_PORTRANGE_LOW;				else					optval = 0;				break;#if defined(NFAITH) && NFAITH > 0			case IP_FAITH:				optval = OPTBIT(INP_FAITH);				break;#endif			}			error = sooptcopyout(sopt, &optval, sizeof optval);			break;		case IP_MULTICAST_IF:		case IP_MULTICAST_VIF:		case IP_MULTICAST_TTL:		case IP_MULTICAST_LOOP:		case IP_ADD_MEMBERSHIP:		case IP_DROP_MEMBERSHIP:			error = ip_getmoptions(sopt, inp->inp_moptions);			break;#ifdef IPSEC		case IP_IPSEC_POLICY:		{			struct mbuf *m = NULL;			caddr_t req = NULL;			size_t len = 0;                        size_t ovalsize = sopt->sopt_valsize;			caddr_t oval = (caddr_t)sopt->sopt_val;                        error = soopt_getm(sopt, &m); /* XXX */                        if (error != 0)                                break;                        error = soopt_mcopyin(sopt, m); /* XXX */                        if (error != 0)				break;                        sopt->sopt_valsize = ovalsize;                        sopt->sopt_val = oval;			if (m != 0) {				req = mtod(m, caddr_t);				len = m->m_len;			}			error = ipsec4_get_policy(sotoinpcb(so), req, len, &m);			if (error == 0)				error = soopt_mcopyout(sopt, m); /* XXX */			if (error == 0)				m_freem(m);			break;		}#endif /*IPSEC*/		default:			error = ENOPROTOOPT;			break;		}		break;	}	return (error);}/* * Set up IP options in pcb for insertion in output packets. * Store in mbuf with pointer in pcbopt, adding pseudo-option * with destination address if source routed. */static intip_pcbopts(optname, pcbopt, m)	int optname;	struct mbuf **pcbopt;	register struct mbuf *m;{	register int cnt, optlen;	register u_char *cp;	u_char opt;	/* turn off any old options */	if (*pcbopt)		(void)m_free(*pcbopt);	*pcbopt = 0;	if (m == (struct mbuf *)0 || m->m_len == 0) {		/*		 * Only turning off any previous options.		 */		if (m)			(void)m_free(m);		return (0);	}#ifndef	vax	if (m->m_len % sizeof(int32_t))		goto bad;#endif	/*	 * IP first-hop destination address will be stored before	 * actual options; move other options back	 * and clear it when none present.	 */	if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN])		goto bad;	cnt = m->m_len;	m->m_len += sizeof(struct in_addr);	cp = mtod(m, u_char *) + sizeof(struct in_addr);	ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt);	bzero(mtod(m, caddr_t), sizeof(struct in_addr));	for (; cnt > 0; cnt -= optlen, cp += optlen) {		opt = cp[IPOPT_OPTVAL];		if (opt == IPOPT_EOL)			break;		if (opt == IPOPT_NOP)			optlen = 1;		else {			if (cnt < IPOPT_OLEN + sizeof(*cp))				goto bad;			optlen = cp[IPOPT_OLEN];			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)				goto bad;		}		switch (opt) {		default:			break;		case IPOPT_LSRR:		case IPOPT_SSRR:			/*			 * user process specifies route as:			 *	->A->B->C->D			 * D must be our final destination (but we can't			 * check that since we may not have connected yet).			 * A is first hop destination, which doesn't appear in			 * actual IP option, but is stored before the options.			 */			if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))				goto bad;			m->m_len -= sizeof(struct in_addr);			cnt -= sizeof(struct in_addr);			optlen -= sizeof(struct in_addr);			cp[IPOPT_OLEN] = optlen;			/*			 * Move first hop before start of options.			 */			bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),			    sizeof(struct in_addr));			/*			 * Then copy rest of options back			 * to close up the deleted entry.			 */			ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] +			    sizeof(struct in_addr)),			    (caddr_t)&cp[IPOPT_OFFSET+1],			    (unsigned)cnt + sizeof(struct in_addr));			break;		}

⌨️ 快捷键说明

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