summaryrefslogtreecommitdiffstats
path: root/sys/netinet/tcp_input.c
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2012-08-25 09:26:37 +0000
committerrrs <rrs@FreeBSD.org>2012-08-25 09:26:37 +0000
commit4952c1e53e5666c79737ca683e5e9594f39dda7a (patch)
treecc0d774770e770ffeebd13e45a38f3c133f9bce4 /sys/netinet/tcp_input.c
parentbf6955f98a62434041797235b55ed2b35c80cece (diff)
downloadFreeBSD-src-4952c1e53e5666c79737ca683e5e9594f39dda7a.zip
FreeBSD-src-4952c1e53e5666c79737ca683e5e9594f39dda7a.tar.gz
This small change takes care of a race condition
that can occur when both sides close at the same time. If that occurs, without this fix the connection enters FIN1 on both sides and they will forever send FIN|ACK at each other until the connection times out. This is because we stopped processing the FIN|ACK and thus did not advance the sequence and so never ACK'd each others FIN. This fix adjusts it so we *do* process the FIN properly and the race goes away ;-) MFC after: 1 month
Diffstat (limited to 'sys/netinet/tcp_input.c')
-rw-r--r--sys/netinet/tcp_input.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 1840b08..015b1ff 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -2414,6 +2414,16 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
}
} else
tp->snd_cwnd += tp->t_maxseg;
+ if ((thflags & TH_FIN) &&
+ (TCPS_HAVERCVDFIN(tp->t_state) == 0)) {
+ /*
+ * If its a fin we need to process
+ * it to avoid a race where both
+ * sides enter FIN-WAIT and send FIN|ACK
+ * at the same time.
+ */
+ break;
+ }
(void) tcp_output(tp);
goto drop;
} else if (tp->t_dupacks == tcprexmtthresh) {
@@ -2453,6 +2463,16 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
}
tp->snd_nxt = th->th_ack;
tp->snd_cwnd = tp->t_maxseg;
+ if ((thflags & TH_FIN) &&
+ (TCPS_HAVERCVDFIN(tp->t_state) == 0)) {
+ /*
+ * If its a fin we need to process
+ * it to avoid a race where both
+ * sides enter FIN-WAIT and send FIN|ACK
+ * at the same time.
+ */
+ break;
+ }
(void) tcp_output(tp);
KASSERT(tp->snd_limited <= 2,
("%s: tp->snd_limited too big",
@@ -2479,6 +2499,16 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
(tp->snd_nxt - tp->snd_una) +
(tp->t_dupacks - tp->snd_limited) *
tp->t_maxseg;
+ if ((thflags & TH_FIN) &&
+ (TCPS_HAVERCVDFIN(tp->t_state) == 0)) {
+ /*
+ * If its a fin we need to process
+ * it to avoid a race where both
+ * sides enter FIN-WAIT and send FIN|ACK
+ * at the same time.
+ */
+ break;
+ }
(void) tcp_output(tp);
sent = tp->snd_max - oldsndmax;
if (sent > tp->t_maxseg) {
OpenPOWER on IntegriCloud