ip_input.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,665 行 · 第 1/3 页

C
1,665
字号
		goto ours;	}	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)		goto ours;	if (ip->ip_dst.s_addr == INADDR_ANY)		goto ours;	/*	 * Not for us; forward if possible and desirable.	 */	if (ipforwarding == 0) {		ipstat.ips_cantforward++;		m_freem(m);	} else		ip_forward(m, 0);#ifdef IPFIREWALL_FORWARD	ip_fw_fwd_addr = NULL;#endif	return;ours:	/*	 * If offset or IP_MF are set, must reassemble.	 * Otherwise, nothing need be done.	 * (We could look in the reassembly queue to see	 * if the packet was previously fragmented,	 * but it's not worth the time; just let them time out.)	 */	if (ip->ip_off & (IP_MF | IP_OFFMASK | IP_RF)) {		if (m->m_flags & M_EXT) {		/* XXX */			if ((m = m_pullup(m, hlen)) == 0) {				ipstat.ips_toosmall++;#ifdef IPDIVERT				frag_divert_port = 0;				ip_divert_cookie = 0;#endif#ifdef IPFIREWALL_FORWARD				ip_fw_fwd_addr = NULL;#endif				return;			}			ip = mtod(m, struct ip *);		}		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);		/*		 * Look for queue of fragments		 * of this datagram.		 */		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)			if (ip->ip_id == fp->ipq_id &&			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&			    ip->ip_p == fp->ipq_p)				goto found;		fp = 0;		/* check if there's a place for the new queue */		if (nipq > maxnipq) {		    /*		     * drop something from the tail of the current queue		     * before proceeding further		     */		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */			for (i = 0; i < IPREASS_NHASH; i++) {			    if (ipq[i].prev != &ipq[i]) {				ip_freef(ipq[i].prev);				break;			    }			}		    } else			ip_freef(ipq[sum].prev);		}found:		/*		 * Adjust ip_len to not reflect header,		 * set ip_mff if more fragments are expected,		 * convert offset of this to bytes.		 */		ip->ip_len -= hlen;		mff = (ip->ip_off & IP_MF) != 0;		if (mff) {		        /*		         * Make sure that fragments have a data length			 * that's a non-zero multiple of 8 bytes.		         */			if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {				ipstat.ips_toosmall++; /* XXX */				goto bad;			}			m->m_flags |= M_FRAG;		}		ip->ip_off <<= 3;		/*		 * If datagram marked as having more fragments		 * or if this is not the first fragment,		 * attempt reassembly; if it succeeds, proceed.		 */		if (mff || ip->ip_off) {			ipstat.ips_fragments++;			m->m_pkthdr.header = ip;			ip = ip_reass(m, fp, &ipq[sum]);			if (ip == 0) {#ifdef	IPFIREWALL_FORWARD				ip_fw_fwd_addr = NULL;#endif				return;			}			/* Get the length of the reassembled packets header */			hlen = IP_VHL_HL(ip->ip_vhl) << 2;			ipstat.ips_reassembled++;			m = dtom(ip);#ifdef IPDIVERT			if (frag_divert_port) {				ip->ip_len += hlen;				HTONS(ip->ip_len);				HTONS(ip->ip_off);				HTONS(ip->ip_id);				ip->ip_sum = 0;				ip->ip_sum = in_cksum_hdr(ip);				NTOHS(ip->ip_id);				NTOHS(ip->ip_off);				NTOHS(ip->ip_len);				ip->ip_len -= hlen;			}#endif		} else			if (fp)				ip_freef(fp);	} else		ip->ip_len -= hlen;#ifdef IPDIVERT	/*	 * Divert reassembled packets to the divert protocol if required	 *  If divert port is null then cookie should be too,	 * so we shouldn't need to clear them here. Assume ip_divert does so.	 */	if (frag_divert_port) {		ipstat.ips_delivered++;		ip_divert_port = frag_divert_port;		frag_divert_port = 0;		(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);		return;	}	/* Don't let packets divert themselves */	if (ip->ip_p == IPPROTO_DIVERT) {		ipstat.ips_noproto++;		goto bad;	}#endif	/*	 * Switch out to protocol's input routine.	 */	ipstat.ips_delivered++;	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);#ifdef	IPFIREWALL_FORWARD	ip_fw_fwd_addr = NULL;	/* tcp needed it */#endif	return;bad:#ifdef	IPFIREWALL_FORWARD	ip_fw_fwd_addr = NULL;#endif	m_freem(m);}/* * IP software interrupt routine - to go away sometime soon */#ifdef OSKITvoid #else /* !OSKIT */static void#endif /* !OSKIT */ipintr(void){	int s;	struct mbuf *m;	while(1) {		s = splimp();		IF_DEQUEUE(&ipintrq, m);		splx(s);		if (m == 0)			return;		ip_input(m);	}}NETISR_SET(NETISR_IP, ipintr);  /* * Take incoming datagram fragment and try to * reassemble it into whole datagram.  If a chain for * reassembly of this datagram already exists, then it * is given as fp; otherwise have to make a chain. */static struct ip *ip_reass(m, fp, where)	register struct mbuf *m;	register struct ipq *fp;	struct   ipq    *where;{	struct ip *ip = mtod(m, struct ip *);	register struct mbuf *p = 0, *q, *nq;	struct mbuf *t;	int hlen = IP_VHL_HL(ip->ip_vhl) << 2;	int i, next;	/*	 * Presence of header sizes in mbufs	 * would confuse code below.	 */	m->m_data += hlen;	m->m_len -= hlen;	/*	 * If first fragment to arrive, create a reassembly queue.	 */	if (fp == 0) {		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)			goto dropfrag;		fp = mtod(t, struct ipq *);		insque(fp, where);		nipq++;		fp->ipq_ttl = IPFRAGTTL;		fp->ipq_p = ip->ip_p;		fp->ipq_id = ip->ip_id;		fp->ipq_src = ip->ip_src;		fp->ipq_dst = ip->ip_dst;		fp->ipq_frags = m;		m->m_nextpkt = NULL;#ifdef IPDIVERT		fp->ipq_divert = 0;		fp->ipq_div_cookie = 0;#endif		goto inserted;	}#define GETIP(m)	((struct ip*)((m)->m_pkthdr.header))	/*	 * Find a segment which begins after this one does.	 */	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)		if (GETIP(q)->ip_off > ip->ip_off)			break;	/*	 * If there is a preceding segment, it may provide some of	 * our data already.  If so, drop the data from the incoming	 * segment.  If it provides all of our data, drop us, otherwise	 * stick new segment in the proper place.	 */	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(dtom(ip), i);			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);			break;		}		nq = q->m_nextpkt;		m->m_nextpkt = nq;		m_freem(q);	}inserted:#ifdef IPDIVERT	/*	 * Any fragment diverting causes the whole packet to divert	 */	if (frag_divert_port) {		fp->ipq_divert = frag_divert_port;		fp->ipq_div_cookie = ip_divert_cookie;	}	frag_divert_port = 0;	ip_divert_cookie = 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_cat(m, q);	}#ifdef IPDIVERT	/*	 * extract divert port for packet, if any	 */	frag_divert_port = fp->ipq_divert;	ip_divert_cookie = 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; m; m = m->m_next)			plen += m->m_len;		t->m_pkthdr.len = plen;	}	return (ip);dropfrag:#ifdef IPDIVERT	frag_divert_port = 0;	ip_divert_cookie = 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);			}		}	}	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 {			optlen = cp[IPOPT_OLEN];			if (optlen <= 0 || 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 ((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 - 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 {

⌨️ 快捷键说明

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