BBN-Vax-TCP/bbnnet/tcp_procs.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/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"
#include "../bbnnet/tcp_pred.h"

/*
 * TCP finite state machine procedures.
 *
 * Called from finite state machine action routines, these do most of the work
 * of the protocol.  They in turn call primitive routines (in tcp_prim) to
 * perform lower level functions.
 */

/*
 * Set up a TCB for a connection
 */
t_open(tp, mode)                
register struct tcb *tp;
int mode;
{
	register struct ucb *up;

	/* enqueue the tcb */

	if (netcb.n_tcb_head == NULL) {
		netcb.n_tcb_head = tp;
		netcb.n_tcb_tail = tp;
	} else {
		tp->t_tcb_next = netcb.n_tcb_head;
		netcb.n_tcb_head->t_tcb_prev = tp;
		netcb.n_tcb_head = tp;
	}

	/* initialize non-zero tcb fields */

	tp->t_rcv_next = (struct th *)tp;
	tp->t_rcv_prev = (struct th *)tp;
	tp->t_xmtime = T_REXMT;
	tp->t_maxseg = TCPMAXSND;
	tp->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi = 
				tp->snd_una = tp->iss = netcb.n_iss;
	netcb.n_iss += (ISSINCR >> 1) + 1;

	/* set timeout for open */

	up = tp->t_ucb;           
	tp->t_init = (up->uc_timeo != 0 ? up->uc_timeo : 
					(mode == ACTIVE ? T_INIT : 0));
	up->uc_timeo = 0;       /* overlays uc_ssize */
}

/*
 * Delete TCB and free all resources used by the connection.  Called after
 * the close protocol is complete.
 */
t_close(tp, state)
register struct tcb *tp;
short state;
{
	register struct ucb *up;
	register struct th *t;
	register struct mbuf *m;
	register struct work *w;

	up = tp->t_ucb;

	/* cancel all timers */

	tp->t_init = 0;
	tp->t_rexmt = 0;
	tp->t_rexmttl = 0;
	tp->t_persist = 0;
	tp->t_finack = 0;

	/* remove all work entries for tcb */

	for (w = netcb.n_work; w != NULL; w = w->w_next)
		if (w->w_tcb == tp)
			w->w_type = INOP;

	/* delete tcb */

	if (tp->t_tcb_prev == NULL)
		netcb.n_tcb_head = tp->t_tcb_next;
	else
		tp->t_tcb_prev->t_tcb_next = tp->t_tcb_next;
	if (tp->t_tcb_next == NULL)
		netcb.n_tcb_tail = tp->t_tcb_prev;
	else
		tp->t_tcb_next->t_tcb_prev = tp->t_tcb_prev;

	/* free all data on receive and send buffers */

	for (t = tp->t_rcv_next; t != (struct th *)tp; t = t->t_next)
		m_freem(dtom(t));

	if (up->uc_rbuf != NULL) {
		m_freem(up->uc_rbuf);
		up->uc_rbuf = NULL;
	}

	if (up->uc_sbuf != NULL) {
		m_freem(up->uc_sbuf);
		up->uc_sbuf = NULL;
	}

	for (m = tp->t_rcv_unack; m != NULL; m = m->m_act) {
		m_freem(m);
		tp->t_rcv_unack = NULL;
	}

	/* free tcb buffer */

	m_free(dtom(tp));
	up->uc_tcb = NULL;

	/* lower buffer allocation and decrement host entry */

	netcb.n_lowat -= up->uc_snd + up->uc_rcv + 2;
	netcb.n_hiwat = 2 * netcb.n_lowat;
	if (up->uc_route != NULL) {
		h_free(up->uc_route);
		up->uc_route = NULL;
	}

	/* if user has initiated close (via close call), delete ucb
	   entry, otherwise just wakeup so user can issue close call */

	if (tp->usr_abort) 
        	up->uc_proc = NULL;
	else
        	to_user(up, state);

}

/*
 * Accept data for the user to receive.  Moves data from sequenced tcp
 * segments from the sequencing queue to the user's receive queue (in the
 * ucb).  Observes locking on receive queue.
 */
