BBN-Vax-TCP/bbnnet-oct82/tcp_states.c

#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/imp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"

/*
 * These are the action routines of the TCP finite state machine.  They are
 * called from the TCP fsm dispatcher (in tcp_input.c).  These routines call 
 * on the routines in tcp_procs.c to do the actual segment processing.
 */
lis_cls(wp)             /* passive open (1) */
struct work *wp;
{
	t_open(wp->w_tcb, TCP_PASSIVE);
	return(LISTEN);
}

sys_cls(wp)             /* active open (6) */
register struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	t_open(tp, TCP_ACTIVE);
	send_tcp(tp, TCP_CTL);		/* send SYN */
	return(SYN_SENT);
}

cls_opn(wp)             /* close request before receiving foreign SYN (10) */
struct work *wp;                           
{
	t_close(wp->w_tcb, UCLOSED);
	return(CLOSED);
}

cl2_clw(wp)             /* close request after receiving foreign FIN (13) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	tp->snd_fin = TRUE;             /* send our own FIN */
	send_tcp(tp, TCP_CTL);                   
	tp->usr_closed = TRUE;
	return(CLOSING2);
}

cls_rwt(wp)             /* rcv request after foreign close (20) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	present_data(tp);       /* present any remaining data */
	if (rcv_empty(tp)) {
        	t_close(tp, UCLOSED);
         	return(CLOSED);
	} else
		return(RCV_WAIT);

}

fw1_syr(wp)             /* close request on synched connection (24,25) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	tp->snd_fin = TRUE;                     /* send FIN */
	send_tcp(tp, TCP_CTL);
	tp->usr_closed = TRUE;
	return(FIN_W1);
}

sss_syn(wp)             /* incoming seq on open connection (39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	rcv_tcp(tp, wp->w_dat, TCP_DATA);
	present_data(tp);
	return(SAME);
}

sss_snd(wp)             /* send request on open connection (40,41) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct ucb *up = tp->t_ucb;
	register struct mbuf *m, *n;
	register off;
	sequence last;

	last = tp->snd_una;

	/* count number of mbufs in send data */

	for (m = n = (struct mbuf *)wp->w_dat; m != NULL; m = m->m_next) {
		up->uc_ssize++;
		last += m->m_len;
	}

	/* find end of send buffer and append data */

	if ((m = up->uc_sbuf) != NULL) {        /* something in send buffer */
		while (m->m_next != NULL) {             /* find the end */
			m = m->m_next;
			last += m->m_len;
		}
		last += m->m_len;

		/* if there's room in old buffer for new data, consolidate */

		off = m->m_off + m->m_len;
		while (n != NULL && (MSIZE - off) >= n->m_len) {
			bcopy((caddr_t)((int)n + n->m_off), 
			      (caddr_t)((int)m + off), n->m_len);
			m->m_len += n->m_len;
			off += n->m_len;
			up->uc_ssize--;
			n = m_free(n);
		}
		m->m_next = n;
	} else                                  /* nothing in send buffer */
		up->uc_sbuf = n;

	if (up->uc_flags & UEOL) {		/* set PUSH */
		tp->snd_end = last;
	}
	if (up->uc_flags & UURG) {              /* urgent data */
		tp->snd_urp = last+1;
		tp->snd_urg = TRUE;
	} 
	send_tcp(tp, TCP_DATA);
	return(SAME);
}

sss_rcv(wp)             /* rcv request on open connection (42) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	present_data(tp);

	/* if last window sent was zero, send an ACK to update window */

	if (tp->sent_zero)
		send_tcp(tp, TCP_CTL);
	return(SAME);
}

cls_nsy(wp)                  /* abort request on unsynched connection (44) */
struct work *wp;
{
	t_close(wp->w_tcb, UABORT);
	return(CLOSED);
}

cls_syn(wp)             /* abort request on synched connection (45) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;

	tp->snd_rst = TRUE;            /* send reset */
	send_pkt(tp, 0, 0, NULL);
	tp->ack_due = FALSE;
	t_close(tp, UABORT);
	return(CLOSED);
}

cls_act(wp)             /* net closing open connection (47) */
struct work *wp;
{
	t_close(wp->w_tcb, UABORT);
	return(CLOSED);
}

