Skip to content

Commit 7c85af8

Browse files
Eric Dumazetdavem330
authored andcommitted
tcp: avoid reorders for TFO passive connections
We found that a TCP Fast Open passive connection was vulnerable to reorders, as the exchange might look like [1] C -> S S <FO ...> <request> [2] S -> C S. ack request <options> [3] S -> C . <answer> packets [2] and [3] can be generated at almost the same time. If C receives the 3rd packet before the 2nd, it will drop it as the socket is in SYN_SENT state and expects a SYNACK. S will have to retransmit the answer. Current OOO avoidance in linux is defeated because SYNACK packets are attached to the LISTEN socket, while DATA packets are attached to the children. They might be sent by different cpus, and different TX queues might be selected. It turns out that for TFO, we created a child, which is a full blown socket in TCP_SYN_RECV state, and we simply can attach the SYNACK packet to this socket. This means that at the time tcp_sendmsg() pushes DATA packet, skb->ooo_okay will be set iff the SYNACK packet had been sent and TX completed. This removes the reorder source at the host level. We also removed the export of tcp_try_fastopen(), as it is no longer called from IPv6. Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: Yuchung Cheng <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent eae93fe commit 7c85af8

File tree

3 files changed

+34
-28
lines changed

3 files changed

+34
-28
lines changed

include/net/tcp.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,10 +1422,10 @@ void tcp_free_fastopen_req(struct tcp_sock *tp);
14221422

14231423
extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
14241424
int tcp_fastopen_reset_cipher(void *key, unsigned int len);
1425-
bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
1426-
struct request_sock *req,
1427-
struct tcp_fastopen_cookie *foc,
1428-
struct dst_entry *dst);
1425+
struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
1426+
struct request_sock *req,
1427+
struct tcp_fastopen_cookie *foc,
1428+
struct dst_entry *dst);
14291429
void tcp_fastopen_init_key_once(bool publish);
14301430
#define TCP_FASTOPEN_KEY_LENGTH 16
14311431

net/ipv4/tcp_fastopen.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req,
124124
return false;
125125
}
126126

127-
static bool tcp_fastopen_create_child(struct sock *sk,
128-
struct sk_buff *skb,
129-
struct dst_entry *dst,
130-
struct request_sock *req)
127+
static struct sock *tcp_fastopen_create_child(struct sock *sk,
128+
struct sk_buff *skb,
129+
struct dst_entry *dst,
130+
struct request_sock *req)
131131
{
132132
struct tcp_sock *tp;
133133
struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
@@ -140,7 +140,7 @@ static bool tcp_fastopen_create_child(struct sock *sk,
140140

141141
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
142142
if (!child)
143-
return false;
143+
return NULL;
144144

145145
spin_lock(&queue->fastopenq->lock);
146146
queue->fastopenq->qlen++;
@@ -216,9 +216,11 @@ static bool tcp_fastopen_create_child(struct sock *sk,
216216
tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq;
217217
sk->sk_data_ready(sk);
218218
bh_unlock_sock(child);
219-
sock_put(child);
219+
/* Note: sock_put(child) will be done by tcp_conn_request()
220+
* after SYNACK packet is sent.
221+
*/
220222
WARN_ON(!req->sk);
221-
return true;
223+
return child;
222224
}
223225

224226
static bool tcp_fastopen_queue_check(struct sock *sk)
@@ -261,13 +263,14 @@ static bool tcp_fastopen_queue_check(struct sock *sk)
261263
* may be updated and return the client in the SYN-ACK later. E.g., Fast Open
262264
* cookie request (foc->len == 0).
263265
*/
264-
bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
265-
struct request_sock *req,
266-
struct tcp_fastopen_cookie *foc,
267-
struct dst_entry *dst)
266+
struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
267+
struct request_sock *req,
268+
struct tcp_fastopen_cookie *foc,
269+
struct dst_entry *dst)
268270
{
269271
struct tcp_fastopen_cookie valid_foc = { .len = -1 };
270272
bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1;
273+
struct sock *child;
271274

272275
if (foc->len == 0) /* Client requests a cookie */
273276
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENCOOKIEREQD);
@@ -276,7 +279,7 @@ bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
276279
(syn_data || foc->len >= 0) &&
277280
tcp_fastopen_queue_check(sk))) {
278281
foc->len = -1;
279-
return false;
282+
return NULL;
280283
}
281284

282285
if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD))
@@ -296,18 +299,18 @@ bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
296299
* data in SYN_RECV state.
297300
*/
298301
fastopen:
299-
if (tcp_fastopen_create_child(sk, skb, dst, req)) {
302+
child = tcp_fastopen_create_child(sk, skb, dst, req);
303+
if (child) {
300304
foc->len = -1;
301305
NET_INC_STATS_BH(sock_net(sk),
302306
LINUX_MIB_TCPFASTOPENPASSIVE);
303-
return true;
307+
return child;
304308
}
305309
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
306310
} else if (foc->len > 0) /* Client presents an invalid cookie */
307311
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
308312

309313
valid_foc.exp = foc->exp;
310314
*foc = valid_foc;
311-
return false;
315+
return NULL;
312316
}
313-
EXPORT_SYMBOL(tcp_try_fastopen);

net/ipv4/tcp_input.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6111,14 +6111,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
61116111
const struct tcp_request_sock_ops *af_ops,
61126112
struct sock *sk, struct sk_buff *skb)
61136113
{
6114+
struct tcp_fastopen_cookie foc = { .len = -1 };
6115+
__u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn;
61146116
struct tcp_options_received tmp_opt;
6115-
struct request_sock *req;
61166117
struct tcp_sock *tp = tcp_sk(sk);
6118+
struct sock *fastopen_sk = NULL;
61176119
struct dst_entry *dst = NULL;
6118-
__u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn;
6119-
bool want_cookie = false, fastopen;
6120+
struct request_sock *req;
6121+
bool want_cookie = false;
61206122
struct flowi fl;
6121-
struct tcp_fastopen_cookie foc = { .len = -1 };
61226123
int err;
61236124

61246125

@@ -6229,11 +6230,13 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
62296230
tcp_rsk(req)->snt_isn = isn;
62306231
tcp_rsk(req)->txhash = net_tx_rndhash();
62316232
tcp_openreq_init_rwin(req, sk, dst);
6232-
fastopen = !want_cookie &&
6233-
tcp_try_fastopen(sk, skb, req, &foc, dst);
6234-
err = af_ops->send_synack(sk, dst, &fl, req,
6233+
if (!want_cookie)
6234+
fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst);
6235+
err = af_ops->send_synack(fastopen_sk ?: sk, dst, &fl, req,
62356236
skb_get_queue_mapping(skb), &foc);
6236-
if (!fastopen) {
6237+
if (fastopen_sk) {
6238+
sock_put(fastopen_sk);
6239+
} else {
62376240
if (err || want_cookie)
62386241
goto drop_and_free;
62396242

0 commit comments

Comments
 (0)