present_data(tp)                
register struct tcb *tp;
{
	register struct th *t;
	register struct ucb *up;
	register struct mbuf *m, *n, *top, *bot;
	register sequence ready;

	up = tp->t_ucb;                 /* -> ucb */

	/* connection must be synced and data available for user */

	if (tp->syn_acked && (t = tp->t_rcv_next) != (struct th *)tp) {

		ready = firstempty(tp);     /* seq # of last complete datum */

		/* lock the user's receive buffer */

		while (up->uc_flags & ULOCK)
			sleep(&up->uc_flags, PZERO);
		up->uc_flags |= ULOCK;

		/* find the end of the user's receive buffer */

		m = up->uc_rbuf;
		if (m != NULL)
			while (m->m_next != NULL)
				m = m->m_next;

		/* move as many mbufs as possible from tcb to user queue */

		while (up->uc_rsize < up->uc_rcv && t != (struct th *) tp && 
			t_end(t) < ready) {

			/* count mbufs in msg chunk and free null ones */

			bot = NULL;
			for (n = top = dtom(t); n != NULL;) { 
				if (n->m_len == 0) {
					if (n == top)  
						top = n = m_free(n);
					else
						bot->m_next = n = m_free(n);
				} else {
					bot = n;
					up->uc_rsize++;
					n = n->m_next;
				}
			}

			/* chain new data to user receive buf */

			if (m == NULL)
				up->uc_rbuf = top;
			else 
				m->m_next = top;

			if (bot != NULL)
        			m = bot;

			/* dequeue chunk from tcb */

			tcp_deq(t);
			t = t->t_next;
		}

		/* unlock receive queue for user */

		up->uc_flags &= ~ULOCK;
		wakeup(&up->uc_flags);

		/* awaken reader only if any data on user rcv queue */

		if (up->uc_rsize != 0)
			wakeup(up);

		/* let user know about foreign tcp close if no more data */

		if (tp->fin_rcvd && !tp->usr_closed && rcv_empty(tp))
			to_user(up, UCLOSED);  
	}
}

/*
 * Process incoming ACKs.  Remove data from send queue up to acknowledgement.
 * Also handles round-trip timer for retransmissions and acknowledgement of
 * SYN.
 */
rcv_ack(tp, n)          
register struct tcb *tp;
register struct th *n;
{
	register struct ucb *up;
	register struct mbuf *m;
	register len;

	up = tp->t_ucb;

	len = n->t_ackno - tp->snd_una; 
	tp->snd_una = n->t_ackno;
	if (tp->snd_una > tp->snd_nxt) 
		tp->snd_nxt = tp->snd_una;

	/* if timed message has been acknowledged, use the time to set
	   the retransmission time value */

	if (tp->syn_acked && tp->snd_una > tp->t_xmt_val) {
		tp->t_xmtime = (tp->t_xmt != 0 ? tp->t_xmt : T_REXMT);
		if (tp->t_xmtime > T_REMAX)
			tp->t_xmtime = T_REMAX;
	}

	/* handle ack of opening syn (tell user) */

	if (!tp->syn_acked && (tp->snd_una > tp->iss)) {
		tp->syn_acked = TRUE;
		len--;			/* ignore SYN */
		t_cancel(tp, TINIT);	/* cancel init timer */
	}

	/* remove acknowledged data from send buff */

	m = up->uc_sbuf;
	while (len > 0 && m != NULL) 
		if (m->m_len <= len) {
			len -= m->m_len;
			m = m_free(m);
			up->uc_ssize--;
		} else {
			m->m_len -= len;
			m->m_off += len;
			break;
		}

	up->uc_sbuf = m;
	wakeup(tp->t_ucb);

	/* handle ack of closing fin */

	if (tp->seq_fin != tp->iss && tp->snd_una > tp->seq_fin)
		tp->snd_fin = FALSE;
	t_cancel(tp, TREXMT);          /* cancel retransmit timer */
	t_cancel(tp, TREXMTTL);        /* cancel retransmit too long timer */
	tp->cancelled = TRUE;      
}