cls_err(wp)             /* invalid user request in closing states */
struct work *wp;
{
	to_user(wp->w_tcb->t_ucb, UCLSERR);
	return(SAME);
}

lis_netr(wp)             /* incoming seg in LISTEN (3,4) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;
	register struct ucb *up = tp->t_ucb;
	register struct ifcb *ip;
	struct socket local;

	/* fill in unspecified foreign host address. */

	up->uc_host = n->t_s;
	tp->t_fport = n->t_src;
	up->uc_local = n->t_d;
	local.s_addr = addr_1822(n->t_d);

	/* pick a network interface to respond on */
	
	for (ip = netcb.n_ifcb_hd; ip != NULL; ip = ip->if_next) 
		if (ip->if_addr.s_addr == local.s_addr && ip->if_avail) {
			up->uc_srcif = ip;
			tp->t_maxseg = MIN(ip->if_mtu - TCPIPMAX, tp->t_maxseg);
			break;
		}

	/* in case we can't find a good i/f, close the connection,  foreign
	   host will get a reset on his next retransmission */

	if (ip == NULL) {
		t_close(tp, UNRCH);
		return(CLOSED);
	}
	rcv_tcp(tp, n, TCP_DATA);

	if (!tp->fin_rcvd) {            /* no FIN (4) */

		/* start init timer now that we have foreign host */

		tp->t_timers[TINIT] = T_INIT/2;
		return(L_SYN_RCVD);
	} else {                        /* got a FIN, start timer (3) */
        	tp->t_timers[TFINACK] = T_2ML;
        	tp->waited_2_ml = FALSE;
		return(CLOSE_WAIT);
	}
}

sys_netr(wp)            /* incoming segment after SYN sent (8,9,11,32) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	rcv_tcp(tp, n, TCP_DATA);
	if (tp->fin_rcvd) {             /* got a FIN */

		/* if good ACK, present any data */

		if (n->t_flags&T_ACK) {
			if (SEQ_GT(n->t_ackno, tp->iss))	/* 32 */
				present_data(tp);
		} else {                                /* 9 */
                	tp->t_timers[TFINACK] = T_2ML;
                	tp->waited_2_ml = FALSE;
		}
		return (CLOSE_WAIT);
	} else                          /* no FIN */

		/* if good ACK, open connection, otherwise wait for one */

		if (n->t_flags&T_ACK) {			/* 11 */
			present_data(tp);
			return(ESTAB);
		} else
			return(SYN_RCVD);               /* 8 */
}

cl1_netr(wp)            /* incoming seg after we closed (15,18,22,23,30,39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	if (ack_fin(tp, n)) {			/* got ACK of our FIN */
        	if (n->t_flags&T_FIN) {		/* got for FIN (23) */
        		rcv_tcp(tp, n, TCP_CTL);
                	tp->t_timers[TFINACK] = T_2ML;
                	tp->waited_2_ml = FALSE;
                        return(TIME_WAIT);
        	} else {

			/* if wait done, see if any data left for user */

        		if (tp->waited_2_ml)
        			if (rcv_empty(tp)) {    /* 15 */
        				t_close(tp, UCLOSED);
        				return(CLOSED);
        			} else
        				return(RCV_WAIT);       /* 18 */
        		else
        			return(TIME_WAIT);      /* 22 */
		}
	} else {				/* our FIN not ACKed yet */
		if (n->t_flags&T_FIN) {		/* rcvd for FIN (30) */
			rcv_tcp(tp, n, TCP_CTL);
                	tp->t_timers[TFINACK] = T_2ML;
                	tp->waited_2_ml = FALSE;
		} else {		/* no FIN, just proc new data (39) */
        		rcv_tcp(tp, n, TCP_DATA);
        		present_data(tp);
		}
	}
	return(SAME);
}

cl2_netr(wp)            /* incoming seg after foreign close (16,19,31,39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	if (ack_fin(tp, n)) {                   /* this is ACK of our fin */

		/* if no data left for user, close; otherwise wait */

		if (rcv_empty(tp)) {                            /* 16 */
			t_close(tp, UCLOSED);
			return(CLOSED);
		} else                                          /* 19 */
			return(RCV_WAIT);
	} else {				/* no ACK of our FIN */
		/* duplicate FIN or data */

		if (n->t_flags&T_FIN)				/* 31 */
			send_tcp(tp, TCP_CTL);		/* ACK duplicate FIN */
		else {                                          /* 39 */
			rcv_tcp(tp, n, TCP_DATA);                         
			present_data(tp);
		}
	}
	return(SAME);
}

