tcp: fix sequence numbers for repaired sockets re-using TIME-WAIT sockets
authorStefan Baranoff <sbaranoff@gmail.com>
Tue, 10 Jul 2018 21:25:20 +0000 (17:25 -0400)
committerDavid S. Miller <davem@davemloft.net>
Thu, 12 Jul 2018 21:33:45 +0000 (14:33 -0700)
This patch fixes a bug where the sequence numbers of a socket created using
TCP repair functionality are lower than set after connect is called.
This occurs when the repair socket overlaps with a TIME-WAIT socket and
triggers the re-use code. The amount lower is equal to the number of times
that a particular IP/port set is re-used and then put back into TIME-WAIT.
Re-using the first time the sequence number is 1 lower, closing that socket
and then re-opening (with repair) a new socket with the same addresses/ports
puts the sequence number 2 lower than set via setsockopt. The third time is
3 lower, etc. I have not tested what the limit of this acrewal is, if any.

The fix is, if a socket is in repair mode, to respect the already set
sequence number and timestamp when it would have already re-used the
TIME-WAIT socket.

Signed-off-by: Stefan Baranoff <sbaranoff@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/tcp_ipv4.c

index bea17f1..3b2711e 100644 (file)
@@ -156,11 +156,24 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
         */
        if (tcptw->tw_ts_recent_stamp &&
            (!twp || (reuse && get_seconds() - tcptw->tw_ts_recent_stamp > 1))) {
-               tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
-               if (tp->write_seq == 0)
-                       tp->write_seq = 1;
-               tp->rx_opt.ts_recent       = tcptw->tw_ts_recent;
-               tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
+               /* In case of repair and re-using TIME-WAIT sockets we still
+                * want to be sure that it is safe as above but honor the
+                * sequence numbers and time stamps set as part of the repair
+                * process.
+                *
+                * Without this check re-using a TIME-WAIT socket with TCP
+                * repair would accumulate a -1 on the repair assigned
+                * sequence number. The first time it is reused the sequence
+                * is -1, the second time -2, etc. This fixes that issue
+                * without appearing to create any others.
+                */
+               if (likely(!tp->repair)) {
+                       tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
+                       if (tp->write_seq == 0)
+                               tp->write_seq = 1;
+                       tp->rx_opt.ts_recent       = tcptw->tw_ts_recent;
+                       tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
+               }
                sock_hold(sktw);
                return 1;
        }