/* 
 * Process incoming control packets (no data expected)
 */
rcv_ctl(tp, n)                  
register struct tcb *tp;
register struct th *n;
{
	register sent;

	tp->dropped_txt = FALSE;
	tp->ack_due = FALSE;
	tp->new_window = FALSE;

	/* process control fields of incoming segment */

	if (!tp->syn_rcvd && n->t_syn)
        	rcv_syn(tp, n);

	if (n->t_ack && tp->syn_rcvd && n->t_ackno > tp->snd_una)
        	rcv_ack(tp, n);

	if (tp->syn_rcvd && n->t_seq >= tp->snd_wl)
        	rcv_window(tp, n);

	if (n->t_fin && !tp->dropped_txt)
        	rcv_fin(tp);

	/* if ACK required or rcv window has changed, try to send something */

	sent = FALSE;
	if (tp->ack_due)                
		sent = send_ctl(tp);
	else if (tp->new_window)       
		sent = send(tp);
		
	/*  set up for retransmission, if necessary */

	if (!sent && tp->snd_una < tp->snd_nxt && tp->cancelled) {

		tp->t_rexmt = tp->t_xmtime;
		tp->t_rexmttl = T_REXMTTL;
		tp->t_rexmt_val = tp->t_rtl_val = tp->snd_lst;
		tp->cancelled = FALSE;
	}
}

/*
 * Process incoming segments and data
 */
rcv_data(tp, n)                 
register struct tcb *tp;
register struct th *n;
{
	register sent;

	tp->dropped_txt = FALSE;
	tp->ack_due = FALSE;
	tp->new_window = FALSE;

	/* process control and data fields of incoming segment */

	if (!tp->syn_rcvd && n->t_syn)
        	rcv_syn(tp, n);

	if (n->t_ack && tp->syn_rcvd && n->t_ackno > tp->snd_una)
        	rcv_ack(tp, n);

	if (tp->syn_rcvd && n->t_seq >= tp->snd_wl)
        	rcv_window(tp, n);

	if (n->t_len != 0)
        	rcv_text(tp, n);

	if (n->t_urg)
        	rcv_urgent(tp, n);

	if (n->t_eol && !tp->dropped_txt)
        	rcv_eol(tp);

	if (n->t_fin && !tp->dropped_txt)
        	rcv_fin(tp);

	/* if ACK required or rcv window has changed, try to send something */

	sent = FALSE;
	if (tp->ack_due)                
		sent = send_ctl(tp);
	else if (tp->new_window)       
		sent = send(tp);
		
	/*  set up for retransmission, if necessary */

	if (!sent && tp->snd_una < tp->snd_nxt && tp->cancelled) {

		tp->t_rexmt = tp->t_xmtime;
		tp->t_rexmttl = T_REXMTTL;
		tp->t_rexmt_val = tp->t_rtl_val = tp->snd_lst;
		tp->cancelled = FALSE;
	}
}

/*
 * Process segment urgent field
 */
rcv_urgent(tp, n)            
register struct tcb *tp;
register struct th *n;
{           
	register sequence urgent;

	urgent = n->t_urp + n->t_seq;

	/* new urgent pointer received */

	if (tp->rcv_nxt < urgent) {

		/* has user been told about this urgent data yet? */

		if (tp->rcv_urp <= tp->rcv_nxt)
        		to_user(tp->t_ucb, UURGENT);

		tp->rcv_urp = urgent;
	}
}

/*
 * Process segment EOL field.  EOL mark is maintained in extra mbuf pointer
 * field and picked up in tcpread.  Remember, TCP EOL is really a "push", and
 * may not preserve letter boundaries, so it's virtually useless. 
 */
rcv_eol(tp)                  
register struct tcb *tp;
{
	register struct mbuf *m;

	if (tp->t_rcv_prev != (struct th *)tp) {

		/* find the last mbuf on received data chain and mark */

	        m = dtom(tp->t_rcv_prev);

        	if (m != NULL) {
        		while (m->m_next != NULL)
				m = m->m_next;
			m->m_act = (struct mbuf *)(m->m_off + m->m_len - 1);
		}
	}
}

