From 28257b5cccce8b2e835710a878f3705e77563ce5 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 10 Oct 2002 19:21:50 +0000 Subject: [PATCH] Update various comments mainly related to retransmit/FIN that I documented while working on a previous bug. Fix a PERSIST bug. Properly account for a FIN sent during a PERSIST. MFC after: 7 days --- sys/netinet/tcp_output.c | 42 ++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index 845aff8befa4..944e7eeff17c 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -215,6 +215,18 @@ tcp_output(struct tcpcb *tp) } } + /* + * If snd_nxt == snd_max and we have transmitted a FIN, the + * offset will be > 0 even if so_snd.sb_cc is 0, resulting in + * a negative length. This can also occur when tcp opens up + * its congestion window while receiving additional duplicate + * acks after fast-retransmit because TCP will reset snd_nxt + * to snd_max after the fast-retransmit. + * + * In the normal retransmit-FIN-only case, however, snd_nxt will + * be set to snd_una, the offset will be 0, and the length may + * wind up 0. + */ len = (long)ulmin(so->so_snd.sb_cc, win) - off; if ((taop = tcp_gettaocache(&tp->t_inpcb->inp_inc)) == NULL) { @@ -252,7 +264,7 @@ tcp_output(struct tcpcb *tp) /* * If FIN has been sent but not acked, * but we haven't been called to retransmit, - * len will be -1. Otherwise, window shrank + * len will be < 0. Otherwise, window shrank * after we sent into it. If window shrank to 0, * cancel pending retransmit, pull snd_nxt back * to (closed) window, and set the persist timer @@ -268,6 +280,12 @@ tcp_output(struct tcpcb *tp) tcp_setpersist(tp); } } + + /* + * len will be >= 0 after this point. Truncate to the maximum + * segment length and ensure that FIN is removed if the length + * no longer contains the last data byte. + */ if (len > tp->t_maxseg) { len = tp->t_maxseg; sendalot = 1; @@ -336,7 +354,8 @@ tcp_output(struct tcpcb *tp) } /* - * Send if we owe peer an ACK. + * Send if we owe the peer an ACK, RST, SYN, or urgent data. ACKNOW + * is also a catch-all for the retransmit timer timeout case. */ if (tp->t_flags & TF_ACKNOW) goto send; @@ -347,8 +366,7 @@ tcp_output(struct tcpcb *tp) goto send; /* * If our state indicates that FIN should be sent - * and we have not yet done so, or we're retransmitting the FIN, - * then we need to send. + * and we have not yet done so, then we need to send. */ if (flags & TH_FIN && ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) @@ -794,7 +812,7 @@ tcp_output(struct tcpcb *tp) /* * Set retransmit timer if not currently set, - * and not doing an ack or a keep-alive probe. + * and not doing a pure ack or a keep-alive probe. * Initial value for retransmit timer is smoothed * round-trip time + 2 * round-trip time variance. * Initialize shift counter which is used for backoff @@ -809,9 +827,21 @@ tcp_output(struct tcpcb *tp) callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); } - } else + } else { + /* + * Persist case, update snd_max but since we are in + * persist mode (no window) we do not update snd_nxt. + */ + int xlen = len; + if (flags & TH_SYN) + ++xlen; + if (flags & TH_FIN) { + ++xlen; + tp->t_flags |= TF_SENTFIN; + } if (SEQ_GT(tp->snd_nxt + len, tp->snd_max)) tp->snd_max = tp->snd_nxt + len; + } #ifdef TCPDEBUG /*