BBN-Vax-TCP/bbnnet/tcp_input.c

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

#include "../h/param.h"
#include "../h/systm.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/inode.h"
#include "../bbnnet/fsm.h"
#include "../bbnnet/fsmdef.h"

extern int nosum;

/*
 * This is the scheduler for the tcp machine.  It is called
 * from the lower network levels, either directly from the
 * internet level, in case of input from the network; or
 * indirectly from netmain, in case of user or timer events 
 * which awaken the main loop.
 */
tcp_input(mp, ip)
struct mbuf *mp;
struct ifcb *ip;
{
	register struct tcb *t;
	register struct th *tp;
	register struct work *p;
	register struct mbuf *m;
	register i, hlen, tlen;
	unsigned short lport, fport;
	struct ucb *up;
	struct work w;
	struct socket temp;
	struct ucb tempucb;
	struct if_local tempif;

	if (mp != NULL) {       /* called directly from ip with a datagram */

		/* set up needed info from ip header, note that beginning
		   of tcp header struct overlaps ip header.  ip options
		   have been removed by ip level option processing */

		tp = (struct th *)((int)mp + mp->m_off);

		/* make sure header does not overflow mbuf */

		if ((hlen = tp->t_off << 2) > mp->m_len) {
			printf("tcp header overflow\n");
			netlog(mp);
			return;
		}
 
		tlen = ((struct ip *)tp)->ip_len;
		tp->t_len = short_to_net(tlen);                        
		tp->t_next = NULL;
		tp->t_prev = NULL;
		tp->t_x1 = 0;
		lport = short_from_net(tp->t_dst);
		fport = short_from_net(tp->t_src);

		/* do checksum calculation, drop seg if bad */

#ifndef mbb
		i = (unsigned short)tp->t_sum;
#else
		tp->t_x0 = 0;
		tp->t_x00 = 0;
		i = (unsigned short)short_from_net(tp->t_sum);
#endif mbb
		tp->t_sum = 0;

		if (i != (unsigned short)cksum(mp, tlen + sizeof(struct ip))) {
        		netstat.t_badsum++;
			if (nosum) 
				goto ignore;
        		netlog(mp);

		} else {                        
ignore:
        		/* find a tcb for incoming message */
        
        		t = netcb.n_tcb_head;
        		while (t != NULL) {     /* look for exact match */
        
        			if (t->t_lport == lport &&
        				t->t_fport == fport &&
        		                t->t_ucb->uc_host.s_addr == 
                                                          tp->t_s.s_addr)
        				break;
        			t = t->t_tcb_next;
        		}
        
        		if (t == NULL) {        /* look for wild card match */
        
        		        t = netcb.n_tcb_head;
        			while (t != NULL) {
        
        				if (t->t_lport == lport &&
        				    (t->t_fport == fport ||
        				     t->t_fport == 0) &&
        				     (t->t_ucb->uc_host.s_addr == 
							   tp->t_s.s_addr ||
        				      t->t_ucb->uc_host.s_addr == 0))
        		 		        break;
                			t = t->t_tcb_next;
        			}
        		}
        
        		if (t != NULL) {        /* found a tcp for message */

				/* byte swap header */

				tp->t_len = tlen - hlen;
				tp->t_src = fport;
				tp->t_dst = lport;
				tp->t_seq = long_from_net(tp->t_seq);
				tp->t_ackno = long_from_net(tp->t_ackno);
				tp->t_win = short_from_net(tp->t_win);
				tp->t_urp = short_from_net(tp->t_urp);

				/* do TCP option processing */

				if (hlen > TCPSIZE)
					tcp_opt(t, tp, hlen);

			        /* check seg seq #, do RST processing */

				if ((i = netprepr(t, tp)) != 0) {

				/* seg failed preproc, drop it and
				   possibly enter new state */

					m_freem(mp);
					if (i != -1)
						t->t_state = i;
				} else {

			                /* put msg on unack queue if user 
					   rcv queue is full and there is
					   not a reasonable number of free
					   buffers available. */
			                
					up = t->t_ucb;
			                if ((int)up->uc_rcv - (int)up->uc_rsize <= 0
					     && tp->t_len != 0
					     && netcb.n_bufs < netcb.n_lowat) {
			                
			                	mp->m_act = (struct mbuf *)0;
			                	if ((m = t->t_rcv_unack) != NULL) {
			                		while (m->m_act != NULL)
			                			m = m->m_act;
			                		m->m_act = mp;
			                	} else
			                		t->t_rcv_unack = mp;
			                
			                } else {

					/* set up work entry for seg, and call
					   the fsm to process it */

					        hlen += sizeof(struct ip);
					        mp->m_off += hlen;
					        mp->m_len -= hlen;
                        			w.w_tcb = t;
                        			w.w_dat = (char *)tp;
                        			action(&w, INRECV);     
					}
				}

			} else {        /* nobody wants it */

        			/* free everything but the header */
        
        			m_freem(mp->m_next);
        			mp->m_next = NULL;
        			mp->m_len = sizeof(struct th);
        
        			/* form a reset from the packet and send */
        
        			temp = tp->t_d;
        			tp->t_d = tp->t_s;
        			tp->t_s = temp;
        			lport = tp->t_src;
        			tp->t_src = tp->t_dst;
        			tp->t_dst = lport;
        			if (tp->t_ack)
        				tp->t_seq = tp->t_ackno;
        			else {
        				tp->t_ackno = long_to_net(
						       long_from_net(tp->t_seq)
							+ tlen - hlen +
							(tp->t_syn ? 1 : 0));
        				tp->t_seq = 0;
        			}
        			tp->t_rst = TRUE;
        			tp->t_ack = (tp->t_ack ? FALSE : TRUE);
        			tp->t_syn = FALSE;
        			tp->t_fin = FALSE;
        			tp->t_len = short_to_net(TCPSIZE);
        			tp->t_off = TCPSIZE/4;
#ifndef mbb
				tp->t_sum = cksum(mp, sizeof(struct th));
#else
        			i = cksum(mp, sizeof(struct th));
				tp->t_sum = short_to_net(i);
#endif
				tempucb.uc_host = tp->t_d;
				tempucb.uc_route = NULL;
				tempucb.uc_srcif = &tempif;
				tempif.if_ifcb = ip;
        			ip_output(&tempucb, mp, TCPROTO, 
					  TCPSIZE, 0, NULL);
        			netstat.t_badsegs++;
			}
		}
	}