/*
 * Process FIN field.  Make sure we don't process twice and that all previous
 * data has been received.
 */
rcv_fin(tp)                     
register struct tcb *tp;
{
	register sequence last;

	if (!tp->fin_rcvd) {

        	/* check if we really have FIN (rcv buf filled in, no drops */
        
        	last = firstempty(tp);
        	if (tp->t_rcv_prev == (struct th *)tp || 
			last == t_end(tp->t_rcv_prev)) {
        		tp->fin_rcvd = TRUE;

			/* wakeup user in case he was waiting for more data */

			wakeup(tp->t_ucb);
		}
        
        	/* if FIN, then set to ACK: incr rcv_nxt, since FIN 
		   occupies sequence space */
        
        	if (tp->fin_rcvd && tp->rcv_nxt >= last) {
        		tp->rcv_nxt = last + 1;
        		tp->ack_due = TRUE;
        	}
	} else
		tp->ack_due = TRUE;

}

/*
 * Process SYN field.  
 */
rcv_syn(tp, n)             
register struct tcb *tp;
register struct th *n;
{

	tp->irs = n->t_seq;
	tp->rcv_nxt = n->t_seq + 1;
	tp->snd_wl = tp->rcv_urp = tp->irs;
	tp->syn_rcvd = TRUE;
	tp->ack_due = TRUE;
}

/*
 * Process incoming data.  Put the segments on sequencing queue in order,
 * taking care of overlaps and duplicates.  Data is removed from sequence
 * queue by present_data when sequence is complete (no holes at top).
 * Drop data that falls outside buffer quota if tight for space.  Otherwise,
 * process and recycle data held in tcp_input.
 */