fw1_netr(wp)            /* incoming seg after user close (26,27,28,39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	/* process any incoming data, since we closed but they didn't */

	rcv_tcp(tp, n, TCP_DATA);
	present_data(tp);

	/* send any data remaining on send buffer */

	send_tcp(tp, TCP_DATA);
	if (ack_fin(tp, n)) {			/* our FIN got ACKed */
		if (tp->fin_rcvd) {                     /* got for FIN (28) */
                	tp->t_timers[TFINACK] = T_2ML;
                	tp->waited_2_ml = FALSE;
			return(TIME_WAIT);
		} else                                  /* no FIN, wait (27) */
			return(FIN_W2);
	} else {				/* no ACK of FIN */
		if (tp->fin_rcvd) {                     /* got for FIN (26) */
                	tp->t_timers[TFINACK] = T_2ML;
                	tp->waited_2_ml = FALSE;
			return(CLOSING1);
                } 
	}
	return(SAME);                                   /* 39 */
}

syr_netr(wp)             /* incoming seg after SYN rcvd (5,33) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	rcv_tcp(tp, n, TCP_DATA);
	present_data(tp);

	/* if no FIN, open connection, otherwise wait for user close */

	if (tp->fin_rcvd)                               /* 33 */
		return(CLOSE_WAIT);
	else                                            /* 5 */
		return(ESTAB);
}

est_netr(wp)            /* incoming seg on open connection (12,39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	rcv_tcp(tp, n, TCP_DATA);
	present_data(tp);

	/* if no FIN, remain open, otherwise wait for user close */

	if (tp->fin_rcvd)                       /* 12 */
		return(CLOSE_WAIT);
	else                                    /* 39 */
        	return(SAME);
}

fw2_netr(wp)            /* incoming seg while waiting for for FIN (12,39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	/* process data since we closed, but they may not have */

	rcv_tcp(tp, n, TCP_DATA);
	present_data(tp);

	/* if we get the FIN, start the finack timer, else keep waiting */

	if (tp->fin_rcvd) {                     /* got for FIN (29) */
		tp->t_timers[TFINACK] = T_2ML;
		tp->waited_2_ml = FALSE;
		return(TIME_WAIT);
	} else                                  /* 39 */
        	return(SAME);
}

cwt_netr(wp)            /* incoming seg after exchange of FINs (30,31,39) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	/* either duplicate FIN or data */

	if (n->t_flags&T_FIN) {
		if (n->t_flags&T_ACK && SEQ_LEQ(n->t_ackno, tp->seq_fin)) {	
                	rcv_tcp(tp, n, TCP_CTL);
                	tp->t_timers[TFINACK] = T_2ML;
                	tp->waited_2_ml = FALSE;
		} else                                          /* 31 */
			send_tcp(tp, TCP_CTL);
	} else {				/* duplicate data (39) */
		rcv_tcp(tp, n, TCP_DATA);
		present_data(tp);
	}
	return(SAME);
}

rwt_netr(wp)            /* incoming seg while waiting for user rcv (30,21) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register struct th *n = (struct th *)wp->w_dat;

	/* handle duplicate ACK of our FIN */

	if (n->t_flags&T_FIN && n->t_flags&T_ACK && 
	    SEQ_LEQ(n->t_ackno, tp->seq_fin)) { 		/* 30 */
        	rcv_tcp(tp, n, TCP_CTL);
        	tp->t_timers[TFINACK] = T_2ML;
        	tp->waited_2_ml = FALSE;
	} 
	return(SAME);
}

timers(wp)              /* timer processor (14,17,34,35,36,37,38) */
struct work *wp;
{
	register struct tcb *tp = wp->w_tcb;
	register type = wp->w_stype;