	/* now look through the work list for user or timer events */

	for (p = netcb.n_work; p != NULL; p = netcb.n_work) {

		if ((i = p->w_type) > 0 && i < INOP)
			action(p, i);

		spl5();
		p->w_type = 0;
		netcb.n_work = p->w_next;
		p->w_next = NULL;
		spl0();
	}
	wakeup(workfree);       /* in case anyone waiting for work entries */
}

/*
 * Entry into TCP finite state machine
 */
action(wp, input)                       
register struct work *wp;
int input;
{
	register act, newstate;
	register struct tcb *tp;

	tp = wp->w_tcb;

	/* get index of action routine from transition table */

	act = fstab[tp->t_state][input];         

	/* invalid state transition, just print a message and ignore */

	if (act == 0)  {
		printf("tcp: bad state: tcb=%X state=%d input=%d\n",
			tp, tp->t_state, input);
		if (wp->w_dat != NULL)
			m_freem(dtom(wp->w_dat));
		return;
	}

	tp->net_keep = FALSE;

	newstate = (*fsactab[act])(wp);         /* call action routine */

	/* print debugging info for this transition if debug flag on */

	if (tp->t_ucb->uc_flags & UDEBUG)
		tcp_debug(tp, wp, input, newstate);

#ifdef TCPDEBUG
	tcp_prt(tp, input, wp->w_stype, newstate);
#endif

	/* handle invalid states and state transition */

	if (newstate == EFAILEC) {
		if (wp->w_dat != NULL)
			m_freem(dtom(wp->w_dat));

	/* valid transition, see if we should free net input */

	} else {
		if (newstate != SAME) 
			tp->t_state = newstate;
		if (wp->w_dat != NULL && !tp->net_keep && input == INRECV)
			m_freem(dtom(wp->w_dat));
	}

}

