diff options
author | Eric Dumazet <edumazet@google.com> | 2015-09-24 17:16:05 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-09-28 22:11:19 -0700 |
commit | 7c85af8810448d8ef59331be51e482413b5f503d (patch) | |
tree | 25beda8e7a35a221e3f43dfabd0225ac3c01c6c8 /net/ipv4/tcp_input.c | |
parent | eae93fe4ff88ec0979a00c440a1aa63f92c8f367 (diff) | |
download | op-kernel-dev-7c85af8810448d8ef59331be51e482413b5f503d.zip op-kernel-dev-7c85af8810448d8ef59331be51e482413b5f503d.tar.gz |
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 <edumazet@google.com>
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_input.c')
-rw-r--r-- | net/ipv4/tcp_input.c | 19 |
1 files changed, 11 insertions, 8 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 497adf5..4964d53 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6111,14 +6111,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb) { + struct tcp_fastopen_cookie foc = { .len = -1 }; + __u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn; struct tcp_options_received tmp_opt; - struct request_sock *req; struct tcp_sock *tp = tcp_sk(sk); + struct sock *fastopen_sk = NULL; struct dst_entry *dst = NULL; - __u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn; - bool want_cookie = false, fastopen; + struct request_sock *req; + bool want_cookie = false; struct flowi fl; - struct tcp_fastopen_cookie foc = { .len = -1 }; int err; @@ -6229,11 +6230,13 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tcp_rsk(req)->snt_isn = isn; tcp_rsk(req)->txhash = net_tx_rndhash(); tcp_openreq_init_rwin(req, sk, dst); - fastopen = !want_cookie && - tcp_try_fastopen(sk, skb, req, &foc, dst); - err = af_ops->send_synack(sk, dst, &fl, req, + if (!want_cookie) + fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst); + err = af_ops->send_synack(fastopen_sk ?: sk, dst, &fl, req, skb_get_queue_mapping(skb), &foc); - if (!fastopen) { + if (fastopen_sk) { + sock_put(fastopen_sk); + } else { if (err || want_cookie) goto drop_and_free; |