rcv_text(tp, t)                 
register struct tcb *tp;
register struct th *t;
{
	register i;
	register struct th *p, *q;
	register struct mbuf *m, *n;
	struct th *savq;
	int j;
	sequence last;

	/* throw away any data we have already received */

	if ((i = tp->rcv_nxt - t->t_seq) > 0)  {
		if (i < t->t_len) {
        		t->t_seq += i;
        		t->t_len -= i;
        		m_adj(dtom(t), i);
		} else {
			tp->ack_due = TRUE;	/* send ack just in case */
			return;
		}
	}

	last = t_end(t);                /* last seq # in incoming seg */
	i = rcv_resource(tp);           /* # buffers available to con */
	        
	/* count buffers in segment */
	        
	for (m = dtom(t), j = 0; m != NULL; m = m->m_next)
		if (m->m_len != 0)
        		j++;
	        
	/* not enough resources to process segment */

	if (j > i && netcb.n_bufs < netcb.n_lowat) {            

		/* if segment preceeds top of seqeuncing queue, try to take
		   buffers from bottom of queue */

                q = tp->t_rcv_next;             
		if (q != (struct th *)tp && tp->rcv_nxt < q->t_seq &&
		    t->t_seq < q->t_seq) 

			for (p = tp->t_rcv_prev; i < j &&
			     p != (struct th *)tp;) {
				savq = p->t_prev;
				tcp_deq(p);
				i += m_freem(dtom(p));
				p = savq;
			}

		/* if still not enough room, drop text from end of segment */

		if (j > i) {

			for (m = dtom(t); i > 0 && m != NULL; i--)
				m = m->m_next;

        		while (m != NULL) {
        			t->t_len -= m->m_len;
        			last -= m->m_len;
        			m->m_len = 0;
        			m = m->m_next;
        		}
        		tp->dropped_txt = TRUE;
        		if (last < t->t_seq)
        			return;
        	}       
	}

	/* merge incoming data into the sequence queue */

        q = tp->t_rcv_next;             /* -> top of sequencing queue */
	        
        /* skip frags which new doesn't overlap at end */
                
        while ((q != (struct th *)tp) && (t->t_seq > t_end(q)))
        	q = q->t_next;
                
        if (q == (struct th *)tp) {     /* frag at end of chain */

		if (last >= tp->rcv_nxt) {
		        tp->net_keep = TRUE;
        	        tcp_enq(t, tp->t_rcv_prev);
		}

        } else {  

		/* frag doesn't overlap any on chain */

        	if (last < q->t_seq) {  
			tp->net_keep = TRUE;
        		tcp_enq(t, q->t_prev);
                
        	/* new overlaps beginning of next frag only */
                
        	} else if (last < t_end(q)) {
        		if ((i = last - q->t_seq + 1) < t->t_len) {
                		t->t_len -= i;
        			m_adj(dtom(t), -i);
				tp->net_keep = TRUE;
        			tcp_enq(t, q->t_prev);
        		} 
                
        	/* new overlaps end of previous frag */
                
        	} else {
        		savq = q;
        		if (t->t_seq <= q->t_seq) {     /* complete cover */
        			savq = q->t_prev;
        			tcp_deq(q);
        			m_freem(dtom(q));
        		  
        		} else {                        /* overlap */
        			if ((i = t_end(q) - t->t_seq + 1) < t->t_len) {
                			t->t_seq += i;  
                			t->t_len -= i;  
                			m_adj(dtom(t), i);
				} else 
					t->t_len = 0;
        		}
                
        	/* new overlaps at beginning of successor frags */
                
        		q = savq->t_next;
        		while ((q != (struct th *)tp) && (t->t_len != 0) && 
        			(q->t_seq < last))
                
        			/* complete cover */
                
        			if (t_end(q) <= last) {
        				p = q->t_next;
        				tcp_deq(q);
        				m_freem(dtom(q));
        				q = p;
                
        			} else {        /* overlap */
                
        				if ((i = last-q->t_seq+1) < t->t_len) {
                				t->t_len -= i;
                				m_adj(dtom(t), -i);
					} else
						t->t_len = 0;
        				break;
        			}
                
        	/* enqueue whatever is left of new before successors */
                
        		if (t->t_len != 0) {
				tp->net_keep = TRUE;
        			tcp_enq(t, savq);
			}
        	}
        }

	/* set to ack completed data (no gaps) */

	tp->rcv_nxt = firstempty(tp);
	tp->ack_due = TRUE;

	/* if any room remaining in rcv buf, take any unprocessed
	   messages and schedule for later processing */

	i = rcv_resource(tp);

	while ((m = tp->t_rcv_unack) != NULL && i > 0) {        
        
		/* schedule work request */

		t = (struct th *)((int)m + m->m_off);
		j = (t->t_off << 2) + sizeof(struct ip);
		m->m_off += j;
		m->m_len -= j;
		tp->t_rcv_unack = m->m_act;
		m->m_act = (struct mbuf *)0;
		netstat.t_unack++;
		w_alloc(INRECV, 0, tp, t);     

		/* remaining buffer space */

		for (n = m; n != NULL; n = n->m_next)
			i--;
	}
}

/*
 * Process incoming segment window field
 */
rcv_window(tp, n)               
register struct tcb *tp;
register struct th *n;
{

	/* record new window parameters */

	tp->snd_wl = n->t_seq;
	tp->snd_wnd = n->t_win;
	tp->new_window = TRUE;
	t_cancel(tp, TPERSIST);         /* cancel persist timer */  
}

/*
 * Send a TCP segment.  Send data from left window edge of send buffer up to
 * window size or end (whichever is less).  Set retransmission timers.
 */