	switch (type) {

	case TINIT:             /* initialization timer */

		if (!tp->syn_acked) {	/* haven't got ACK of our SYN (35) */
			t_close(tp, UINTIMO);
			return(CLOSED);
		}
		break;

	case TFINACK:           /* fin-ack timer */   

		if (tp->t_state == TIME_WAIT) {

			/* can be sure our ACK of for FIN was rcvd,
			   can close if no data left for user */

			if (rcv_empty(tp)) {            /* 14 */
				t_close(tp, UCLOSED);
				return(CLOSED);
			} else                          /* 17 */
				return(RCV_WAIT);

		} else if (tp->t_state == CLOSING1)     /* 37 */

			/* safe to close */

			tp->waited_2_ml = TRUE;

	        break;

	case TREXMT:            /* retransmission timer */

		/* set up for a retransmission, increase rexmt time
		   in case of multiple retransmissions. */

	        if (SEQ_GT(tp->t_rexmt_val, tp->snd_una)) {   /* 34 */
	        	tp->snd_nxt = tp->snd_una;
	        	tp->rexmt = TRUE;
			tp->t_rxtct++;
			tp->t_xmtime = tp->t_xmtime << 1;                  
        		if (tp->t_xmtime > T_REMAX)
        			tp->t_xmtime = T_REMAX;
	        	send_tcp(tp, TCP_DATA);
	        }
		break;

	case TREXMTTL:          /* retransmit too long */

		/* tell user */

        	if (SEQ_GT(tp->t_rtl_val, tp->snd_una))		/* 36 */
        		to_user(tp->t_ucb, URXTIMO);

		/* if user has already closed, abort the connection */

		if (tp->usr_closed) {
			t_close(tp, URXTIMO);
			return(CLOSED);
		}
		break;

	case TPERSIST:          /* persist timer */

		/* force a byte send through closed window */

        	tp->force_one = TRUE;                   /* 38 */
        	send_tcp(tp, TCP_DATA);
		break;
	}
	return(SAME);
}

netprepr(tp, n)         /* net preproc (66,67,68,69,70,71,72,73,74,75,76) */
register struct tcb *tp;
register struct th *n;
{
	register struct ucb *up;

	switch (tp->t_state) {

	case LISTEN:
		/*
		 * Ignore resets, ACKs cause resets, must have SYN.
		 */
		if (n->t_flags&T_RST)
			break;
		else if (n->t_flags&T_ACK)
			send_rst(tp, n);
		else if (n->t_flags&T_SYN)
			return(0);
		break;

	case SYN_SENT:
		/*
		 * Bad ACKs cause resets, good resets close, must have SYN.
		 */
		if (n->t_flags&T_ACK && !ack_ok(tp, n))
			send_rst(tp, n);		
		else if (n->t_flags&T_RST) {
			if (n->t_flags&T_ACK) {
				t_close(tp, URESET);
				return(CLOSED);
			}
		} else if (n->t_flags&T_SYN)
			return(0);
		break;

	default:
		/*
		 * Sequence number must fall in window, otherwise just
		 * ACK and drop.
		 */
		if (SEQ_LT(n->t_seq, tp->rcv_nxt) ||
		    SEQ_GT(n->t_seq, tp->rcv_nxt + rcv_resource(tp)))
			send_tcp(tp, TCP_CTL);
		/*
		 * Acceptable resets close in all states except L_SYN_RCVD,
		 * where connection returns to LISTEN.
		 */
		else if (n->t_flags&T_RST) {		
			if (tp->t_state == L_SYN_RCVD) {
				t_cancel(tp, TREXMT);
				t_cancel(tp, TREXMTTL);
				t_cancel(tp, TPERSIST);
				up = tp->t_ucb;
				up->uc_host.s_addr = NULL;
				if (up->uc_route != NULL) {
					h_free(up->uc_route);
					up->uc_route = NULL;
				}
				return(LISTEN);
			} else {                        /* 66 */
				t_close(tp, URESET);
				return(CLOSED);
			}
		/*
		 * No SYNs allowed in window.
		 */
		} else if (n->t_flags&T_SYN) {
			send_rst(tp, n);        
			t_close(tp, URESET);
			return(CLOSED);
		/*
		 * Must have good ACK.  Bad ACKs cause resets only in
		 * SYN_RCVD states.
		 */
		} else if (n->t_flags&T_ACK)
			if (!ack_ok(tp, n)) {
				if (tp->t_state == SYN_RCVD || 
				    tp->t_state == L_SYN_RCVD)
					send_rst(tp, n);
			} else
				return(0);	/* acceptable segment */
	}
	return(-1);     /* tell caller to eat segment (unacceptable) */
}