BBN-Vax-TCP/bbnnet/ip_input.c

Compare this file to the similar file:
Show the results in this format:

#include "../h/param.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/icmp.h"
#include "../bbnnet/ucb.h"

int nosum = 0;

/*
 * IP level input routine 
 *
 * Called from 1822 level upon receipt of an internet datagram or fragment.  
 * This routine does fragment reassembly, if necessary, and passes completed
 * datagrams to higher level protocol processing routines on the basis of the 
 * ip header protocol field.  It is passed a pointer to an mbuf chain 
 * containing the datagram/fragment. The mbuf offset/length are set to point 
 * at the ip header.
 */
ip_input(mp, ifp)
struct mbuf *mp;
struct ifcb *ifp;
{
	register struct ip *ip, *q;
	register struct ipq *fp;
	register struct mbuf *m;
	register i;
	struct mbuf *n;
	int hlen;
	struct ip *p, *savq;
	struct ipq *ip_findf();

	ip = (struct ip *)((int)mp + mp->m_off);        /* ->ip hdr */

	/* make sure header does not overflow mbuf */

	if ((hlen = ip->ip_hl << 2) > mp->m_len) {
		printf("ip header overflow\n");
		netlog(mp);
		return;
	}

#ifndef mbb
	i = (unsigned short)ip->ip_sum;
#else
	i = (unsigned short)short_from_net(ip->ip_sum);
#endif mbb
	ip->ip_sum = 0;

	if (i != (unsigned short)cksum(mp, hlen)) {     /* verify checksum */
		netstat.ip_badsum++;
		if (!nosum) {
			netlog(mp);
        		return;
		}
	}

	ip_from_net(ip);	/* byte-swap header */

	netcb.n_ip_lock++;      /* lock frag reass.q */
	fp = ip_findf(ip);      /* look for chain on reass.q with this hdr */

	/* adjust message length to remove any padding */

	for (i=0, m=mp; m != NULL; m = m->m_next) {
		i += m->m_len;
		n = m;
	}
	i -= ip->ip_len;

	if (i != 0) 
        	if (i > (int)n->m_len)
        		m_adj(mp, -i);
        	else
        		n->m_len -= i;