/*
 * Allocate a work list entry
 */
w_alloc(type, stype, tp, m)     
register type;
int stype;
struct tcb *tp;
char *m;
{
	register struct work *w, *wp;
	register char *t;
	register x;

	/* look for a free work entry */

again:
	for (w = workfree; w < workNWORK; w++)
		if (w->w_type == 0) {

			/* found a free entry */

			x = spl5();
			w->w_type = type;
			w->w_stype = stype;
			w->w_tcb = tp;
			w->w_dat = m;

			/* chain work request at end of list */

         		w->w_next = NULL;
        		
        		if ((wp = netcb.n_work) != NULL) {
        			while (wp->w_next != NULL)       
        				wp = wp->w_next;
        			wp->w_next = w;
        		} else 
        			netcb.n_work = w;
			splx(x);

			/* wakeup net input process */

			wakeup((caddr_t)&ifcb[0]);
			return;
		}

	/* couldn't find a free entry: wait for one if request is from user */

	if (type != INRECV && type != ISTIMER && type != INCLEAR) {
		sleep(workfree, PZERO);
		goto again;

	/* if timer request, just reschedule a second later */

	} else if (type == ISTIMER) {
		t = (char *)((char *)&tp->t_init + stype - 1);
		*t++;

	/* otherwise, just drop the request, since it can only be
	   a net clear or an unacked queue recycle (regular net input
	   is done with a private work entry). */

	} else 
		if (m != NULL) 
			m_freem(dtom(m));       
}

/*
 * Write a record in the tcp debugging log
 */
tcp_debug(tp, wp, input, newstate)   
register struct tcb *tp;
register struct work *wp;
register input, newstate;
{
	struct t_debug debuf;
	register struct th *n;
	register struct inode *ip;
	register off_t siz;

	/* look for debugging file inode */

	if ((ip=netcb.n_debug)==NULL)
		return;
	plock(ip);

	/* set up the debugging record */

	debuf.t_tod = (long)time;
	if ((debuf.t_tcb = tp) != NULL)
		debuf.t_old = tp->t_state;
	else
		debuf.t_old = 0;
	debuf.t_inp = input;
	debuf.t_tim = wp->w_stype;
	debuf.t_new = newstate;

	if (input == INRECV) {
		n = (struct th *)wp->w_dat;
		debuf.t_sno = n->t_seq;
		debuf.t_ano = n->t_ackno;
		debuf.t_wno = n->t_win;
		if (debuf.t_new == 0xff)
	        	debuf.t_lno = ((struct ip *)n)->ip_len;
		else
			debuf.t_lno = n->t_len;
		debuf.t_flg = *(char *)((int)&n->t_win - 1);
	} 

	/* write out the record */

	siz = ip->i_size;
	u.u_offset = siz;
	u.u_base = (caddr_t)&debuf;
	u.u_count = sizeof(debuf);
	u.u_segflg = 1;
	u.u_error = 0;
	writei(ip);
	if(u.u_error)
		ip->i_size = siz;
	prele(ip);
}


#ifdef TCPDEBUG

/*
 * Print debugging info on the console
 */
tcp_prt(tp, input, timer, newstate)     
register struct tcb *tp;
register input, timer, newstate;
{
	register oldstate;

	oldstate = tp->t_state;

	printf("TCP(%X) %s X %s", tp, tcpstates[oldstate], tcpinputs[input]);

	if (input == ISTIMER)
		printf("(%s)", tcptimers[timer]);

	printf(" --> %s", tcpstates[ (newstate > 0) ? newstate : oldstate ]);

	if (newstate < 0)
		printf(" (FAILED)\n");
	else
		putchar('\n');
}

#endif