aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_fastopen.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2015-09-24 20:16:05 -0400
committerDavid S. Miller <davem@davemloft.net>2015-09-29 01:11:19 -0400
commit7c85af8810448d8ef59331be51e482413b5f503d (patch)
tree25beda8e7a35a221e3f43dfabd0225ac3c01c6c8 /net/ipv4/tcp_fastopen.c
parenteae93fe4ff88ec0979a00c440a1aa63f92c8f367 (diff)
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_fastopen.c')
-rw-r--r--net/ipv4/tcp_fastopen.c35
1 files changed, 19 insertions, 16 deletions
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index f9c0fb84e435..db43c6286cf7 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -124,10 +124,10 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req,
124 return false; 124 return false;
125} 125}
126 126
127static bool tcp_fastopen_create_child(struct sock *sk, 127static struct sock *tcp_fastopen_create_child(struct sock *sk,
128 struct sk_buff *skb, 128 struct sk_buff *skb,
129 struct dst_entry *dst, 129 struct dst_entry *dst,
130 struct request_sock *req) 130 struct request_sock *req)
131{ 131{
132 struct tcp_sock *tp; 132 struct tcp_sock *tp;
133 struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; 133 struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
@@ -140,7 +140,7 @@ static bool tcp_fastopen_create_child(struct sock *sk,
140 140
141 child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); 141 child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
142 if (!child) 142 if (!child)
143 return false; 143 return NULL;
144 144
145 spin_lock(&queue->fastopenq->lock); 145 spin_lock(&queue->fastopenq->lock);
146 queue->fastopenq->qlen++; 146 queue->fastopenq->qlen++;
@@ -216,9 +216,11 @@ static bool tcp_fastopen_create_child(struct sock *sk,
216 tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq; 216 tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq;
217 sk->sk_data_ready(sk); 217 sk->sk_data_ready(sk);
218 bh_unlock_sock(child); 218 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 */
220 WARN_ON(!req->sk); 222 WARN_ON(!req->sk);
221 return true; 223 return child;
222} 224}
223 225
224static bool tcp_fastopen_queue_check(struct sock *sk) 226static bool tcp_fastopen_queue_check(struct sock *sk)
@@ -261,13 +263,14 @@ static bool tcp_fastopen_queue_check(struct sock *sk)
261 * may be updated and return the client in the SYN-ACK later. E.g., Fast Open 263 * may be updated and return the client in the SYN-ACK later. E.g., Fast Open
262 * cookie request (foc->len == 0). 264 * cookie request (foc->len == 0).
263 */ 265 */
264bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, 266struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
265 struct request_sock *req, 267 struct request_sock *req,
266 struct tcp_fastopen_cookie *foc, 268 struct tcp_fastopen_cookie *foc,
267 struct dst_entry *dst) 269 struct dst_entry *dst)
268{ 270{
269 struct tcp_fastopen_cookie valid_foc = { .len = -1 }; 271 struct tcp_fastopen_cookie valid_foc = { .len = -1 };
270 bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1; 272 bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1;
273 struct sock *child;
271 274
272 if (foc->len == 0) /* Client requests a cookie */ 275 if (foc->len == 0) /* Client requests a cookie */
273 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENCOOKIEREQD); 276 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,
276 (syn_data || foc->len >= 0) && 279 (syn_data || foc->len >= 0) &&
277 tcp_fastopen_queue_check(sk))) { 280 tcp_fastopen_queue_check(sk))) {
278 foc->len = -1; 281 foc->len = -1;
279 return false; 282 return NULL;
280 } 283 }
281 284
282 if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD)) 285 if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD))
@@ -296,11 +299,12 @@ bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
296 * data in SYN_RECV state. 299 * data in SYN_RECV state.
297 */ 300 */
298fastopen: 301fastopen:
299 if (tcp_fastopen_create_child(sk, skb, dst, req)) { 302 child = tcp_fastopen_create_child(sk, skb, dst, req);
303 if (child) {
300 foc->len = -1; 304 foc->len = -1;
301 NET_INC_STATS_BH(sock_net(sk), 305 NET_INC_STATS_BH(sock_net(sk),
302 LINUX_MIB_TCPFASTOPENPASSIVE); 306 LINUX_MIB_TCPFASTOPENPASSIVE);
303 return true; 307 return child;
304 } 308 }
305 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL); 309 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
306 } else if (foc->len > 0) /* Client presents an invalid cookie */ 310 } else if (foc->len > 0) /* Client presents an invalid cookie */
@@ -308,6 +312,5 @@ fastopen:
308 312
309 valid_foc.exp = foc->exp; 313 valid_foc.exp = foc->exp;
310 *foc = valid_foc; 314 *foc = valid_foc;
311 return false; 315 return NULL;
312} 316}
313EXPORT_SYMBOL(tcp_try_fastopen);