X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/d63599acb0be3ef81a1f3311d84a8154b8fad88c..4023eed27a3d5dfd1145560a2a8cc42880469c82:/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 16d6fc1b8c..63ac1ba44b 100644 --- a/usr/src/sys/netinet/tcp_input.c +++ b/usr/src/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* tcp_input.c 1.36 81/12/07 */ +/* tcp_input.c 1.68 82/06/20 */ #include "../h/param.h" #include "../h/systm.h" @@ -7,6 +7,7 @@ #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" @@ -18,10 +19,16 @@ #include "../net/tcp_timer.h" #include "../net/tcp_var.h" #include "../net/tcpip.h" +#include "../net/tcp_debug.h" #include "../errno.h" +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. @@ -32,77 +39,110 @@ tcp_input(m0) register struct tcpiphdr *ti; struct inpcb *inp; register struct mbuf *m; + struct mbuf *om = 0; int len, tlen, off; - register struct tcpcb *tp; + register struct tcpcb *tp = 0; register int tiflags; struct socket *so; int todrop, acked; + short ostate; + struct in_addr laddr; -COUNT(TCP_INPUT); /* - * Get ip and tcp header together in first mbuf. + * Get IP and TCP header together in first mbuf. + * Note: IP leaves IP header in first mbuf. */ m = m0; ti = mtod(m, struct tcpiphdr *); - if (ti->ti_len > sizeof (struct ip)) + if (((struct ip *)ti)->ip_hl > (sizeof (struct ip) >> 2)) ip_stripoptions((struct ip *)ti, (struct mbuf *)0); - if (m->m_len < sizeof (struct tcpiphdr)) { - if (m_pullup(m, sizeof (struct tcpiphdr)) == 0) { + if (m->m_off > MMAXOFF || m->m_len < sizeof (struct tcpiphdr)) { + if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) { tcpstat.tcps_hdrops++; - goto drop; + return; } ti = mtod(m, struct tcpiphdr *); } /* - * Checksum extended tcp header and data. + * 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 = htons((u_short)tlen); - if ((ti->ti_sum = in_cksum(m, len)) != 0xffff) { + 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++; - printf("tcp cksum %x\n", ti->ti_sum); + if (tcpprintfs) + printf("tcp cksum %x\n", ti->ti_sum); goto drop; } } /* - * Check that tcp offset makes sense, - * process tcp options and adjust length. + * Check that TCP offset makes sense, + * pull out TCP options and adjust length. */ off = ti->ti_off << 2; - if (off < sizeof (struct tcphdr) || off > ti->ti_len) { + if (off < sizeof (struct tcphdr) || off > tlen) { tcpstat.tcps_badoff++; goto drop; } - ti->ti_len = tlen - off; -#if 0 - if (off > sizeof (struct tcphdr) >> 2) - tcp_options(ti); -#endif + 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; /* - * Convert tcp protocol specific fields to host format. + * 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. + * 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); + (&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. (p. 65). + * all data in the incoming segment is discarded. */ if (inp == 0) goto dropwithreset; @@ -110,6 +150,10 @@ COUNT(TCP_INPUT); if (tp == 0) goto dropwithreset; so = inp->inp_socket; + if (so->so_options & SO_DEBUG) { + ostate = tp->t_state; + tcp_saveti = *ti; + } /* * Segment received on connection. @@ -118,11 +162,21 @@ COUNT(TCP_INPUT); tp->t_idle = 0; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; + /* + * Process options. + */ + if (om) { + tcp_dooptions(tp, om); + om = 0; + } + /* * 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) { @@ -136,7 +190,7 @@ COUNT(TCP_INPUT); * 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. (p. 65) + * segment in this state. */ case TCPS_LISTEN: if (tiflags & TH_RST) @@ -145,15 +199,28 @@ COUNT(TCP_INPUT); 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_template = tcp_template(tp); + if (tp->t_template == 0) { + in_pcbdisconnect(inp); + inp->inp_laddr = laddr; + tp = 0; + goto drop; + } 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; - if (inp->inp_faddr.s_addr == 0) { - inp->inp_faddr = ti->ti_src; - inp->inp_fport = ti->ti_sport; - } + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; goto trimthenstep6; /* @@ -170,22 +237,30 @@ COUNT(TCP_INPUT); */ case TCPS_SYN_SENT: if ((tiflags & TH_ACK) && - (SEQ_LEQ(ti->ti_ack, tp->iss) || - SEQ_GT(ti->ti_ack, tp->snd_nxt))) +/* 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, ECONNRESET); + if (tiflags & TH_ACK) { + tcp_drop(tp, ECONNREFUSED); + tp = 0; + } goto drop; } if ((tiflags & TH_SYN) == 0) goto drop; - tp->iss = ti->ti_ack; - tcp_sendseqinit(tp); + 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 @@ -194,22 +269,18 @@ COUNT(TCP_INPUT); trimthenstep6: /* - * If had syn, advance ti->ti_seq to correspond - * to first data byte. - */ - if (tiflags & TH_SYN) - ti->ti_seq++; - - /* + * 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; } @@ -221,23 +292,26 @@ trimthenstep6: if (tp->rcv_wnd == 0) { /* * If window is closed can only take segments at - * window edge, and have to drop data and EOL from + * 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_next, drop leading + * If segment begins before rcv_nxt, drop leading * data (and SYN); if nothing left, just ack. */ - if (SEQ_GT(tp->rcv_nxt, ti->ti_seq)) { - todrop = tp->rcv_nxt - ti->ti_seq; + 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--; @@ -245,7 +319,8 @@ trimthenstep6: tiflags &= ~TH_URG; todrop--; } - if (todrop > ti->ti_len) + if (todrop > ti->ti_len || + todrop == ti->ti_len && (tiflags&TH_FIN) == 0) goto dropafterack; m_adj(m, todrop); ti->ti_seq += todrop; @@ -254,20 +329,17 @@ trimthenstep6: ti->ti_urp -= todrop; else { tiflags &= ~TH_URG; - /* ti->ti_flags &= ~TH_URG; */ - /* ti->ti_urp = 0; */ + ti->ti_flags &= ~TH_URG; + ti->ti_urp = 0; } - /* tiflags &= ~TH_SYN; */ - /* ti->ti_flags &= ~TH_SYN; */ } /* * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ - if (SEQ_GT(ti->ti_seq+ti->ti_len, tp->rcv_nxt+tp->rcv_wnd)) { - todrop = - ti->ti_seq+ti->ti_len - (tp->rcv_nxt+tp->rcv_wnd); - if (todrop > ti->ti_len) + 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; @@ -275,6 +347,16 @@ trimthenstep6: } } + /* + * If a segment is received on a connection after the + * user processes are gone, then RST the other end. + */ + if (so->so_state & SS_USERGONE) { + tcp_close(tp); + tp = 0; + goto dropwithreset; + } + /* * If the RST bit is set examine the state: * SYN_RECEIVED STATE: @@ -286,14 +368,23 @@ trimthenstep6: * 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: @@ -301,12 +392,14 @@ trimthenstep6: 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; } @@ -315,7 +408,8 @@ trimthenstep6: * error and we send an RST and drop the connection. */ if (tiflags & TH_SYN) { - tcp_drop(tp, ECONNABORTED); + tcp_drop(tp, ECONNRESET); + tp = 0; goto dropwithreset; } @@ -337,17 +431,24 @@ trimthenstep6: */ case TCPS_SYN_RECEIVED: if (SEQ_GT(tp->snd_una, ti->ti_ack) || - SEQ_GT(ti->ti_ack, tp->snd_nxt)) + 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 ... */ /* * 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_nxt + * 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. @@ -357,44 +458,52 @@ trimthenstep6: 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_LT(ti->ti_ack, tp->snd_una)) + if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) break; - if (SEQ_GT(ti->ti_ack, tp->snd_nxt)) + if (SEQ_GT(ti->ti_ack, tp->snd_max)) goto dropafterack; acked = ti->ti_ack - tp->snd_una; - if (acked > so->so_snd.sb_cc) { - sbflush(&so->so_snd); - acked -= so->so_snd.sb_cc; - /* if acked our FIN is acked */ - } else { - sbdrop(&so->so_snd, acked); - acked = 0; - } /* * 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)) { - tp->t_srtt = - tcp_beta * tp->t_srtt + - (1 - tcp_beta) * tp->t_rtt; + 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; } - tp->snd_una = ti->ti_ack; - - /* - * Update window information. - */ - if (SEQ_LT(tp->snd_wl1, ti->ti_seq) || - tp->snd_wl1==ti->ti_seq && SEQ_LEQ(tp->snd_wl2,ti->ti_seq)) { - tp->snd_wnd = ti->ti_win; - tp->snd_wl1 = ti->ti_seq; - tp->snd_wl2 = ti->ti_ack; + 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 (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; } + 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) { @@ -404,8 +513,15 @@ trimthenstep6: * then enter FIN_WAIT_2. */ case TCPS_FIN_WAIT_1: - if (ourfinisacked) + 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; /* @@ -415,9 +531,13 @@ trimthenstep6: * the segment. */ case TCPS_CLOSING: - if (ourfinisacked) + if (ourfinisacked) { tp->t_state = TCPS_TIME_WAIT; - goto drop; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisdisconnected(so); + } + break; /* * The only thing that can arrive in LAST_ACK state @@ -426,8 +546,10 @@ trimthenstep6: * and return. */ case TCPS_LAST_ACK: - if (ourfinisacked) + if (ourfinisacked) { tcp_close(tp); + tp = 0; + } goto drop; /* @@ -444,20 +566,51 @@ trimthenstep6: step6: /* - * If an URG bit is set in the segment and is greater than the - * current known urgent pointer, then signal the user that the - * remote side has urgent data. 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 ((tiflags & TH_URG) == 0 && TCPS_HAVERCVDFIN(tp->t_state) == 0) - if (SEQ_GT(ti->ti_urp, tp->rcv_up)) { - tp->rcv_up = ti->ti_urp; -#if 0 - soisurgendata(so); /* XXX */ + * 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) && 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); + } + } /* * Process the segment text, merging it into the TCP sequencing queue, @@ -467,28 +620,28 @@ step6: * case PRU_RCVD). If a FIN has already been received on this * connection then we just ignore the text. */ - if (ti->ti_len) { - if (TCPS_HAVERCVDFIN(tp->t_state)) - goto drop; - off += sizeof (struct ip); /* drop IP header */ - m->m_off += off; - m->m_len -= off; + if ((ti->ti_len || (tiflags&TH_FIN)) && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { tiflags = tcp_reass(tp, ti); - tp->t_flags |= TF_ACKNOW; /* XXX TF_DELACK */ - } else + if (tcpnodelack == 0) + tp->t_flags |= TF_DELACK; + else + tp->t_flags |= TF_ACKNOW; + } else { m_freem(m); + tiflags &= ~TH_FIN; + } /* - * If FIN is received then if we haven't received SYN and - * therefore can't validate drop the segment. Otherwise ACK - * the FIN and let the user know that the connection is closing. + * If FIN is received ACK the FIN and let the user know + * that the connection is closing. */ - if ((tiflags & TH_FIN)) { - if (TCPS_HAVERCVDSYN(tp->t_state) == 0) - goto drop; - socantrcvmore(so); - tp->t_flags |= TF_ACKNOW; - tp->rcv_nxt++; + 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) { /* @@ -514,9 +667,10 @@ step6: * standard timers. */ case TCPS_FIN_WAIT_2: - tp->t_state = TCPS_TIME_WAIT;; + tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisdisconnected(so); break; /* @@ -527,44 +681,175 @@ step6: break; } } + if (so->so_options & SO_DEBUG) + tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0); /* * Return any desired output. */ - tcp_output(tp); + (void) tcp_output(tp); return; dropafterack: /* - * Generate an ACK, then drop incoming segment. - * Make ACK reflect our state. + * Generate an ACK dropping incoming segment if it occupies + * sequence space, where the ACK reflects our state. */ - if (tiflags & TH_RST) + if ((tiflags&TH_RST) || + tlen == 0 && (tiflags&(TH_SYN|TH_FIN)) == 0) goto drop; - tcp_respond(ti, tp->rcv_nxt, tp->snd_nxt, TH_ACK); - 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, then drop incoming segment. + * 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(ti, (tcp_seq)0, ti->ti_ack, TH_RST); + tcp_respond(tp, ti, (tcp_seq)0, ti->ti_ack, TH_RST); else { if (tiflags & TH_SYN) ti->ti_len++; - tcp_respond(ti, ti->ti_seq+ti->ti_len, (tcp_seq)0, TH_RST|TH_ACK); + tcp_respond(tp, ti, ti->ti_seq+ti->ti_len, (tcp_seq)0, + TH_RST|TH_ACK); } - goto drop; + 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; +} + +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) { + + default: + break; + + 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; + } + 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; + } + + 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; + } +#endif TCPTRUEOOB + } + } + (void) m_free(om); +} + +/* + * 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"); } /* @@ -578,8 +863,8 @@ tcp_reass(tp, ti) { register struct tcpiphdr *q; struct socket *so = tp->t_inpcb->inp_socket; - int flags = 0; /* no FIN */ -COUNT(TCP_REASS); + struct mbuf *m; + int flags; /* * Call with ti==0 after become established to @@ -603,7 +888,7 @@ COUNT(TCP_REASS); */ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) { register int i; - q = (struct tcpiphdr *)(q->ti_prev); + 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) { @@ -620,17 +905,20 @@ COUNT(TCP_REASS); * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ - while (q != (struct tcpiphdr *)tp && - SEQ_GT(ti->ti_seq + ti->ti_len, q->ti_seq)) { + 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_freem(dtom(q->ti_prev)); + m = dtom(q->ti_prev); remque(q->ti_prev); + m_freem(m); } /* @@ -638,35 +926,32 @@ COUNT(TCP_REASS); */ insque(ti, q->ti_prev); - /* - * Advance rcv_next through newly completed sequence space. - */ - while (ti->ti_seq == tp->rcv_nxt) { - tp->rcv_nxt += ti->ti_len; - flags = ti->ti_flags & TH_FIN; - ti = (struct tcpiphdr *)ti->ti_next; - if (ti == (struct tcpiphdr *)tp) - break; - } - present: /* - * Present data to user. + * Present data to user, advancing rcv_nxt through + * completed sequence space. */ - if (tp->t_state < TCPS_ESTABLISHED) - return (flags); + if (TCPS_HAVERCVDSYN(tp->t_state) == 0) + return (0); ti = tp->seg_next; - while (ti != (struct tcpiphdr *)tp && ti->ti_seq < tp->rcv_nxt) { + 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); - sbappend(&so->so_rcv, dtom(ti)); + m = dtom(ti); ti = (struct tcpiphdr *)ti->ti_next; - } - if (so->so_state & SS_CANTRCVMORE) - sbflush(&so->so_rcv); - else - sorwakeup(so); + 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 (flags); + return (0); }