	ip->ip_len -= hlen;     /* length of data */
  	ip->ip_mff = ((ip->ip_off & ip_mf) ? TRUE : FALSE);                
	ip->ip_off <<= 3;                                                 
	if (!ip->ip_mff && ip->ip_off == 0) {    /* not fragmented */

		if (fp != NULL) {               /* free existing reass.q chain */
		        
			q = fp->iqx.ip_next;    /* free mbufs assoc. w/chain */
			while (q != (struct ip *)fp) {
				p = q->ip_next;
				m_freem(dtom(q));
				q = p;
			}
			ip_freef(fp);           /* free header */
		} 
		netcb.n_ip_lock = 0;

		if (hlen > sizeof(struct ip))
			ip_opt(ip, hlen);	/* option processing */
		i = ip->ip_p;

		/* pass to next level */

		if (i == TCPROTO)
			tcp_input(mp, ifp);
		else if (i == GGPROTO)
			ggp(mp, ifp);
		else if (i == ICMPROTO)
			icmp(mp, ifp);
		else
			raw_input(mp, ifp, &ip->ip_src, &ip->ip_dst, i, 
				  UIP, FALSE);

	} else {                       

        	/* -> msg buf beyond ip hdr if not first fragment */

		if (ip->ip_off != 0) {
                	mp->m_off += hlen;
                	mp->m_len -= hlen;
		}

		if (fp == NULL) {               /* first fragment of datagram in */

		/* set up reass.q header: enq it, set up as head of frag
		   chain, set a timer value, and move in ip header */

			if ((m = m_get(1)) == NULL) {   /* allocate an mbuf */
				m_freem(mp);
				return;
			}

			fp = (struct ipq *)((int)m + MHEAD);
			fp->iqx.ip_next = fp->iqx.ip_prev = (struct ip *)fp; 
			bcopy(ip, &fp->iqh, 
			      MIN(MLEN-(sizeof(struct ipq)-sizeof(struct ip)), 
			    		hlen));
			fp->iqx.ip_ttl = MAXTTL;
			fp->iq_next = NULL;                   
			fp->iq_prev = netcb.n_ip_tail;
			if (netcb.n_ip_head != NULL) 
				netcb.n_ip_tail->iq_next = fp;
			else 
				netcb.n_ip_head = fp;
			netcb.n_ip_tail = fp;
		} 

		/*
                 * Merge fragment into reass.q
		 *
                 * Algorithm:   Match  start  and  end  bytes  of new
                 * fragment  with  fragments  on  the  queue.   If no
                 * overlaps  are  found,  add  new  frag. to the queue.
                 * Otherwise, adjust start and end of new frag.  so  no
                 * overlap   and   add  remainder  to  queue.   If  any
                 * fragments are completely covered by the new one,  or
                 * if  the  new  one is completely duplicated, free the
                 * fragments.
                 */	              
	        q = fp->iqx.ip_next;    /* -> top of reass. chain */
		ip->ip_end = ip->ip_off + ip->ip_len - 1;

		/* skip frags which new doesn't overlap at end */

		while ((q != (struct ip *)fp) && (ip->ip_off > q->ip_end))
			q = q->ip_next;

		if (q == (struct ip *)fp)	/* frag at end of chain */
			ip_enq(ip, fp->iqx.ip_prev);
		else { 				/* frag doesn't overlap any */ 
			if (ip->ip_end < q->ip_off)     
				ip_enq(ip, q->ip_prev);

			/* new overlaps beginning of next frag only */

			else if (ip->ip_end < q->ip_end) {
				if ((i = ip->ip_end-q->ip_off+1) < ip->ip_len) {
        				ip->ip_len -= i;
        				ip->ip_end -= i;
					m_adj(mp, -i);
					ip_enq(ip, q->ip_prev);
				} else
					m_freem(mp);

			/* new overlaps end of previous frag */

			} else {
				savq = q;
				if (ip->ip_off <= q->ip_off) {  

					/* complete cover */

					savq = q->ip_prev;
					ip_deq(q);
					m_freem(dtom(q));

				} else {	/* overlap */
					if ((i = q->ip_end-ip->ip_off+1) 
								< ip->ip_len) {
        					ip->ip_off += i;  
        					ip->ip_len -= i;  
        					m_adj(mp, i);
					} else
						ip->ip_len = 0;
				}

			/* new overlaps at beginning of successor frags */

				q = savq->ip_next;
				while ((q != (struct ip *)fp) &&
					(ip->ip_len != 0) && 
					(q->ip_off < ip->ip_end)) 

					/* complete cover */

					if (q->ip_end <= ip->ip_end) {
						p = q->ip_next;
						ip_deq(q);
						m_freem(dtom(q));
						q = p;

					} else {        /* overlap */
						if ((i = ip->ip_end-q->ip_off+1) 
								< ip->ip_len) {
        						ip->ip_len -= i;
        						ip->ip_end -= i;
        						m_adj(mp, -i);
						} else
							ip->ip_len = 0;
						break;
					}

			/* enqueue whatever is left of new before successors */

				if (ip->ip_len != 0)
					ip_enq(ip, savq);
				else
					m_freem(mp);
			}
		}

		/* check for completed fragment reassembly */

		if ((i = ip_done(fp)) > 0) {

			p = fp->iqx.ip_next;            /* -> top mbuf */
			m = dtom(p);
			p->ip_len = i;                  /* total data length */

			/* option processing */

			if ((hlen = p->ip_hl<<2) > sizeof(struct ip))
				ip_opt(p, hlen);       

			ip_mergef(fp);                  /* clean frag chain */

			/* copy src/dst internet address to header mbuf */

			bcopy(&fp->iqh.ip_src, &p->ip_src, 
					2*sizeof(struct socket));
			ip_freef(fp);                   /* dequeue header */
			netcb.n_ip_lock = 0;

			/* call next level with completed datagram */

			i = p->ip_p;
			if (i == TCPROTO)
				tcp_input(m, ifp);
			else if (i == GGPROTO)
				ggp(m, ifp);
			else if (i == ICMPROTO)
				icmp(m, ifp);
        		else
				raw_input(m, ifp, &p->ip_src, &p->ip_dst, i, 
					  UIP, FALSE);
		} else
			netcb.n_ip_lock = 0;
	}
}

/*
 * Check to see if fragment reassembly is complete
 */
ip_done(p)      
register struct ip *p;
{
	register struct ip *q;
	register next;

	q = p->ip_next;                       

	if (q->ip_off != 0)
		return(0);
	do {
		next = q->ip_end + 1;
		q = q->ip_next;
	} while ((q != p) && (q->ip_off == next));

	if ((q == p) && !(q->ip_prev->ip_mff))        /* all fragments in */
		return(next);                         /* total data length */
	else
		return(0);
}

