X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/1b40e06f897c17a52d43739cff3b6f266041a4e0..4ab1a5c3d51e670b73ca6e3477d72344edf9b224:/usr/src/sys/netinet/tcp_input.c diff --git a/usr/src/sys/netinet/tcp_input.c b/usr/src/sys/netinet/tcp_input.c index ef91f5ce6c..d00ead02e7 100644 --- a/usr/src/sys/netinet/tcp_input.c +++ b/usr/src/sys/netinet/tcp_input.c @@ -1,576 +1,885 @@ -/* tcp_input.c 1.8 81/10/30 */ +/* tcp_input.c 1.71 82/06/30 */ #include "../h/param.h" #include "../h/systm.h" #include "../h/mbuf.h" +#include "../h/protosw.h" #include "../h/socket.h" -#include "../inet/inet.h" -#include "../inet/inet_systm.h" -#include "../inet/imp.h" -#include "../inet/inet_host.h" -#include "../inet/ip.h" -#include "../inet/tcp.h" -#include "../inet/tcp_fsm.h" - -extern int nosum; - -tcp_input(mp) - register struct mbuf *mp; +#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); + 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 *); + } - /* 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; } + /* + * Checksum extended TCP header and data. + */ + 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; + } + } /* - * Checksum extended header and data + * Check that TCP offset makes sense, + * pull out TCP options and adjust length. */ - 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; + 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; + + /* + * Drop TCP and IP headers. + */ + off += sizeof (struct ip); + m->m_off += off; + m->m_len -= off; + +#if vax || pdp11 + /* + * Convert TCP protocol specific fields to host format. + */ + 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 + + /* + * 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); + + /* + * 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; + } /* - * Find tcb for message (SHOULDN'T USE LINEAR SEARCH!) + * Segment received on connection. + * Reset idle time and keep-alive timer. */ - for (tp = 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 = 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: + tp->t_idle = 0; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; /* - * Byte swap header + * Process options. */ - 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); + if (om) { + tcp_dooptions(tp, om); + om = 0; + } /* - * Check segment seq # and do rst processing + * Calculate amount of space in receive window, + * and then do TCP input processing. */ + tp->rcv_wnd = sbspace(&so->so_rcv); + if (tp->rcv_wnd < 0) + tp->rcv_wnd = 0; + 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 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; } - if (n->th_flags&TH_RST) { - t_close(tp, URESET); /* 70 */ - tp->t_state = CLOSED; - goto badseg; + tp->t_template = tcp_template(tp); + if (tp->t_template == 0) { + in_pcbdisconnect(inp); + inp->inp_laddr = laddr; + tp = 0; + goto drop; } - 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) { + 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 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; - - default: - t_close(tp, URESET); /* 66 */ - tp->t_state = CLOSED; - goto badseg; + /* + * 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; + } + goto drop; } - /*NOTREACHED*/ + 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 + 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; + } + tp->snd_wl1 = ti->ti_seq - 1; + goto step6; + } - case SYN_RCVD: -common: - if (ack_ok(tp, n) == 0) { - send_rst(tp, n); /* 74 */ - goto badseg; + /* + * 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); } - if (syn_ok(tp, n) && n->t_seq != tp->irs) { - send_null(tp); /* 74 */ - goto badseg; + } 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--; + } + 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; + } + } + /* + * 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 goodseg; } -badseg: - m_freem(mp); - return; -goodseg: -#ifdef notdef /* - * Defer processing if no buffer space for this connection. + * If a segment is received on a connection after the + * user processes are gone, then RST the other end. */ - up = tp->t_ucb; - if (up->uc_rcc > up->uc_rhiwat && - && n->t_len != 0 && mbstat.m_bufs < mbstat.m_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; - return; + if (so->so_state & SS_USERGONE) { + tcp_close(tp); + tp = 0; + goto dropwithreset; } -#endif /* - * Discard ip header, and do tcp input processing. + * 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. + */ + 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; + } + + /* + * 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; + } + + /* + * If the ACK bit is off we drop the segment and return. + */ + if ((tiflags & TH_ACK) == 0) + goto drop; + + /* + * Ack 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 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; - } - 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; - } - goto done; + /* + * 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 ... */ - case SYN_SENT: - if (!syn_ok(tp, n)) { - nstate = EFAILEC; - goto done; + /* + * 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; } - 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; - } - nstate = CLOSE_WAIT; - goto done; + + 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 (n->th_flags&TH_ACK) { - present_data(tp); /* 11 */ - nstate = ESTAB; - } 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; + 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; } - 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) { + 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; - case ESTAB: - if (tp->tc_flags&TC_FIN_RCVD) - nstate = CLOSE_WAIT; - break; + switch (tp->t_state) { - case SYN_RCVD: - case L_SYN_RCVD: - nstate = (tp->tc_flags&TC_FIN_RCVD) ? - CLOSE_WAIT : ESTAB; /* 33:5 */ + /* + * 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; + } break; - case FIN_W1: - j = ack_fin(tp, n); - if ((tp->tc_flags & TC_FIN_RCVD) == 0) { - if (j) - nstate = FIN_W2; /* 27 */ - 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); } - 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; + /* + * 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; } - 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; + 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; } - if (n->th_flags&TH_FIN) { - send_ctl(tp); /* 31 */ - goto done; +#undef ourfinisacked + } + +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; + } + + /* + * 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; } - 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 */ + /* + * 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); } - goto done; } - panic("tcp_input"); -done: /* - * Done with state*input specific processing. - * Form trace records, free input if not needed, - * and enter new state. + * 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. */ -#ifdef TCPDEBUG - if (tdb.td_tod) - tdb_stuff(&tdb, nstate); -#endif - switch (nstate) { + 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; + } - case EFAILEC: - m_freem(mp); - return; + /* + * 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) { - default: - tp->t_state = nstate; - /* fall into ... */ + /* + * 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; - case CLOSED: - /* IF CLOSED CANT LOOK AT tc_flags */ - if ((tp->tc_flags&TC_NET_KEEP) == 0) - m_freem(mp); - return; + /* + * 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; + + /* + * 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; + + /* + * In TIME_WAIT state restart the 2 MSL time_wait timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + break; + } } - /* NOTREACHED */ + if (so->so_options & SO_DEBUG) + tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0); /* - * Unwanted packed; free everything - * but the header and return an rst. + * Return any desired output. */ -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; + (void) tcp_output(tp); + return; + +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; + +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 { - n->t_ackno = htonl(ntohl(n->t_seq) + tlen - hlen); - n->t_seq = 0; + if (tiflags & TH_SYN) + ti->ti_len++; + tcp_respond(tp, ti, ti->ti_seq+ti->ti_len, (tcp_seq)0, + TH_RST|TH_ACK); } - 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++; + return; + +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; } -rcv_ctldat(tp, n, dataok) - register struct tcb *tp; - register struct th *n; +tcp_dooptions(tp, om) + struct tcpcb *tp; + struct mbuf *om; { - 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); - } -/* 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) - tp->snd_nxt = tp->snd_una; + 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) { - /* 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; - } + default: + break; - /* 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; + 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; + + case TCPOPT_OOBDATA: { + int seq; + register struct socket *so = tp->t_inpcb->inp_socket; + tcp_seq mark; + + 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; } - 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; + 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; } - 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; + + case TCPOPT_OOBACK: { + int seq; + + if (optlen != 4) + continue; + if (tp->t_oobseq != cp[2]) { +printf("wrong ack\n"); + break; } +printf("take oob ack %x and cancel rexmt\n", cp[2]); + tp->t_oobflags &= ~TCPOOB_NEEDACK; + tp->t_timer[TCPT_OOBREXMT] = 0; + break; } -/* 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); - } +#endif TCPTRUEOOB } } -/* 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 */ - } - if ((tp->tc_flags&TC_FIN_RCVD) && - tp->rcv_nxt >= last) { - tp->rcv_nxt = last + 1; /* fin seq */ - tp->tc_flags |= TC_ACK_DUE; - } - } else - tp->tc_flags |= TC_ACK_DUE; - } - -/* respond */ - sent = 0; - if (tp->tc_flags&TC_ACK_DUE) - sent = send_ctl(tp); - else if (tp->tc_flags&TC_NEW_WINDOW) { - seq_t last = tp->snd_off; - up = tp->t_ucb; - for (m = up->uc_sbuf; m != NULL; m = m->m_next) - last += m->m_len; - if (tp->snd_nxt <= last || (tp->tc_flags&TC_SND_FIN)) - sent = send(tp); - } + (void) m_free(om); +} -/* 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; +/* + * 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 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; } + panic("tcp_pulloutofband"); } -rcv_text(tp, n) - register struct tcb *tp; - register struct th *n; +/* + * 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 int i; - register struct th *p, *q; - register struct mbuf *m; - int overage; -COUNT(RCV_TEXT); + register struct tcpiphdr *q; + struct socket *so = tp->t_inpcb->inp_socket; + struct mbuf *m; + int flags; /* - * Discard duplicate data already passed to user. + * Call with ti==0 after become established to + * force pre-ESTABLISHED data up to user socket. */ - if (SEQ_LT(n->t_seq, tp->rcv_nxt)) { - i = tp->rcv_nxt - n->t_seq; - if (i >= n->t_len) - goto dropseg; - n->t_seq += i; - n->t_len -= i; - m_adj(dtom(n), i); - } + if (ti == 0) + goto present; /* * Find a segment which begins after this one does. */ - for (q = tp->t_rcv_next; q != (struct th *)tp; q = q->t_next) - if (SEQ_GT(q->t_seq, n->t_seq)) + 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; /* @@ -578,127 +887,72 @@ COUNT(RCV_TEXT); * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ - if (q->t_prev != (struct th *)tp) { + 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->t_prev->t_seq + q->t_prev->t_len - n->t_seq; + i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { - if (i >= n->t_len) - goto dropseg; - m_adj(dtom(tp), i); - n->t_len -= i; - n->t_seq += i; + 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); } /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ - while (q != (struct th *)tp && SEQ_GT(n->t_seq + n->t_len, q->t_seq)) { - i = (n->t_seq + n->t_len) - q->t_seq; - if (i < q->t_len) { - q->t_len -= i; + 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 = q->t_next; - m_freem(dtom(q->t_prev)); - remque(q->t_prev); + 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(n, q->t_prev); - tp->seqcnt += n->t_len; - -#ifdef notdef - /* - * Calculate available space and discard segments for - * which there is too much. - */ - q = tp->t_rcv_prev; - overage = - (tp->t_socket->uc_rcc + tp->rcv_seqcnt) - tp->t_socket->uc_rhiwat; - if (overage > 0) - for (;;) { - i = MIN(q->t_len, overage); - overage -= i; - q->t_len -= i; - m_adj(q, -i); - if (q == n) - tp->tc_flags |= TC_DROPPED_TXT; - if (q->t_len) - break; - if (q == n) - panic("tcp_text dropall"); - q = q->t_prev; - remque(q->t_next); - } -#endif + insque(ti, q->ti_prev); +present: /* - * Advance rcv_next through - * newly completed sequence space - * and return forcing an ack. + * Present data to user, advancing rcv_nxt through + * completed sequence space. */ - while (n->t_seq == tp->rcv_nxt) { - /* present data belongs here */ - tp->rcv_nxt += n->t_len; - n = n->t_next; - if (n == (struct th *)tp) - break; - } - tp->tc_flags |= (TC_ACK_DUE|TC_NET_KEEP); - return; - -dropseg: - /* don't set TC_NET_KEEP, so that mbuf's will get dropped */ - return; -} - -#define socket ucb /* ### */ -#define t_socket t_ucb /* ### */ - -present_data(tp) - register struct tcb *tp; -{ - register struct th *t; - register struct socket *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) - return; - up = tp->t_socket; - mp = &up->uc_rbuf; - while (*mp) - mp = &(*mp)->m_next; - t = tp->t_rcv_next; - /* SHOULD PACK DATA IN HERE */ - while (t != (struct th *)tp && t->t_seq < tp->rcv_nxt) { - remque(t); - m = dtom(t); - up->uc_rcc += t->t_len; - tp->seqcnt -= t->t_len; - if (tp->seqcnt < 0) panic("present_data"); - t = t->t_next; - while (m) { - if (m->m_len == 0) { - m = m_free(m); - continue; - } - *mp = m; - mp = &m->m_next; - m = *mp; - } - } - if (up->uc_rcc != 0) - netwakeup(up); - if ((tp->tc_flags&TC_FIN_RCVD) && /* ### */ - (tp->tc_flags&TC_USR_CLOSED) == 0 && /* ### */ - rcv_empty(tp)) /* ### */ - to_user(up, UCLOSED); /* ### */ + 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); }