send(tp)                        
register struct tcb *tp;
{
	register struct ucb *up;
	register sequence last, wind;
	struct mbuf *m;
	int snd_eol, do_snd_fin, forced, snd_syn, sent;
	struct mbuf *snd_copy();
	int len;

	up = tp->t_ucb;
	snd_eol = FALSE;
	tp->snd_lst = tp->snd_nxt;
	do_snd_fin = FALSE;
	forced = FALSE;
	snd_syn = FALSE;
	m = NULL;

	if (tp->snd_nxt == tp->iss) {           /* first data to be sent */
		snd_syn = TRUE;                 /* make sure to send SYN */
		tp->snd_lst++;
	} 

	/* get seq # of last datum in send buffer */

	last = tp->snd_una;
	if (!tp->syn_acked)
		last++;				/* don't forget SYN */
	for (m = up->uc_sbuf; m != NULL; m = m->m_next) 
		last += m->m_len;

        /* no data to send in buffer */

	if (tp->snd_nxt > last) {

		/* should send FIN?  don't unless haven't already sent one */

		if (tp->snd_fin && 
		    (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) {
			do_snd_fin = TRUE;
			tp->seq_fin = tp->snd_lst++;
		}

	/* send data only if there is any to send and a window is defined */

	} else if (tp->syn_acked) { 

		wind = tp->snd_una + tp->snd_wnd;

		/* use window to limit send */

		tp->snd_lst = MIN(last, wind);

		/* make sure we don't do ip fragmentation or send
		   more than peer can handle */

		if ((len = tp->snd_lst - tp->snd_nxt) > tp->t_maxseg) 
			tp->snd_lst -= len - tp->t_maxseg;

		/* set persist timer */

		if (tp->snd_lst >= wind)                       
			tp->t_persist = T_PERS;

		/* check if window is closed and must force a byte out */

		if (tp->force_one && tp->snd_lst == wind) {
			tp->snd_lst = tp->snd_nxt + 1;
			forced = TRUE;
		}

		/* copy data to send from send buffer */

		m = snd_copy(tp, MAX(tp->iss+1,tp->snd_nxt), tp->snd_lst);

		/* see if EOL should be sent */

		if (tp->snd_end > tp->iss && tp->snd_end <= tp->snd_lst) 
			snd_eol = TRUE;

		/* must send FIN and no more data left to send after this */

		if (tp->snd_fin && !forced && tp->snd_lst == last &&
		    (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) {

			do_snd_fin = TRUE;
			tp->seq_fin = tp->snd_lst++;
		}
	}

	if (tp->snd_nxt < tp->snd_lst) {        /* something to send */

		sent = send_tcp(tp, do_snd_fin, 
				snd_syn, 
				snd_eol,
				tp->snd_urg, 
				tp->snd_urp,
				tp->snd_lst - tp->snd_nxt, 
				m);  

		/* set timers for retransmission if necessary */

		if (!forced) {
			tp->t_rexmt = tp->t_xmtime;
			tp->t_rexmt_val = tp->snd_lst;

			if (!tp->rexmt) {
				tp->t_rexmttl = T_REXMTTL;
				tp->t_rtl_val = tp->snd_lst;
			}

		}

		/* update seq for next send if this one got out */

		if (sent)
			tp->snd_nxt = tp->snd_lst;                


		/* if last timed message has been acked, start timing
		   this one */

		if (tp->syn_acked && tp->snd_una > tp->t_xmt_val) {
			tp->t_xmt = 0;
			tp->t_xmt_val = tp->snd_lst;
		}

		tp->ack_due = FALSE;
		tp->snd_hi = MAX(tp->snd_nxt, tp->snd_hi);
		tp->rexmt = FALSE;
		tp->force_one = FALSE;
		return(TRUE);
	}

	return(FALSE);
}

/*
 * Send a control message.  Try to send any data on the send buffer.  If
 * there isn't any, just send a control segment only.
 */
send_ctl(tp)                            
struct tcb *tp;
{
        if (!send(tp)) { 
		send_null(tp);
		return(FALSE);
	}
	return(TRUE);
}

/*
 * Send only a control header (for ACKs with no data).
 */
send_null(tp)                           
register struct tcb *tp;
{
	send_tcp(tp, FALSE, FALSE, FALSE, FALSE, 0, 0, NULL);
        tp->ack_due = FALSE;    
}

/*
 * Send a reset segment
 */
send_rst(tp, n)                         
register struct tcb *tp;
register struct th *n;
{
        /* don't send a reset in response to a reset */

	if (n->t_rst)                   
		return;

	tp->snd_rst = TRUE;

	if (n->t_ack)
		tp->snd_nxt = n->t_ackno;

	tp->syn_rcvd = FALSE;
	send_null(tp);
	tp->snd_rst = FALSE;
}