/*
 * Merge mbufs of fragments of completed datagram 
 */
ip_mergef(p)    
register struct ip *p;
{
	register struct mbuf *m, *n;
	register struct ip *q;
	int dummy;

	q = p->ip_next;                         /* -> bottom of reass chain */
	n = (struct mbuf *)&dummy;              /* dummy for init assignment */

	while (q != p) {        /* through chain */

	        n->m_next = m = dtom(q);
		while (m != NULL) 
			if (m->m_len != 0) {
				n = m;
				m = m->m_next;
			} else                  /* free null mbufs */
				n->m_next = m = m_free(m);
		q = q->ip_next;
	}
}

/*
 * Dequeue and free reass.q header 
 */
ip_freef(fp)	        
register struct ipq *fp;
{
	if (fp->iq_prev != NULL)                               
		(fp->iq_prev)->iq_next = fp->iq_next;  
	else                                           
		netcb.n_ip_head = fp->iq_next;         
	if (fp->iq_next != NULL)                               
		(fp->iq_next)->iq_prev = fp->iq_prev;  
	else
		netcb.n_ip_tail = fp->iq_prev;
	m_free(dtom(fp));
}

/*
 * Does fragment reassembly chain with this header exist? 
 */
struct ipq *ip_findf(p)         
register struct ip *p;
{
	register struct ipq *fp;

	for (fp = netcb.n_ip_head; (fp != NULL && (
			p->ip_src.s_addr != fp->iqh.ip_src.s_addr ||
			p->ip_dst.s_addr != fp->iqh.ip_dst.s_addr ||
			p->ip_id != fp->iqh.ip_id ||
			p->ip_p != fp->iqh.ip_p)); fp = fp->iq_next);
	return(fp);
}

/*
 * Process ip options 
 */
ip_opt(ip, hlen)        
struct ip *ip;
int hlen;
{
	register char *p, *q;
	register i, len;
	register struct mbuf *m;

	p = q = (char *)((int)ip + sizeof(struct ip));  /* -> at options */
	
	if ((i = hlen - sizeof(struct ip)) > 0) {       /* any options */

/*      *** IP OPTION PROCESSING ***

		while (i > 0)

  			switch (*q++) {
			case 0:                 
			case 1:                 
				i--;
				break;

			default:
				i -= *q;
				q += *q;
			}
*/              q += i;
		m = dtom(q);
		len = (int)m + m->m_off + m->m_len - (int)q;
		bcopy((caddr_t)q, (caddr_t)p, len);    /* remove options */
		m->m_len -= i;
	}
}

/*
 * Enqueue a fragment on the reassembly chain.
 */
ip_enq(p, prev)
register struct ip *p;
register struct ip *prev;
{
	p->ip_prev = prev;
	p->ip_next = prev->ip_next;
	prev->ip_next->ip_prev = p;
	prev->ip_next = p;
}
 
/*
 * Dequeue a fragment from reassembly chain.
 */
ip_deq(p)
register struct ip *p;
{
	p->ip_prev->ip_next = p->ip_next;
	p->ip_next->ip_prev = p->ip_prev;
}

/*
 * Take the non-byte data in the ip header as it comes in from the net
 * and convert it to the form used internally (i.e., either byte swap
 * or squeeze).
 */
ip_from_net(p)    
register struct ip *p;
{

	p->ip_len = short_from_net(p->ip_len);
	p->ip_id = short_from_net(p->ip_id);
	p->ip_off = short_from_net(p->ip_off);
}

/*
 * IP fragment reassembly timeout routine.
 */
ip_timeo()      
{
	register struct ip *p, *q;
	register struct ipq *fp;

	timeout(ip_timeo, 0, HZ);       /* reschedule every second */

	if (netcb.n_ip_lock)    /* reass.q must not be in use */
		return;

	/* search through reass.q */

	for (fp = netcb.n_ip_head; fp != NULL; fp = fp->iq_next) 

		if (--(fp->iqx.ip_ttl) == 0) {  /* time to die */

			q = fp->iqx.ip_next;    /* free mbufs assoc. w/chain */
			while (q != (struct ip *)fp) {                         
				p = q->ip_next;                                
				m_freem(dtom(q));   
				q = p;                                
			}                                                      
			ip_freef(fp);           /* free header */              
		}
}