X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/2e56471e6b4cf3dae6d40cdec21817677d3f6c46..4ab1a5c3d51e670b73ca6e3477d72344edf9b224:/usr/src/sys/netinet/tcp_input.c?ds=sidebyside diff --git a/usr/src/sys/netinet/tcp_input.c b/usr/src/sys/netinet/tcp_input.c index 0aa582b2c7..d00ead02e7 100644 --- a/usr/src/sys/netinet/tcp_input.c +++ b/usr/src/sys/netinet/tcp_input.c @@ -1,815 +1,958 @@ -/* tcp_input.c 1.4 81/10/26 */ +/* tcp_input.c 1.71 82/06/30 */ #include "../h/param.h" #include "../h/systm.h" -#include "../bbnnet/net.h" -#include "../bbnnet/mbuf.h" -#include "../bbnnet/host.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" - -extern int nosum; - -tcp_input(mp) - register struct mbuf *mp; +#include "../h/mbuf.h" +#include "../h/protosw.h" +#include "../h/socket.h" +#include "../h/socketvar.h" +#include "../net/in.h" +#include "../net/route.h" +#include "../net/in_pcb.h" +#include "../net/in_systm.h" +#include "../net/if.h" +#include "../net/ip.h" +#include "../net/ip_var.h" +#include "../net/tcp.h" +#include "../net/tcp_fsm.h" +#include "../net/tcp_seq.h" +#include "../net/tcp_timer.h" +#include "../net/tcp_var.h" +#include "../net/tcpip.h" +#include "../net/tcp_debug.h" +#include + +int tcpprintfs = 0; +int tcpcksum = 1; +struct sockaddr_in tcp_in = { AF_INET }; +struct tcpiphdr tcp_saveti; +extern tcpnodelack; + +struct tcpcb *tcp_newtcpcb(); +/* + * TCP input routine, follows pages 65-76 of the + * protocol specification dated September, 1981 very closely. + */ +tcp_input(m0) + struct mbuf *m0; { - register struct tcb *tp; - register struct th *n; - int nstate; - struct mbuf *m; - struct ucb *up; - int hlen, tlen, j; - u_short lport, fport; -#ifdef TCPDEBUG - struct tcp_debug tdb; -#endif -COUNT(TCP_INPUT); + register struct tcpiphdr *ti; + struct inpcb *inp; + register struct mbuf *m; + struct mbuf *om = 0; + int len, tlen, off; + register struct tcpcb *tp = 0; + register int tiflags; + struct socket *so; + int todrop, acked; + short ostate; + struct in_addr laddr; /* - * Build extended tcp header + * Get IP and TCP header together in first mbuf. + * Note: IP leaves IP header in first mbuf. */ - n = (struct th *)((int)mp + mp->m_off); - tlen = ((struct ip *)n)->ip_len; - n->t_len = htons(tlen); - n->t_next = NULL; - n->t_prev = NULL; - n->t_x1 = 0; - lport = ntohs(n->t_dst); - fport = ntohs(n->t_src); - - /* WONT BE POSSIBLE WHEN MBUFS ARE 256 BYTES */ - if ((hlen = n->t_off << 2) > mp->m_len) - { printf("tcp header overflow\n"); m_freem(mp); return; } + m = m0; + ti = mtod(m, struct tcpiphdr *); + if (((struct ip *)ti)->ip_hl > (sizeof (struct ip) >> 2)) + ip_stripoptions((struct ip *)ti, (struct mbuf *)0); + if (m->m_off > MMAXOFF || m->m_len < sizeof (struct tcpiphdr)) { + if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) { + tcpstat.tcps_hdrops++; + return; + } + ti = mtod(m, struct tcpiphdr *); + } /* - * Checksum extended header and data + * Checksum extended TCP header and data. */ - j = n->t_sum; n->t_sum = 0; - if (j != cksum(mp, sizeof (struct ip) + tlen)) { - netstat.t_badsum++; - if (nosum == 0) { - m_freem(mp); - return; + tlen = ((struct ip *)ti)->ip_len; + len = sizeof (struct ip) + tlen; + if (tcpcksum) { + ti->ti_next = ti->ti_prev = 0; + ti->ti_x1 = 0; + ti->ti_len = (u_short)tlen; +#if vax || pdp11 + ti->ti_len = htons((u_short)ti->ti_len); +#endif + if (ti->ti_sum = in_cksum(m, len)) { + tcpstat.tcps_badsum++; + if (tcpprintfs) + printf("tcp cksum %x\n", ti->ti_sum); + goto drop; } } /* - * Find tcb for message (SHOULDN'T USE LINEAR SEARCH!) + * Check that TCP offset makes sense, + * pull out TCP options and adjust length. */ - for (tp = netcb.n_tcb_head; tp != 0; tp = tp->t_tcb_next) - if (tp->t_lport == lport && tp->t_fport == fport && - tp->t_ucb->uc_host->h_addr.s_addr == n->t_s.s_addr) - goto found; - for (tp = netcb.n_tcb_head; tp != 0; tp = tp->t_tcb_next) - if (tp->t_lport == lport && - (tp->t_fport==fport || tp->t_fport==0) && - (tp->t_ucb->uc_host->h_addr.s_addr == n->t_s.s_addr || - tp->t_ucb->uc_host->h_addr.s_addr == 0)) - goto found; - goto notwanted; -found: + off = ti->ti_off << 2; + if (off < sizeof (struct tcphdr) || off > tlen) { + tcpstat.tcps_badoff++; + goto drop; + } + tlen -= off; + ti->ti_len = tlen; + if (off > sizeof (struct tcphdr)) { + if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) { + tcpstat.tcps_hdrops++; + goto drop; + } + ti = mtod(m, struct tcpiphdr *); + om = m_get(M_DONTWAIT); + if (om == 0) + goto drop; + om->m_off = MMINOFF; + om->m_len = off - sizeof (struct tcphdr); + { caddr_t op = mtod(m, caddr_t) + sizeof (struct tcpiphdr); + bcopy(op, mtod(om, caddr_t), (unsigned)om->m_len); + m->m_len -= om->m_len; + bcopy(op+om->m_len, op, + (unsigned)(m->m_len-sizeof (struct tcpiphdr))); + } + } + tiflags = ti->ti_flags; /* - * Byte swap header + * Drop TCP and IP headers. */ - n->t_len = tlen - hlen; - n->t_src = fport; - n->t_dst = lport; - n->t_seq = ntohl(n->t_seq); - n->t_ackno = ntohl(n->t_ackno); - n->t_win = ntohs(n->t_win); - n->t_urp = ntohs(n->t_urp); + off += sizeof (struct ip); + m->m_off += off; + m->m_len -= off; +#if vax || pdp11 /* - * Check segment seq # and do rst processing + * Convert TCP protocol specific fields to host format. */ - switch (tp->t_state) { - - case LISTEN: - if ((n->th_flags&TH_ACK) || !syn_ok(tp, n)) { - send_rst(tp, n); - goto badseg; - } - if (n->th_flags&TH_RST) - goto badseg; - goto goodseg; - - case SYN_SENT: - if (!ack_ok(tp, n) || !syn_ok(tp, n)) { - send_rst(tp, n); /* 71,72,75 */ - goto badseg; - } - if (n->th_flags&TH_RST) { - t_close(tp, URESET); /* 70 */ - tp->t_state = CLOSED; - goto badseg; - } - goto goodseg; - - default: - if ((n->th_flags&TH_RST) == 0) - goto common; - if (n->t_seq < tp->rcv_nxt) /* bad rst */ - goto badseg; /* 69 */ - switch (tp->t_state) { - - case L_SYN_RCVD: - if (ack_ok(tp, n) == 0) - goto badseg; /* 69 */ - tp->t_rexmt = 0; - tp->t_rexmttl = 0; - tp->t_persist = 0; - h_free(tp->t_ucb->uc_host); - tp->t_state = LISTEN; - goto badseg; + ti->ti_seq = ntohl(ti->ti_seq); + ti->ti_ack = ntohl(ti->ti_ack); + ti->ti_win = ntohs(ti->ti_win); + ti->ti_urp = ntohs(ti->ti_urp); +#endif - default: - t_close(tp, URESET); /* 66 */ - tp->t_state = CLOSED; - goto badseg; - } - /*NOTREACHED*/ + /* + * Locate pcb for segment. On match, update the local + * address stored in the block to reflect anchoring. + */ + inp = in_pcblookup + (&tcb, ti->ti_src, ti->ti_sport, ti->ti_dst, ti->ti_dport, + INPLOOKUP_WILDCARD); - case SYN_RCVD: -common: - if (ack_ok(tp, n) == 0) { - send_rst(tp, n); /* 74 */ - goto badseg; - } - if (syn_ok(tp, n) && n->t_seq != tp->irs) { - send_null(tp); /* 74 */ - goto badseg; - } - goto goodseg; + /* + * If the state is CLOSED (i.e., TCB does not exist) then + * all data in the incoming segment is discarded. + */ + if (inp == 0) + goto dropwithreset; + tp = intotcpcb(inp); + if (tp == 0) + goto dropwithreset; + so = inp->inp_socket; + if (so->so_options & SO_DEBUG) { + ostate = tp->t_state; + tcp_saveti = *ti; } -badseg: - m_freem(mp); - return; -goodseg: -#ifdef notdef /* - * Defer processing if no buffer space for this connection. + * Segment received on connection. + * Reset idle time and keep-alive timer. */ - up = tp->t_ucb; - if ((int)up->uc_rcv - (int)up->uc_rsize <= 0 - && n->t_len != 0 && netcb.n_bufs < netcb.n_lowat) { - mp->m_act = (struct mbuf *)0; - if ((m = tp->t_rcv_unack) != NULL) { - while (m->m_act != NULL) - m = m->m_act; - m->m_act = mp; - } else - tp->t_rcv_unack = mp; + tp->t_idle = 0; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; - return; + /* + * Process options. + */ + if (om) { + tcp_dooptions(tp, om); + om = 0; } -#endif /* - * Discard ip header, and do tcp input processing. + * Calculate amount of space in receive window, + * and then do TCP input processing. */ - hlen += sizeof(struct ip); - mp->m_off += hlen; - mp->m_len -= hlen; - nstate = tp->t_state; - tp->tc_flags &= ~TC_NET_KEEP; - acounts[tp->t_state][INRECV]++; -#ifdef TCPDEBUG - if ((tp->t_ucb->uc_flags & UDEBUG) || tcpconsdebug) { - tdb_setup(tp, n, INRECV, &tdb); - } else - tdb.td_tod = 0; -#endif + tp->rcv_wnd = sbspace(&so->so_rcv); + if (tp->rcv_wnd < 0) + tp->rcv_wnd = 0; + switch (tp->t_state) { - case LISTEN: - if (!syn_ok(tp, n) || - ((tp->t_ucb->uc_host = h_make(&n->t_s)) == 0)) { - nstate = EFAILEC; - goto done; + /* + * If the state is LISTEN then ignore segment if it contains an RST. + * If the segment contains an ACK then it is bad and send a RST. + * If it does not contain a SYN then it is not interesting; drop it. + * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial + * tp->iss, and send a segment: + * + * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. + * Fill in remote peer address fields if not previously specified. + * Enter SYN_RECEIVED state, and process any other fields of this + * segment in this state. + */ + case TCPS_LISTEN: + if (tiflags & TH_RST) + goto drop; + if (tiflags & TH_ACK) + goto dropwithreset; + if ((tiflags & TH_SYN) == 0) + goto drop; + tcp_in.sin_addr = ti->ti_src; + tcp_in.sin_port = ti->ti_sport; + laddr = inp->inp_laddr; + if (inp->inp_laddr.s_addr == 0) + inp->inp_laddr = ti->ti_dst; + if (in_pcbconnect(inp, (struct sockaddr_in *)&tcp_in)) { + inp->inp_laddr = laddr; + goto drop; } - tp->t_fport = n->t_src; - tp->t_ucb->uc_template = tcp_template(tp); - rcv_ctldat(tp, n, 1); - if (tp->tc_flags&TC_FIN_RCVD) { - tp->t_finack = T_2ML; /* 3 */ - tp->tc_flags &= ~TC_WAITED_2_ML; - nstate = CLOSE_WAIT; - } else { - tp->t_init = T_INIT / 2; /* 4 */ - nstate = L_SYN_RCVD; + tp->t_template = tcp_template(tp); + if (tp->t_template == 0) { + in_pcbdisconnect(inp); + inp->inp_laddr = laddr; + tp = 0; + goto drop; } - goto done; + tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; + tp->irs = ti->ti_seq; + tcp_sendseqinit(tp); + tcp_rcvseqinit(tp); + tp->t_state = TCPS_SYN_RECEIVED; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; + goto trimthenstep6; - case SYN_SENT: - if (!syn_ok(tp, n)) { - nstate = EFAILEC; - goto done; - } - rcv_ctldat(tp, n, 1); - if (tp->tc_flags&TC_FIN_RCVD) { - if (n->th_flags&TH_ACK) { - if (n->t_ackno > tp->iss) - present_data(tp); /* 32 */ - } else { - tp->t_finack = T_2ML; /* 9 */ - tp->tc_flags &= ~TC_WAITED_2_ML; + /* + * If the state is SYN_SENT: + * if seg contains an ACK, but not for our SYN, drop the input. + * if seg contains a RST, then drop the connection. + * if seg does not contain SYN, then drop it. + * Otherwise this is an acceptable SYN segment + * initialize tp->rcv_nxt and tp->irs + * if seg contains ack then advance tp->snd_una + * if SYN has been acked change to ESTABLISHED else SYN_RCVD state + * arrange for segment to be acked (eventually) + * continue processing rest of data/controls, beginning with URG + */ + case TCPS_SYN_SENT: + if ((tiflags & TH_ACK) && +/* this should be SEQ_LT; is SEQ_LEQ for BBN vax TCP only */ + (SEQ_LT(ti->ti_ack, tp->iss) || + SEQ_GT(ti->ti_ack, tp->snd_max))) + goto dropwithreset; + if (tiflags & TH_RST) { + if (tiflags & TH_ACK) { + tcp_drop(tp, ECONNREFUSED); + tp = 0; } - nstate = CLOSE_WAIT; - goto done; + goto drop; } - if (n->th_flags&TH_ACK) { - present_data(tp); /* 11 */ - nstate = ESTAB; + if ((tiflags & TH_SYN) == 0) + goto drop; + tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + tp->t_timer[TCPT_REXMT] = 0; + tp->irs = ti->ti_seq; + tcp_rcvseqinit(tp); + tp->t_flags |= TF_ACKNOW; + if (SEQ_GT(tp->snd_una, tp->iss)) { + if (so->so_options & SO_ACCEPTCONN) + so->so_state |= SS_CONNAWAITING; + soisconnected(so); + tp->t_state = TCPS_ESTABLISHED; + (void) tcp_reass(tp, (struct tcpiphdr *)0); } else - nstate = SYN_RCVD; /* 8 */ - goto done; - - case SYN_RCVD: - case L_SYN_RCVD: - if ((n->th_flags&TH_ACK) == 0 || - (n->th_flags&TH_ACK) && n->t_ackno <= tp->iss) { - nstate = EFAILEC; - goto done; + tp->t_state = TCPS_SYN_RECEIVED; + goto trimthenstep6; + +trimthenstep6: + /* + * Advance ti->ti_seq to correspond to first data byte. + * If data, trim to stay within window, + * dropping FIN if necessary. + */ + ti->ti_seq++; + if (ti->ti_len > tp->rcv_wnd) { + todrop = ti->ti_len - tp->rcv_wnd; + m_adj(m, -todrop); + ti->ti_len = tp->rcv_wnd; + ti->ti_flags &= ~TH_FIN; } - goto input; - - case ESTAB: - case FIN_W1: - case FIN_W2: - case TIME_WAIT: -input: - rcv_ctldat(tp, n, 1); /* 39 */ - present_data(tp); - switch (tp->t_state) { - - case ESTAB: - if (tp->tc_flags&TC_FIN_RCVD) - nstate = CLOSE_WAIT; - break; - - case SYN_RCVD: - case L_SYN_RCVD: - nstate = (tp->tc_flags&TC_FIN_RCVD) ? - CLOSE_WAIT : ESTAB; /* 33:5 */ - break; + tp->snd_wl1 = ti->ti_seq - 1; + goto step6; + } - case FIN_W1: - j = ack_fin(tp, n); - if ((tp->tc_flags & TC_FIN_RCVD) == 0) { - if (j) - nstate = FIN_W2; /* 27 */ - break; + /* + * States other than LISTEN or SYN_SENT. + * First check that at least some bytes of segment are within + * receive window. + */ + if (tp->rcv_wnd == 0) { + /* + * If window is closed can only take segments at + * window edge, and have to drop data and PUSH from + * incoming segments. + */ + if (tp->rcv_nxt != ti->ti_seq) + goto dropafterack; + if (ti->ti_len > 0) { + m_adj(m, ti->ti_len); + ti->ti_len = 0; + ti->ti_flags &= ~(TH_PUSH|TH_FIN); + } + } else { + /* + * If segment begins before rcv_nxt, drop leading + * data (and SYN); if nothing left, just ack. + */ + todrop = tp->rcv_nxt - ti->ti_seq; + if (todrop > 0) { + if (tiflags & TH_SYN) { + tiflags &= ~TH_SYN; + ti->ti_flags &= ~TH_SYN; + ti->ti_seq++; + if (ti->ti_urp > 1) + ti->ti_urp--; + else + tiflags &= ~TH_URG; + todrop--; } - tp->t_finack = T_2ML; - tp->tc_flags &= ~TC_WAITED_2_ML; - nstate = j ? TIME_WAIT : CLOSING1; /* 28:26 */ - break; - - case FIN_W2: - if (tp->tc_flags&TC_FIN_RCVD) { - tp->t_finack = T_2ML; /* 29 */ - tp->tc_flags &= ~TC_WAITED_2_ML; - nstate = TIME_WAIT; - break; + if (todrop > ti->ti_len || + todrop == ti->ti_len && (tiflags&TH_FIN) == 0) + goto dropafterack; + m_adj(m, todrop); + ti->ti_seq += todrop; + ti->ti_len -= todrop; + if (ti->ti_urp > todrop) + ti->ti_urp -= todrop; + else { + tiflags &= ~TH_URG; + ti->ti_flags &= ~TH_URG; + ti->ti_urp = 0; } - break; - } - goto done; - - case CLOSE_WAIT: - if (n->th_flags&TH_FIN) { - if ((n->th_flags&TH_ACK) && - n->t_ackno <= tp->seq_fin) { - rcv_ctldat(tp, n, 0); /* 30 */ - tp->t_finack = T_2ML; - tp->tc_flags &= ~TC_WAITED_2_ML; - } else - send_ctl(tp); /* 31 */ - goto done; - } - goto input; - - case CLOSING1: - j = ack_fin(tp, n); - if (n->th_flags&TH_FIN) { - rcv_ctldat(tp, n, 0); - tp->t_finack = T_2ML; - tp->tc_flags &= ~TC_WAITED_2_ML; - if (j) - nstate = TIME_WAIT; /* 23 */ - goto done; - } - if (j) { - if (tp->tc_flags&TC_WAITED_2_ML) - if (rcv_empty(tp)) { - t_close(tp, UCLOSED); /* 15 */ - nstate = CLOSED; - } else - nstate = RCV_WAIT; /* 18 */ - else - nstate = TIME_WAIT; - goto done; - } - goto input; - - case CLOSING2: - if (ack_fin(tp, n)) { - if (rcv_empty(tp)) { /* 16 */ - t_close(tp, UCLOSED); - nstate = CLOSED; - } else - nstate = RCV_WAIT; /* 19 */ - goto done; } - if (n->th_flags&TH_FIN) { - send_ctl(tp); /* 31 */ - goto done; + /* + * If segment ends after window, drop trailing data + * (and PUSH and FIN); if nothing left, just ACK. + */ + todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); + if (todrop > 0) { + if (todrop >= ti->ti_len) + goto dropafterack; + m_adj(m, -todrop); + ti->ti_len -= todrop; + ti->ti_flags &= ~(TH_PUSH|TH_FIN); } - goto input; - - case RCV_WAIT: - if ((n->th_flags&TH_FIN) && (n->th_flags&TH_ACK) && - n->t_ackno <= tp->seq_fin) { - rcv_ctldat(tp, n, 0); - tp->t_finack = T_2ML; - tp->tc_flags &= ~TC_WAITED_2_ML; /* 30 */ - } - goto done; } - panic("tcp_input"); -done: /* - * Done with state*input specific processing. - * Form trace records, free input if not needed, - * and enter new state. + * If a segment is received on a connection after the + * user processes are gone, then RST the other end. */ -#ifdef TCPDEBUG - if (tdb.td_tod) - tdb_stuff(&tdb, nstate); -#endif - switch (nstate) { - - case EFAILEC: - m_freem(mp); - return; - - default: - tp->t_state = nstate; - /* fall into ... */ - - case CLOSED: - /* IF CLOSED CANT LOOK AT tc_flags */ - if ((tp->tc_flags&TC_NET_KEEP) == 0) - m_freem(mp); - return; + if (so->so_state & SS_USERGONE) { + tcp_close(tp); + tp = 0; + goto dropwithreset; } - /* NOTREACHED */ /* - * Unwanted packed; free everything - * but the header and return an rst. + * If the RST bit is set examine the state: + * SYN_RECEIVED STATE: + * If passive open, return to LISTEN state. + * If active open, inform user that connection was refused. + * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: + * Inform user that connection was reset, and close tcb. + * CLOSING, LAST_ACK, TIME_WAIT STATES + * Close the tcb. */ -notwanted: - m_freem(mp->m_next); - mp->m_next = NULL; - mp->m_len = sizeof(struct th); -#define xchg(a,b) j=a; a=b; b=j - xchg(n->t_d.s_addr, n->t_s.s_addr); xchg(n->t_dst, n->t_src); -#undef xchg - if (n->th_flags&TH_ACK) - n->t_seq = n->t_ackno; - else { - n->t_ackno = htonl(ntohl(n->t_seq) + tlen - hlen); - n->t_seq = 0; + if (tiflags&TH_RST) switch (tp->t_state) { + + case TCPS_SYN_RECEIVED: + if (inp->inp_socket->so_options & SO_ACCEPTCONN) { + /* a miniature tcp_close, but invisible to user */ + (void) m_free(dtom(tp->t_template)); + (void) m_free(dtom(tp)); + inp->inp_ppcb = 0; + tp = tcp_newtcpcb(inp); + tp->t_state = TCPS_LISTEN; + inp->inp_faddr.s_addr = 0; + inp->inp_fport = 0; + inp->inp_laddr.s_addr = 0; /* not quite right */ + tp = 0; + goto drop; + } + tcp_drop(tp, ECONNREFUSED); + tp = 0; + goto drop; + + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + tcp_drop(tp, ECONNRESET); + tp = 0; + goto drop; + + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: + tcp_close(tp); + tp = 0; + goto drop; } - n->th_flags = TH_RST; /* not TH_FIN, TH_SYN */ - n->th_flags ^= TH_ACK; - n->t_len = htons(TCPSIZE); - n->t_off = 5; - n->t_sum = cksum(mp, sizeof(struct th)); - ((struct ip *)n)->ip_len = sizeof(struct th); - ip_output(mp); - netstat.t_badsegs++; -} -rcv_ctldat(tp, n, dataok) - register struct tcb *tp; - register struct th *n; -{ - register sent; - register struct ucb *up; - register struct mbuf *m, *mn; - register len; -COUNT(RCV_CTLDAT); - - tp->tc_flags &= ~(TC_DROPPED_TXT|TC_ACK_DUE|TC_NEW_WINDOW); -/* syn */ - if ((tp->tc_flags&TC_SYN_RCVD) == 0 && (n->th_flags&TH_SYN)) { - tp->irs = n->t_seq; - tp->rcv_nxt = n->t_seq + 1; - tp->snd_wl = tp->rcv_urp = tp->irs; - tp->tc_flags |= (TC_SYN_RCVD|TC_ACK_DUE); + /* + * If a SYN is in the window, then this is an + * error and we send an RST and drop the connection. + */ + if (tiflags & TH_SYN) { + tcp_drop(tp, ECONNRESET); + tp = 0; + goto dropwithreset; } -/* ack */ - if ((n->th_flags&TH_ACK) && (tp->tc_flags&TC_SYN_RCVD) && - n->t_ackno > tp->snd_una) { - up = tp->t_ucb; - - /* update snd_una and snd_nxt */ - tp->snd_una = n->t_ackno; - if (tp->snd_una > tp->snd_nxt) + + /* + * If the ACK bit is off we drop the segment and return. + */ + if ((tiflags & TH_ACK) == 0) + goto drop; + + /* + * Ack processing. + */ + switch (tp->t_state) { + + /* + * In SYN_RECEIVED state if the ack ACKs our SYN then enter + * ESTABLISHED state and continue processing, othewise + * send an RST. + */ + case TCPS_SYN_RECEIVED: + if (SEQ_GT(tp->snd_una, ti->ti_ack) || + SEQ_GT(ti->ti_ack, tp->snd_max)) + goto dropwithreset; + tp->snd_una++; /* SYN acked */ + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; + tp->t_timer[TCPT_REXMT] = 0; + if (so->so_options & SO_ACCEPTCONN) + so->so_state |= SS_CONNAWAITING; + soisconnected(so); + tp->t_state = TCPS_ESTABLISHED; + (void) tcp_reass(tp, (struct tcpiphdr *)0); + tp->snd_wl1 = ti->ti_seq - 1; + /* fall into ... */ - /* if timed msg acked, set retrans time value */ - if ((tp->tc_flags&TC_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; + /* + * In ESTABLISHED state: drop duplicate ACKs; ACK out of range + * ACKs. If the ack is in the range + * tp->snd_una < ti->ti_ack <= tp->snd_max + * then advance tp->snd_una to ti->ti_ack and drop + * data from the retransmission queue. If this ACK reflects + * more up to date window information we update our window information. + */ + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: +#define ourfinisacked (acked > 0) + + if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) + break; + if (SEQ_GT(ti->ti_ack, tp->snd_max)) + goto dropafterack; + acked = ti->ti_ack - tp->snd_una; + + /* + * If transmit timer is running and timed sequence + * number was acked, update smoothed round trip time. + */ + if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) { + if (tp->t_srtt == 0) + tp->t_srtt = tp->t_rtt; + else + tp->t_srtt = + tcp_alpha * tp->t_srtt + + (1 - tcp_alpha) * tp->t_rtt; +/* printf("rtt %d srtt*100 now %d\n", tp->t_rtt, (int)(tp->t_srtt*100)); */ + tp->t_rtt = 0; } - /* remove acked data from send buf */ - len = tp->snd_una - tp->snd_off; - m = up->uc_sbuf; - while (len > 0 && m != NULL) - if (m->m_len <= len) { - len -= m->m_len; - if (m->m_off > MMAXOFF) - up->uc_ssize -= NMBPG; - MFREE(m, mn); - m = mn; - up->uc_ssize--; - } else { - m->m_len -= len; - m->m_off += len; - break; - } - up->uc_sbuf = m; - tp->snd_off = tp->snd_una; - if ((tp->tc_flags&TC_SYN_ACKED) == 0 && - (tp->snd_una > tp->iss)) { - tp->tc_flags |= TC_SYN_ACKED; - tp->t_init = 0; + if (ti->ti_ack == tp->snd_max) + tp->t_timer[TCPT_REXMT] = 0; + else { + TCPT_RANGESET(tp->t_timer[TCPT_REXMT], + tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX); + tp->t_rtt = 1; + tp->t_rxtshift = 0; } - if (tp->seq_fin != tp->iss && tp->snd_una > tp->seq_fin) - tp->tc_flags &= ~TC_SND_FIN; - tp->t_rexmt = 0; - tp->t_rexmttl = 0; - tp->tc_flags |= TC_CANCELLED; - netwakeup(tp->t_ucb); /* wasteful */ - } -/* win */ - if ((tp->tc_flags & TC_SYN_RCVD) && n->t_seq >= tp->snd_wl) { - tp->snd_wl = n->t_seq; - tp->snd_wnd = n->t_win; - tp->tc_flags |= TC_NEW_WINDOW; - tp->t_persist = 0; - } - if (dataok) { -/* text */ - if (n->t_len != 0) - rcv_text(tp, n); -/* urg */ - if (n->th_flags&TH_URG) { - unsigned urgent; - - urgent = n->t_urp + n->t_seq; - if (tp->rcv_nxt < urgent) { - if (tp->rcv_urp <= tp->rcv_nxt) - to_user(tp->t_ucb, UURGENT); - tp->rcv_urp = urgent; - } + if (acked > so->so_snd.sb_cc) { + sbdrop(&so->so_snd, so->so_snd.sb_cc); + tp->snd_wnd -= so->so_snd.sb_cc; + } else { + sbdrop(&so->so_snd, acked); + tp->snd_wnd -= acked; + acked = 0; } -/* eol */ - if ((n->th_flags&TH_EOL) && - (tp->tc_flags&TC_DROPPED_TXT) == 0 && - tp->t_rcv_prev != (struct th *)tp) { - /* mark last mbuf */ - 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); + if ((so->so_snd.sb_flags & SB_WAIT) || so->so_snd.sb_sel) + sowwakeup(so); + tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + + switch (tp->t_state) { + + /* + * In FIN_WAIT_1 STATE in addition to the processing + * for the ESTABLISHED state if our FIN is now acknowledged + * then enter FIN_WAIT_2. + */ + case TCPS_FIN_WAIT_1: + if (ourfinisacked) { + /* + * If we can't receive any more + * data, then closing user can proceed. + */ + if (so->so_state & SS_CANTRCVMORE) + soisdisconnected(so); + tp->t_state = TCPS_FIN_WAIT_2; } - } - } -/* fin */ - if ((n->th_flags&TH_FIN) && (tp->tc_flags&TC_DROPPED_TXT) == 0) { - int last; - - if ((tp->tc_flags&TC_FIN_RCVD) == 0) { - /* do we really have fin ? */ - last = firstempty(tp); - if (tp->t_rcv_prev == (struct th *)tp || - last == t_end(tp->t_rcv_prev)) { - tp->tc_flags |= TC_FIN_RCVD; - netwakeup(tp->t_ucb); /* poke */ + break; + + /* + * In CLOSING STATE in addition to the processing for + * the ESTABLISHED state if the ACK acknowledges our FIN + * then enter the TIME-WAIT state, otherwise ignore + * the segment. + */ + case TCPS_CLOSING: + if (ourfinisacked) { + tp->t_state = TCPS_TIME_WAIT; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisdisconnected(so); } - if ((tp->tc_flags&TC_FIN_RCVD) && - tp->rcv_nxt >= last) { - tp->rcv_nxt = last + 1; /* fin seq */ - tp->tc_flags |= TC_ACK_DUE; + break; + + /* + * The only thing that can arrive in LAST_ACK state + * is an acknowledgment of our FIN. If our FIN is now + * acknowledged, delete the TCB, enter the closed state + * and return. + */ + case TCPS_LAST_ACK: + if (ourfinisacked) { + tcp_close(tp); + tp = 0; } - } else - tp->tc_flags |= TC_ACK_DUE; + goto drop; + + /* + * In TIME_WAIT state the only thing that should arrive + * is a retransmission of the remote FIN. Acknowledge + * it and restart the finack timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + goto dropafterack; + } +#undef ourfinisacked } -/* respond */ - if (tp->tc_flags&TC_ACK_DUE) - sent = send_ctl(tp); - else if (tp->tc_flags&TC_NEW_WINDOW) - sent = send(tp); - else - sent = 0; - -/* set for retrans */ - if (!sent && tp->snd_una < tp->snd_nxt && - (tp->tc_flags&TC_CANCELLED)) { - tp->t_rexmt = tp->t_xmtime; - tp->t_rexmttl = T_REXMTTL; - tp->t_rexmt_val = tp->t_rtl_val = tp->snd_lst; - tp->tc_flags &= ~TC_CANCELLED; +step6: + /* + * Update window information. + */ + if (SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq && + (SEQ_LT(tp->snd_wl2, ti->ti_ack) || + tp->snd_wl2 == ti->ti_ack && ti->ti_win > tp->snd_wnd)) { + tp->snd_wnd = ti->ti_win; + tp->snd_wl1 = ti->ti_seq; + tp->snd_wl2 = ti->ti_ack; + if (tp->snd_wnd > 0) + tp->t_timer[TCPT_PERSIST] = 0; } -} -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 last, j, k; -COUNT(RCV_TEXT); - - /* throw away any data we have already received */ - if ((i = tp->rcv_nxt - t->t_seq) > 0) { - if (i >= t->t_len) - return; - t->t_seq += i; - t->t_len -= i; - m_adj(dtom(t), i); + /* + * Process segments with URG. + */ + if ((tiflags & TH_URG) && ti->ti_urp && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* + * If this segment advances the known urgent pointer, + * then mark the data stream. This should not happen + * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since + * a FIN has been received from the remote side. + * In these states we ignore the URG. + */ + if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) { + tp->rcv_up = ti->ti_seq + ti->ti_urp; + so->so_oobmark = so->so_rcv.sb_cc + + (tp->rcv_up - tp->rcv_nxt) - 1; + if (so->so_oobmark == 0) + so->so_state |= SS_RCVATMARK; +#ifdef TCPTRUEOOB + if ((tp->t_flags & TF_DOOOB) == 0) +#endif + sohasoutofband(so); + tp->t_oobflags &= ~TCPOOB_HAVEDATA; + } + /* + * Remove out of band data so doesn't get presented to user. + * This can happen independent of advancing the URG pointer, + * but if two URG's are pending at once, some out-of-band + * data may creep in... ick. + */ + if (ti->ti_urp <= ti->ti_len) { + tcp_pulloutofband(so, ti); + } } - last = t_end(t); /* last seq # in incoming seg */ - i = rcv_resource(tp); /* # buffers available to con */ - - /* count buffers in segment */ + /* + * Process the segment text, merging it into the TCP sequencing queue, + * and arranging for acknowledgment of receipt if necessary. + * This process logically involves adjusting tp->rcv_wnd as data + * is presented to the user (this happens in tcp_usrreq.c, + * case PRU_RCVD). If a FIN has already been received on this + * connection then we just ignore the text. + */ + if ((ti->ti_len || (tiflags&TH_FIN)) && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + tiflags = tcp_reass(tp, ti); + if (tcpnodelack == 0) + tp->t_flags |= TF_DELACK; + else + tp->t_flags |= TF_ACKNOW; + } else { + m_freem(m); + tiflags &= ~TH_FIN; + } - for (m = dtom(t), j = 0; m != NULL; m = m->m_next) - if (m->m_len != 0) { - j++; - if (m->m_off > MMAXOFF) - j += NMBPG; + /* + * If FIN is received ACK the FIN and let the user know + * that the connection is closing. + */ + if (tiflags & TH_FIN) { + if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { + socantrcvmore(so); + tp->t_flags |= TF_ACKNOW; + tp->rcv_nxt++; } + switch (tp->t_state) { - /* 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 */ + /* + * In SYN_RECEIVED and ESTABLISHED STATES + * enter the CLOSE_WAIT state. + */ + case TCPS_SYN_RECEIVED: + case TCPS_ESTABLISHED: + tp->t_state = TCPS_CLOSE_WAIT; + break; - q = tp->t_rcv_next; - if (q != (struct th *)tp && tp->rcv_nxt < q->t_seq && - t->t_seq < q->t_seq) + /* + * If still in FIN_WAIT_1 STATE FIN has not been acked so + * enter the CLOSING state. + */ + case TCPS_FIN_WAIT_1: + tp->t_state = TCPS_CLOSING; + break; - for (k=j-i, p = tp->t_rcv_prev; k > 0 && - p != (struct th *)tp; k--) { - savq = p->t_prev; - tcp_deq(p); - i += m_freem(dtom(p)); - p = savq; - } + /* + * In FIN_WAIT_2 state enter the TIME_WAIT state, + * starting the time-wait timer, turning off the other + * standard timers. + */ + case TCPS_FIN_WAIT_2: + tp->t_state = TCPS_TIME_WAIT; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisdisconnected(so); + break; - /* if still not enough room, drop text from end of segment */ + /* + * In TIME_WAIT state restart the 2 MSL time_wait timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + break; + } + } + if (so->so_options & SO_DEBUG) + tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0); - if (j > i) { + /* + * Return any desired output. + */ + (void) tcp_output(tp); + return; - for (m = dtom(t); i > 0 && m != NULL; i--) - m = m->m_next; +dropafterack: + /* + * Generate an ACK dropping incoming segment if it occupies + * sequence space, where the ACK reflects our state. + */ + if ((tiflags&TH_RST) || + tlen == 0 && (tiflags&(TH_SYN|TH_FIN)) == 0) + goto drop; + if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) + tcp_trace(TA_RESPOND, ostate, tp, &tcp_saveti, 0); + tcp_respond(tp, ti, tp->rcv_nxt, tp->snd_nxt, TH_ACK); + return; - while (m != NULL) { - t->t_len -= m->m_len; - last -= m->m_len; - m->m_len = 0; - m = m->m_next; - } - tp->tc_flags |= TC_DROPPED_TXT; - if (last < t->t_seq) - return; - } +dropwithreset: + if (om) + (void) m_free(om); + /* + * Generate a RST, dropping incoming segment. + * Make ACK acceptable to originator of segment. + */ + if (tiflags & TH_RST) + goto drop; + if (tiflags & TH_ACK) + tcp_respond(tp, ti, (tcp_seq)0, ti->ti_ack, TH_RST); + else { + if (tiflags & TH_SYN) + ti->ti_len++; + tcp_respond(tp, ti, ti->ti_seq+ti->ti_len, (tcp_seq)0, + TH_RST|TH_ACK); } + return; - /* merge incoming data into the sequence queue */ +drop: + /* + * Drop space held by incoming segment and return. + */ + if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) + tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0); + m_freem(m); + return; +} - q = tp->t_rcv_next; /* -> top of sequencing queue */ +tcp_dooptions(tp, om) + struct tcpcb *tp; + struct mbuf *om; +{ + register u_char *cp; + int opt, optlen, cnt; + + cp = mtod(om, u_char *); + cnt = om->m_len; + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else + optlen = cp[1]; + switch (opt) { - /* skip frags which new doesn't overlap at end */ + default: + break; - while ((q != (struct th *)tp) && (t->t_seq > t_end(q))) - q = q->t_next; + case TCPOPT_MAXSEG: + if (optlen != 4) + continue; + tp->t_maxseg = *(u_short *)(cp + 2); +#if vax || pdp11 + tp->t_maxseg = ntohs((u_short)tp->t_maxseg); +#endif + break; + +#ifdef TCPTRUEOOB + case TCPOPT_WILLOOB: + tp->t_flags |= TF_DOOOB; +printf("tp %x dooob\n", tp); + break; - if (q == (struct th *)tp) { /* frag at end of chain */ + case TCPOPT_OOBDATA: { + int seq; + register struct socket *so = tp->t_inpcb->inp_socket; + tcp_seq mark; - if (last >= tp->rcv_nxt) { - tp->tc_flags |= TC_NET_KEEP; - tcp_enq(t, tp->t_rcv_prev); + if (optlen != 8) + continue; + seq = cp[2]; + if (seq < tp->t_iobseq) + seq += 256; +printf("oobdata cp[2] %d iobseq %d seq %d\n", cp[2], tp->t_iobseq, seq); + if (seq - tp->t_iobseq > 128) { +printf("bad seq\n"); + tp->t_oobflags |= TCPOOB_OWEACK; + break; + } + tp->t_iobseq = cp[2]; + tp->t_iobc = cp[3]; + mark = *(tcp_seq *)(cp + 4); +#if vax || pdp11 + mark = ntohl(mark); +#endif + so->so_oobmark = so->so_rcv.sb_cc + (mark-tp->rcv_nxt); + if (so->so_oobmark == 0) + so->so_state |= SS_RCVATMARK; +printf("take oob data %x input iobseq now %x\n", tp->t_iobc, tp->t_iobseq); + sohasoutofband(so); + break; } - } else { - - /* frag doesn't overlap any on chain */ - - if (last < q->t_seq) { - tp->tc_flags |= TC_NET_KEEP; - 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->tc_flags |= TC_NET_KEEP; - 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)); + case TCPOPT_OOBACK: { + int seq; - } 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->tc_flags |= TC_NET_KEEP; - tcp_enq(t, savq); + if (optlen != 4) + continue; + if (tp->t_oobseq != cp[2]) { +printf("wrong ack\n"); + break; } - } - } - - /* set to ack completed data (no gaps) */ - - tp->rcv_nxt = firstempty(tp); - tp->tc_flags |= TC_ACK_DUE; - -#ifdef notdef - /* THIS CODE CANT POSSIBLY WORK */ - /* 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++; - tcp_work(INRECV, 0, tp, t); - - /* remaining buffer space */ - - for (n = m; n != NULL; n = n->m_next) - i--; +printf("take oob ack %x and cancel rexmt\n", cp[2]); + tp->t_oobflags &= ~TCPOOB_NEEDACK; + tp->t_timer[TCPT_OOBREXMT] = 0; + break; + } +#endif TCPTRUEOOB + } } -#endif + (void) m_free(om); } -present_data(tp) - register struct tcb *tp; +/* + * Pull out of band byte out of a segment so + * it doesn't appear in the user's data queue. + * It is still reflected in the segment length for + * sequencing purposes. + */ +tcp_pulloutofband(so, ti) + struct socket *so; + struct tcpiphdr *ti; { - register struct th *t; - register struct ucb *up; - register struct mbuf *m, **mp; - seq_t ready; -COUNT(PRESENT_DATA); - - /* connection must be synced and data available for user */ - if (((tp->tc_flags&TC_SYN_ACKED) == 0) || - (t = tp->t_rcv_next) == (struct th *)tp) - return; - up = tp->t_ucb; - ready = firstempty(tp); /* seq # of last complete datum */ - mp = &up->uc_rbuf; - while (*mp) - mp = &(*mp)->m_next; - while (up->uc_rsize < up->uc_rcv && t != (struct th *) tp && - t_end(t) < ready) { - tcp_deq(t); - m = dtom(t); - t = t->t_next; - while (m) { - if (m->m_len == 0) { - m = m_free(m); - continue; - } - up->uc_rsize++; - if (m->m_off > MMAXOFF) - up->uc_rsize += NMBPG; - if (*mp == 0) - *mp = m; - mp = &m->m_next; - m = *mp; + register struct mbuf *m; + int cnt = ti->ti_urp - 1; + + m = dtom(ti); + while (cnt >= 0) { + if (m->m_len > cnt) { + char *cp = mtod(m, caddr_t) + cnt; + struct tcpcb *tp = sototcpcb(so); + + tp->t_iobc = *cp; + tp->t_oobflags |= TCPOOB_HAVEDATA; + bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1)); + m->m_len--; + return; } + cnt -= m->m_len; + m = m->m_next; + if (m == 0) + break; } - if (up->uc_rsize != 0) - netwakeup(up); - /* - * Let user know about foreign tcp close if no more data. - */ - if ((tp->tc_flags&TC_FIN_RCVD) && (tp->tc_flags&TC_USR_CLOSED) == 0 && - rcv_empty(tp)) - to_user(up, UCLOSED); + panic("tcp_pulloutofband"); } -#ifdef TCPDEBUG -tdb_setup(tp, n, input, tdp) - struct tcb *tp; - register struct th *n; - int input; - register struct tcp_debug *tdp; +/* + * Insert segment ti into reassembly queue of tcp with + * control block tp. Return TH_FIN if reassembly now includes + * a segment with FIN. + */ +tcp_reass(tp, ti) + register struct tcpcb *tp; + register struct tcpiphdr *ti; { + register struct tcpiphdr *q; + struct socket *so = tp->t_inpcb->inp_socket; + struct mbuf *m; + int flags; - tdp->td_tod = time; - tdp->td_tcb = tp; - tdp->td_old = tp->t_state; - tdp->td_inp = input; - tdp->td_tim = 0; - tdp->td_new = -1; - if (n) { - tdp->td_sno = n->t_seq; - tdp->td_ano = n->t_ackno; - tdp->td_wno = n->t_win; - tdp->td_lno = n->t_len; - tdp->td_flg = n->th_flags; - } else - tdp->td_sno = tdp->td_ano = tdp->td_wno = tdp->td_lno = - tdp->td_flg = 0; -} + /* + * Call with ti==0 after become established to + * force pre-ESTABLISHED data up to user socket. + */ + if (ti == 0) + goto present; -tdb_stuff(tdp, nstate) - struct tcp_debug *tdp; - int nstate; -{ + /* + * Find a segment which begins after this one does. + */ + for (q = tp->seg_next; q != (struct tcpiphdr *)tp; + q = (struct tcpiphdr *)q->ti_next) + if (SEQ_GT(q->ti_seq, ti->ti_seq)) + break; + + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) { + register int i; + q = (struct tcpiphdr *)q->ti_prev; + /* conversion to int (in i) handles seq wraparound */ + i = q->ti_seq + q->ti_len - ti->ti_seq; + if (i > 0) { + if (i >= ti->ti_len) + goto drop; + m_adj(dtom(ti), i); + ti->ti_len -= i; + ti->ti_seq += i; + } + q = (struct tcpiphdr *)(q->ti_next); + } - tdp->td_new = nstate; - tcp_debug[tdbx++ % TDBSIZE] = *tdp; - if (tcpconsdebug & 2) - tcp_prt(tdp); + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (q != (struct tcpiphdr *)tp) { + register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; + if (i <= 0) + break; + if (i < q->ti_len) { + q->ti_seq += i; + q->ti_len -= i; + m_adj(dtom(q), i); + break; + } + q = (struct tcpiphdr *)q->ti_next; + m = dtom(q->ti_prev); + remque(q->ti_prev); + m_freem(m); + } + + /* + * Stick new segment in its place. + */ + insque(ti, q->ti_prev); + +present: + /* + * Present data to user, advancing rcv_nxt through + * completed sequence space. + */ + if (TCPS_HAVERCVDSYN(tp->t_state) == 0) + return (0); + ti = tp->seg_next; + if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt) + return (0); + if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len) + return (0); + do { + tp->rcv_nxt += ti->ti_len; + flags = ti->ti_flags & TH_FIN; + remque(ti); + m = dtom(ti); + ti = (struct tcpiphdr *)ti->ti_next; + if (so->so_state & SS_CANTRCVMORE) + m_freem(m); + else + sbappend(&so->so_rcv, m); + } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt); + sorwakeup(so); + return (flags); +drop: + m_freem(dtom(ti)); + return (0); } -#endif