BBN-Vax-TCP/bbnnet/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"
#include "../bbnnet/tcp_pred.h"
/*
* These are the action routines of the TCP finite state machine. They are
* called from the TCP fsm dispatcher. 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, PASSIVE);
return(LISTEN);
}
sys_cls(wp) /* active open (6) */
register struct work *wp;
{
t_open(wp->w_tcb, ACTIVE);
send_ctl(wp->w_tcb); /* 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;
tp = wp->w_tcb;
tp->snd_fin = TRUE; /* send our own FIN */
send_ctl(tp);
tp->usr_closed = TRUE;
return(CLOSING2);
}
cls_rwt(wp) /* rcv request after foreign close (20) */
struct work *wp;
{
register struct tcb *tp;
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 synced connection (24,25) */
struct work *wp;
{
register struct tcb *tp;
tp = wp->w_tcb;
tp->snd_fin = TRUE; /* send FIN */
send_ctl(tp);
tp->usr_closed = TRUE;
return(FIN_W1);
}
sss_syn(wp) /* incoming seq on open connection (39) */
struct work *wp;
{
register struct tcb *tp;
tp = wp->w_tcb;
rcv_data(tp, wp->w_dat);
present_data(tp);
return(SAME);
}
sss_snd(wp) /* send request on open connection (40,41) */
struct work *wp;
{
register struct tcb *tp;
register struct mbuf *m, *n;
register struct ucb *up;
register off;
sequence last;
tp = wp->w_tcb;
up = tp->t_ucb;
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 EOL */
tp->snd_end = last;
}
if (up->uc_flags & UURG) { /* urgent data */
tp->snd_urp = last+1;
tp->snd_urg = TRUE;
}
send(tp);
return(SAME);
}
sss_rcv(wp) /* rcv request on open connection (42) */
struct work *wp;
{
register struct tcb *tp;
tp = wp->w_tcb;
present_data(tp);
return(SAME);
}
cls_nsy(wp) /* abort request on unsynced connection (44) */
struct work *wp;
{
t_close(wp->w_tcb, UABORT);
return(CLOSED);
}
cls_syn(wp) /* abort request on synced connection (45) */
struct work *wp;
{
register struct tcb *tp;
tp = wp->w_tcb;
tp->snd_rst = TRUE; /* send reset */
send_null(tp);
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;
register struct ucb *up;
register struct th *n;
register struct if_local *lp;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
up = tp->t_ucb;
if (!n->t_syn) /* must have good SYN */
return(EFAILEC);
/* fill in unspecified foreign host address. */
up->uc_host = n->t_s;
tp->t_fport = n->t_src;
/* pick a network interface to respond on */
for (lp=locnet; lp < &locnet[nlocnet]; lp++)
if (lp->if_addr.s_addr == n->t_d.s_addr && lp->if_ifcb->if_avail) {
up->uc_srcif = lp;
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 (lp == &locnet[nlocnet]) {
t_close(tp, UNRCH);
return(CLOSED);
}
rcv_data(tp, n);
if (!tp->fin_rcvd) { /* no FIN (4) */
/* start init timer now that we have foreign host */
tp->t_init = T_INIT/2;
return(L_SYN_RCVD);
} else { /* got a FIN, start timer (3) */
tp->t_finack = 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;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
if (!n->t_syn) /* must have good SYN */
return(EFAILEC);
rcv_data(tp, n);
if (tp->fin_rcvd) { /* got a FIN */
/* if good ACK, present any data */
if (n->t_ack) {
if (n->t_ackno > tp->iss) /* 32 */
present_data(tp);
} else { /* 9 */
tp->t_finack = 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_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;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
if (ack_fin(tp, n)) /* got ACK of our FIN */
if (n->t_fin) { /* got for FIN (23) */
rcv_ctl(tp, n);
tp->t_finack = 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_fin) { /* rcvd for FIN (30) */
rcv_ctl(tp, n);
tp->t_finack = T_2ML;
tp->waited_2_ml = FALSE;
} else { /* no FIN, just proc new data (39) */
rcv_data(tp, n);
present_data(tp);
}
return(SAME);
}
cl2_netr(wp) /* incoming seg after foreign close (16,19,31,39) */
struct work *wp;
{
register struct tcb *tp;
register struct th *n;
tp = wp->w_tcb;
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_fin) /* 31 */
send_ctl(tp); /* ACK duplicate FIN */
else { /* 39 */
rcv_data(tp, n);
present_data(tp);
}
return(SAME);
}
fw1_netr(wp) /* incoming seg after user close (26,27,28,39) */
struct work *wp;
{
register struct tcb *tp;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
/* process any incoming data, since we closed but they didn't */
rcv_data(tp, n);
present_data(tp);
/* send any data remaining on send buffer */
send(tp);
if (ack_fin(tp, n)) /* our FIN got ACKed */
if (tp->fin_rcvd) { /* got for FIN (28) */
tp->t_finack = 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_finack = 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;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
/* must have ACK of our SYN */
if (!n->t_ack || (n->t_ack && n->t_ackno <= tp->iss))
return(EFAILEC);
rcv_data(tp, n);
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;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
rcv_data(tp, n);
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;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
/* process data since we closed, but they may not have */
rcv_data(tp, n);
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_finack = 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;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
/* either duplicate FIN or data */
if (n->t_fin) {
if (n->t_ack && n->t_ackno <= tp->seq_fin) { /* dup ACK (30) */
rcv_ctl(tp, n);
tp->t_finack = T_2ML;
tp->waited_2_ml = FALSE;
} else /* 31 */
send_ctl(tp);
} else { /* duplicate data (39) */
rcv_data(tp, n);
present_data(tp);
}
return(SAME);
}
rwt_netr(wp) /* incoming seg while waiting for user rcv (30,21) */
struct work *wp;
{
register struct tcb *tp;
register struct th *n;
tp = wp->w_tcb;
n = (struct th *)wp->w_dat;
/* handle duplicate ACK of our FIN */
if (n->t_fin && n->t_ack && n->t_ackno <= tp->seq_fin) { /* 30 */
rcv_ctl(tp, n);
tp->t_finack = 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;
register type;
tp = wp->w_tcb;
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 (tp->t_rexmt_val > tp->snd_una) { /* 34 */
tp->snd_nxt = tp->snd_una;
tp->rexmt = TRUE;
tp->t_xmtime = tp->t_xmtime << 1;
if (tp->t_xmtime > T_REMAX)
tp->t_xmtime = T_REMAX;
send(tp);
}
break;
case TREXMTTL: /* retransmit too long */
/* tell user */
if (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(tp);
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:
if (n->t_ack || !n->t_syn)
send_rst(tp, n);
else if (!n->t_rst)
return(0);
break;
case SYN_SENT:
if (!ack_ok(tp, n))
send_rst(tp, n); /* 71,72,75 */
else if (n->t_rst) {
t_close(tp, URESET); /* 70 */
return(CLOSED);
} else if (n->t_syn)
return(0);
break;
default:
if (n->t_rst) { /* any resets? */
if (n->t_seq >= tp->rcv_nxt) { /* good reset */
if (tp->t_state == L_SYN_RCVD) {
if (ack_ok(tp, n)) { /* 67 */
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);
}
} /* else 69 */
break;
}
case SYN_RCVD:
if (!ack_ok(tp, n) || (n->t_syn && n->t_seq != tp->irs)) {
send_rst(tp, n); /* bad ack (74) */
if (n->t_syn) {
t_close(tp, URESET);
return(CLOSED);
}
} else
return(0); /* acceptable segment */
}
return(-1); /* tell caller to eat segment (unacceptable) */
}