ip_input.c

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

C
1,992
字号
	 */	if (p) {		i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;		if (i > 0) {			if (i >= ip->ip_len)				goto dropfrag;			m_adj(m, i);			m->m_pkthdr.csum_flags = 0;			ip->ip_off += i;			ip->ip_len -= i;		}		m->m_nextpkt = p->m_nextpkt;		p->m_nextpkt = m;	} else {		m->m_nextpkt = fp->ipq_frags;		fp->ipq_frags = m;	}	/*	 * While we overlap succeeding segments trim them or,	 * if they are completely covered, dequeue them.	 */	for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;	     q = nq) {		i = (ip->ip_off + ip->ip_len) -		    GETIP(q)->ip_off;		if (i < GETIP(q)->ip_len) {			GETIP(q)->ip_len -= i;			GETIP(q)->ip_off += i;			m_adj(q, i);			q->m_pkthdr.csum_flags = 0;			break;		}		nq = q->m_nextpkt;		m->m_nextpkt = nq;		m_freem(q);	}inserted:#ifdef IPDIVERT	/*	 * Transfer firewall instructions to the fragment structure.	 * Any fragment diverting causes the whole packet to divert.	 */	fp->ipq_div_info = *divinfo;	fp->ipq_div_cookie = *divcookie;	*divinfo = 0;	*divcookie = 0;#endif	/*	 * Check for complete reassembly.	 */	next = 0;	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {		if (GETIP(q)->ip_off != next)			return (0);		next += GETIP(q)->ip_len;	}	/* Make sure the last packet didn't have the IP_MF flag */	if (p->m_flags & M_FRAG)		return (0);	/*	 * Reassembly is complete.  Make sure the packet is a sane size.	 */	q = fp->ipq_frags;	ip = GETIP(q);	if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) {		ipstat.ips_toolong++;		ip_freef(fp);		return (0);	}	/*	 * Concatenate fragments.	 */	m = q;	t = m->m_next;	m->m_next = 0;	m_cat(m, t);	nq = q->m_nextpkt;	q->m_nextpkt = 0;	for (q = nq; q != NULL; q = nq) {		nq = q->m_nextpkt;		q->m_nextpkt = NULL;		m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;		m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;		m_cat(m, q);	}#ifdef IPDIVERT	/*	 * Extract firewall instructions from the fragment structure.	 */	*divinfo = fp->ipq_div_info;	*divcookie = fp->ipq_div_cookie;#endif	/*	 * Create header for new ip packet by	 * modifying header of first packet;	 * dequeue and discard fragment reassembly header.	 * Make header visible.	 */	ip->ip_len = next;	ip->ip_src = fp->ipq_src;	ip->ip_dst = fp->ipq_dst;	remque(fp);	nipq--;	(void) m_free(dtom(fp));	m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);	m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);	/* some debugging cruft by sklower, below, will go away soon */	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */		register int plen = 0;		for (t = m; t; t = t->m_next)			plen += t->m_len;		m->m_pkthdr.len = plen;	}	return (m);dropfrag:#ifdef IPDIVERT	*divinfo = 0;	*divcookie = 0;#endif	ipstat.ips_fragdropped++;	m_freem(m);	return (0);#undef GETIP}/* * Free a fragment reassembly header and all * associated datagrams. */static voidip_freef(fp)	struct ipq *fp;{	register struct mbuf *q;	while (fp->ipq_frags) {		q = fp->ipq_frags;		fp->ipq_frags = q->m_nextpkt;		m_freem(q);	}	remque(fp);	(void) m_free(dtom(fp));	nipq--;}/* * IP timer processing; * if a timer expires on a reassembly * queue, discard it. */voidip_slowtimo(){	register struct ipq *fp;	int s = splnet();	int i;	for (i = 0; i < IPREASS_NHASH; i++) {		fp = ipq[i].next;		if (fp == 0)			continue;		while (fp != &ipq[i]) {			--fp->ipq_ttl;			fp = fp->next;			if (fp->prev->ipq_ttl == 0) {				ipstat.ips_fragtimeout++;				ip_freef(fp->prev);			}		}	}	/*	 * If we are over the maximum number of fragments	 * (due to the limit being lowered), drain off	 * enough to get down to the new limit.	 */	if (maxnipq >= 0 && nipq > maxnipq) {		for (i = 0; i < IPREASS_NHASH; i++) {			while ((nipq > maxnipq) &&				(ipq[i].next != &ipq[i])) {				ipstat.ips_fragdropped++;				ip_freef(ipq[i].next);			}		}	}	ipflow_slowtimo();	splx(s);}/* * Drain off all datagram fragments. */voidip_drain(){	int     i;	for (i = 0; i < IPREASS_NHASH; i++) {		while (ipq[i].next != &ipq[i]) {			ipstat.ips_fragdropped++;			ip_freef(ipq[i].next);		}	}	in_rtqdrain();}/* * Do option processing on a datagram, * possibly discarding it if bad options are encountered, * or forwarding it if source-routed. * Returns 1 if packet has been forwarded/freed, * 0 if the packet should be processed further. */static intip_dooptions(m)	struct mbuf *m;{	register struct ip *ip = mtod(m, struct ip *);	register u_char *cp;	register struct ip_timestamp *ipt;	register struct in_ifaddr *ia;	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;	struct in_addr *sin, dst;	n_time ntime;	dst = ip->ip_dst;	cp = (u_char *)(ip + 1);	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);	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)) {				code = &cp[IPOPT_OLEN] - (u_char *)ip;				goto bad;			}			optlen = cp[IPOPT_OLEN];			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {				code = &cp[IPOPT_OLEN] - (u_char *)ip;				goto bad;			}		}		switch (opt) {		default:			break;		/*		 * Source routing with record.		 * Find interface with current destination address.		 * If none on this machine then drop if strictly routed,		 * or do nothing if loosely routed.		 * Record interface address and bring up next address		 * component.  If strictly routed make sure next		 * address is on directly accessible net.		 */		case IPOPT_LSRR:		case IPOPT_SSRR:			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {				code = &cp[IPOPT_OLEN] - (u_char *)ip;				goto bad;			}			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {				code = &cp[IPOPT_OFFSET] - (u_char *)ip;				goto bad;			}			ipaddr.sin_addr = ip->ip_dst;			ia = (struct in_ifaddr *)				ifa_ifwithaddr((struct sockaddr *)&ipaddr);			if (ia == 0) {				if (opt == IPOPT_SSRR) {					type = ICMP_UNREACH;					code = ICMP_UNREACH_SRCFAIL;					goto bad;				}				if (!ip_dosourceroute)					goto nosourcerouting;				/*				 * Loose routing, and not at next destination				 * yet; nothing to do except forward.				 */				break;			}			off--;			/* 0 origin */			if (off > optlen - (int)sizeof(struct in_addr)) {				/*				 * End of source route.  Should be for us.				 */				if (!ip_acceptsourceroute)					goto nosourcerouting;				save_rte(cp, ip->ip_src);				break;			}			if (!ip_dosourceroute) {				if (ipforwarding) {					char buf[16]; /* aaa.bbb.ccc.ddd\0 */					/*					 * Acting as a router, so generate ICMP					 */nosourcerouting:					strcpy(buf, inet_ntoa(ip->ip_dst));					log(LOG_WARNING, 					    "attempted source route from %s to %s\n",					    inet_ntoa(ip->ip_src), buf);					type = ICMP_UNREACH;					code = ICMP_UNREACH_SRCFAIL;					goto bad;				} else {					/*					 * Not acting as a router, so silently drop.					 */					ipstat.ips_cantforward++;					m_freem(m);					return (1);				}			}			/*			 * locate outgoing interface			 */			(void)memcpy(&ipaddr.sin_addr, cp + off,			    sizeof(ipaddr.sin_addr));			if (opt == IPOPT_SSRR) {#define	INA	struct in_ifaddr *#define	SA	struct sockaddr *			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)				ia = (INA)ifa_ifwithnet((SA)&ipaddr);			} else				ia = ip_rtaddr(ipaddr.sin_addr);			if (ia == 0) {				type = ICMP_UNREACH;				code = ICMP_UNREACH_SRCFAIL;				goto bad;			}			ip->ip_dst = ipaddr.sin_addr;			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),			    sizeof(struct in_addr));			cp[IPOPT_OFFSET] += sizeof(struct in_addr);			/*			 * Let ip_intr's mcast routing check handle mcast pkts			 */			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));			break;		case IPOPT_RR:			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {				code = &cp[IPOPT_OFFSET] - (u_char *)ip;				goto bad;			}			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {				code = &cp[IPOPT_OFFSET] - (u_char *)ip;				goto bad;			}			/*			 * If no space remains, ignore.			 */			off--;			/* 0 origin */			if (off > optlen - (int)sizeof(struct in_addr))				break;			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,			    sizeof(ipaddr.sin_addr));			/*			 * locate outgoing interface; if we're the destination,			 * use the incoming interface (should be same).			 */			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {				type = ICMP_UNREACH;				code = ICMP_UNREACH_HOST;				goto bad;			}			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),			    sizeof(struct in_addr));			cp[IPOPT_OFFSET] += sizeof(struct in_addr);			break;		case IPOPT_TS:			code = cp - (u_char *)ip;			ipt = (struct ip_timestamp *)cp;			if (ipt->ipt_len < 4 || ipt->ipt_len > 40) {				code = (u_char *)&ipt->ipt_len - (u_char *)ip;				goto bad;			}			if (ipt->ipt_ptr < 5) {				code = (u_char *)&ipt->ipt_ptr - (u_char *)ip;				goto bad;			}			if (ipt->ipt_ptr >			    ipt->ipt_len - (int)sizeof(int32_t)) {				if (++ipt->ipt_oflw == 0) {					code = (u_char *)&ipt->ipt_ptr -					    (u_char *)ip;					goto bad;				}				break;			}			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);			switch (ipt->ipt_flg) {			case IPOPT_TS_TSONLY:				break;			case IPOPT_TS_TSANDADDR:				if (ipt->ipt_ptr - 1 + sizeof(n_time) +				    sizeof(struct in_addr) > ipt->ipt_len) {					code = (u_char *)&ipt->ipt_ptr -					    (u_char *)ip;					goto bad;				}				ipaddr.sin_addr = dst;				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,							    m->m_pkthdr.rcvif);				if (ia == 0)					continue;				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,				    sizeof(struct in_addr));				ipt->ipt_ptr += sizeof(struct in_addr);				break;			case IPOPT_TS_PRESPEC:				if (ipt->ipt_ptr - 1 + sizeof(n_time) +				    sizeof(struct in_addr) > ipt->ipt_len) {					code = (u_char *)&ipt->ipt_ptr -					    (u_char *)ip;					goto bad;				}				(void)memcpy(&ipaddr.sin_addr, sin,				    sizeof(struct in_addr));				if (ifa_ifwithaddr((SA)&ipaddr) == 0)					continue;				ipt->ipt_ptr += sizeof(struct in_addr);				break;			default:				/* XXX can't take &ipt->ipt_flg */				code = (u_char *)&ipt->ipt_ptr -				    (u_char *)ip + 1;				goto bad;			}			ntime = iptime();			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,			    sizeof(n_time));			ipt->ipt_ptr += sizeof(n_time);		}	}	if (forward && ipforwarding) {		ip_forward(m, 1);		return (1);	}	return (0);bad:	icmp_error(m, type, code, 0, 0);	ipstat.ips_badoptions++;	return (1);}/* * Given address of next destination (final or next hop), * return internet address info of interface to be used to get there. */static struct in_ifaddr *ip_rtaddr(dst)	 struct in_addr dst;{	register struct sockaddr_in *sin;	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {		if (ipforward_rt.ro_rt) {			RTFREE(ipforward_rt.ro_rt);			ipforward_rt.ro_rt = 0;		}		sin->sin_family = AF_INET;		sin->sin_len = sizeof(*sin);		sin->sin_addr = dst;		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);	}	if (ipforward_rt.ro_rt == 0)		return ((struct in_ifaddr *)0);	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);}

⌨️ 快捷键说明

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