/* tcp_usrreq.c 1.6 81/10/21 */ #include "../h/param.h" #include "../h/systm.h" #include "../bbnnet/net.h" #include "../bbnnet/mbuf.h" #include "../bbnnet/tcp.h" #include "../bbnnet/ip.h" #include "../bbnnet/imp.h" #include "../bbnnet/ucb.h" #define TCPFSTAB #include "../bbnnet/fsm.h" /* * This file contains the top level routines for actions * called by the user and by the clock routines, while input * actions for network data are driven by tcp_input. The two * main entry points here are tcp_timeo, which decrements tcp timers * each second and runs timer action routines when the timers go off, * and tcp_usrreq, which performs requests generated by or on behalf * of user processes. Both routines raise the ipl to splnet to block * off tcp_input processing (triggered by imp's) during the work * here. */ tcp_timeo() { register struct tcb *tp; int s = splnet(); COUNT(TCP_TIMEO); /* * Search through tcb's and update active timers. */ for (tp = netcb.n_tcb_head; tp != NULL; tp = tp->t_tcb_next) { if (tp->t_init != 0 && --tp->t_init == 0) tcp_usrreq(ISTIMER, TINIT, tp, 0); if (tp->t_rexmt != 0 && --tp->t_rexmt == 0) tcp_usrreq(ISTIMER, TREXMT, tp, 0); if (tp->t_rexmttl != 0 && --tp->t_rexmttl == 0) tcp_usrreq(ISTIMER, TREXMTTL, tp, 0); if (tp->t_persist != 0 && --tp->t_persist == 0) tcp_usrreq(ISTIMER, TPERSIST, tp, 0); if (tp->t_finack != 0 && --tp->t_finack == 0) tcp_usrreq(ISTIMER, TFINACK, tp, 0); tp->t_xmt++; } netcb.n_iss += ISSINCR; /* increment iss */ timeout(tcp_timeo, 0, hz); /* reschedule every second */ splx(s); } tcp_usrreq(input, stype, tp, m) int input, stype; register struct tcb *tp; char *m; { struct work w; int s = splnet(); register int nstate; register struct work *wp = &w; COUNT(TCP_USRREQ); wp->w_type = input; wp->w_stype = stype; wp->w_tcb = tp; wp->w_dat = m; nstate = tp->t_state; tp->tc_flags &= ~TC_NET_KEEP; acounts[nstate][input]++; switch (tcp_fstab[nstate][input]) { default: printf("tcp: bad state: tcb=%x state=%d input=%d\n", tp, tp->t_state, input); nstate = EFAILEC; break; case LIS_CLS: /* 1 */ t_open(tp, PASSIVE); nstate = LISTEN; break; case SYS_CLS: /* 2 */ t_open(tp, ACTIVE); send_ctl(tp); nstate = SYN_SENT; break; case CLS_OPN: /* 10 */ t_close(tp, UCLOSED); nstate = CLOSED; break; case CL2_CLW: /* 10 */ tp->tc_flags |= TC_SND_FIN; send_ctl(tp); tp->tc_flags |= TC_USR_CLOSED; nstate = CLOSING2; break; case TIMERS: /* 14,17,34,35,36,37,38 */ nstate = tcp_timers(wp); break; case CLS_RWT: /* 20 */ present_data(tp); if (rcv_empty(tp)) { t_close(tp, UCLOSED); nstate = CLOSED; } else nstate = RCV_WAIT; break; case FW1_SYR: /* 24,25 */ tp->tc_flags |= TC_SND_FIN; send_ctl(tp); tp->tc_flags |= TC_USR_CLOSED; nstate = FIN_W1; break; case SSS_SND: /* 40,41 */ nstate = sss_snd(wp); break; case SSS_RCV: /* 42 */ send_ctl(tp); /* send new window */ present_data(tp); break; case CLS_NSY: /* 44 */ t_close(tp, UABORT); nstate = CLOSED; break; case CLS_SYN: /* 45 */ tp->tc_flags |= TC_SND_RST; send_null(tp); t_close(tp, UABORT); nstate = CLOSED; break; case CLS_ACT: /* 47 */ t_close(tp, UNETDWN); nstate = CLOSED; break; case NOP: break; case CLS_ERR: to_user(tp->t_ucb, UCLSERR); break; } if (tp->t_ucb->uc_flags & UDEBUG) tcp_debug(tp, wp, input, nstate); #ifdef TCPDEBUG tcp_prt(tp, input, wp->w_stype, nstate); #endif /* YECH */ switch (nstate) { case SAME: break; case EFAILEC: if (m) m_freem(dtom(m)); break; default: tp->t_state = nstate; break; } splx(s); } t_open(tp, mode) /* set up a tcb for a connection */ register struct tcb *tp; int mode; { register struct ucb *up; COUNT(T_OPEN); /* 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->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi = tp->snd_una = tp->iss = netcb.n_iss; tp->snd_off = tp->iss + 1; 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 */ } 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; COUNT(T_CLOSE); up = tp->t_ucb; tp->t_init = tp->t_rexmt = tp->t_rexmttl = tp->t_persist = tp->t_finack = 0; /* 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; } 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_host != NULL) { h_free(up->uc_host); up->uc_host = NULL; } /* if user has initiated close (via close call), delete ucb entry, otherwise just wakeup so user can issue close call */ if (tp->tc_flags&TC_USR_ABORT) up->uc_proc = NULL; else to_user(up, state); } sss_snd(wp) struct work *wp; { register struct tcb *tp; register struct mbuf *m, *n; register struct ucb *up; register off; seq_t last; tp = wp->w_tcb; up = tp->t_ucb; last = tp->snd_off; /* 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->tc_flags |= TC_SND_URG; } send(tp); return (SAME); } tcp_timers(wp) struct work *wp; { register struct tcb *tp = wp->w_tcb; register type = wp->w_stype; COUNT(TCP_TIMERS); switch (type) { case TINIT: /* initialization timer */ if ((tp->tc_flags&TC_SYN_ACKED) == 0) { /* 35 */ t_close(tp, UINTIMO); return (CLOSED); } return (SAME); case TFINACK: /* fin-ack timer */ switch (tp->t_state) { case TIME_WAIT: /* * We can be sure our ACK of foreign FIN was rcvd, * and can close if no data left for user. */ if (rcv_empty(tp)) { t_close(tp, UCLOSED); /* 14 */ return (CLOSED); } return (RCV_WAIT); /* 17 */ case CLOSING1: tp->tc_flags |= TC_WAITED_2_ML; return (SAME); default: return (SAME); } case TREXMT: /* retransmission timer */ if (tp->t_rexmt_val > tp->snd_una) { /* 34 */ /* * Set up for a retransmission, increase rexmt time * in case of multiple retransmissions. */ tp->snd_nxt = tp->snd_una; tp->tc_flags |= TC_REXMT; tp->t_xmtime = tp->t_xmtime << 1; if (tp->t_xmtime > T_REMAX) tp->t_xmtime = T_REMAX; send(tp); } return (SAME); case TREXMTTL: /* retransmit too long */ 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->tc_flags & TC_USR_CLOSED) { t_close(tp, URXTIMO); return (CLOSED); } return (SAME); case TPERSIST: /* persist timer */ /* * Force a byte send through closed window. */ tp->tc_flags |= TC_FORCE_ONE; send(tp); return (SAME); } panic("tcp_timers"); } tcp_debug(tp, wp, input, newstate) /* write a debugging record */ register struct tcb *tp; register struct work *wp; int input, newstate; { struct t_debug debuf; register struct th *n; register off_t siz; COUNT(TCP_DEBUG); /* look for debugging file inode */ if (netcb.n_debug == 0) return; debuf.t_tod = 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 == -1) debuf.t_lno = ((struct ip *)n)->ip_len; else debuf.t_lno = n->t_len; debuf.t_flg = *(char *)((int)&n->t_win - 1); } /* STUFF INTO CIRC BUFFER */ } #ifdef TCPDEBUG tcp_prt(tp, input, timer, newstate) /* print debugging info on the console */ register struct tcb *tp; register input, timer, newstate; { register oldstate; COUNT(TCP_PRT); 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 /* THIS ROUTINE IS A CROCK */ to_user(up, state) register struct ucb *up; register short state; { COUNT(TO_USER); up->uc_state |= state; netwakeup(up); if (state == UURGENT) psignal(up->uc_proc, SIGURG); }