aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuchung Cheng <ycheng@google.com>2014-05-11 23:22:12 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-13 17:53:03 -0400
commit0a672f74131dd682087dfd5f45bf61f95804772e (patch)
tree4e3781451c7828a66c8d49771bcb4c5f26818709
parent843f4a55e336e6d0c7bb92e7f9621535bc8d5fcd (diff)
tcp: improve fastopen icmp handling
If a fast open socket is already accepted by the user, it should be treated like a connected socket to record the ICMP error in sk_softerr, so the user can fetch it. Do that in both tcp_v4_err and tcp_v6_err. Also refactor the sequence window check to improve readability (e.g., there were two local variables named 'req'). Signed-off-by: Yuchung Cheng <ycheng@google.com> Signed-off-by: Daniel Lee <longinus00@gmail.com> Signed-off-by: Jerry Chu <hkchu@google.com> Acked-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv4/tcp_ipv4.c35
-rw-r--r--net/ipv6/tcp_ipv6.c22
2 files changed, 31 insertions, 26 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 1665f0f84233..a2780e5334c9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -336,8 +336,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
336 const int code = icmp_hdr(icmp_skb)->code; 336 const int code = icmp_hdr(icmp_skb)->code;
337 struct sock *sk; 337 struct sock *sk;
338 struct sk_buff *skb; 338 struct sk_buff *skb;
339 struct request_sock *req; 339 struct request_sock *fastopen;
340 __u32 seq; 340 __u32 seq, snd_una;
341 __u32 remaining; 341 __u32 remaining;
342 int err; 342 int err;
343 struct net *net = dev_net(icmp_skb->dev); 343 struct net *net = dev_net(icmp_skb->dev);
@@ -378,12 +378,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
378 378
379 icsk = inet_csk(sk); 379 icsk = inet_csk(sk);
380 tp = tcp_sk(sk); 380 tp = tcp_sk(sk);
381 req = tp->fastopen_rsk;
382 seq = ntohl(th->seq); 381 seq = ntohl(th->seq);
382 /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
383 fastopen = tp->fastopen_rsk;
384 snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
383 if (sk->sk_state != TCP_LISTEN && 385 if (sk->sk_state != TCP_LISTEN &&
384 !between(seq, tp->snd_una, tp->snd_nxt) && 386 !between(seq, snd_una, tp->snd_nxt)) {
385 (req == NULL || seq != tcp_rsk(req)->snt_isn)) {
386 /* For a Fast Open socket, allow seq to be snt_isn. */
387 NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); 387 NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
388 goto out; 388 goto out;
389 } 389 }
@@ -426,11 +426,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
426 if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH) 426 if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH)
427 break; 427 break;
428 if (seq != tp->snd_una || !icsk->icsk_retransmits || 428 if (seq != tp->snd_una || !icsk->icsk_retransmits ||
429 !icsk->icsk_backoff) 429 !icsk->icsk_backoff || fastopen)
430 break; 430 break;
431 431
432 /* XXX (TFO) - revisit the following logic for TFO */
433
434 if (sock_owned_by_user(sk)) 432 if (sock_owned_by_user(sk))
435 break; 433 break;
436 434
@@ -462,14 +460,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
462 goto out; 460 goto out;
463 } 461 }
464 462
465 /* XXX (TFO) - if it's a TFO socket and has been accepted, rather
466 * than following the TCP_SYN_RECV case and closing the socket,
467 * we ignore the ICMP error and keep trying like a fully established
468 * socket. Is this the right thing to do?
469 */
470 if (req && req->sk == NULL)
471 goto out;
472
473 switch (sk->sk_state) { 463 switch (sk->sk_state) {
474 struct request_sock *req, **prev; 464 struct request_sock *req, **prev;
475 case TCP_LISTEN: 465 case TCP_LISTEN:
@@ -502,10 +492,13 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
502 goto out; 492 goto out;
503 493
504 case TCP_SYN_SENT: 494 case TCP_SYN_SENT:
505 case TCP_SYN_RECV: /* Cannot happen. 495 case TCP_SYN_RECV:
506 It can f.e. if SYNs crossed, 496 /* Only in fast or simultaneous open. If a fast open socket is
507 or Fast Open. 497 * is already accepted it is treated as a connected one below.
508 */ 498 */
499 if (fastopen && fastopen->sk == NULL)
500 break;
501
509 if (!sock_owned_by_user(sk)) { 502 if (!sock_owned_by_user(sk)) {
510 sk->sk_err = err; 503 sk->sk_err = err;
511 504
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 7fa67439f4d6..a7a62ce12b3f 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -340,7 +340,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
340 struct sock *sk; 340 struct sock *sk;
341 int err; 341 int err;
342 struct tcp_sock *tp; 342 struct tcp_sock *tp;
343 __u32 seq; 343 struct request_sock *fastopen;
344 __u32 seq, snd_una;
344 struct net *net = dev_net(skb->dev); 345 struct net *net = dev_net(skb->dev);
345 346
346 sk = inet6_lookup(net, &tcp_hashinfo, &hdr->daddr, 347 sk = inet6_lookup(net, &tcp_hashinfo, &hdr->daddr,
@@ -371,8 +372,11 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
371 372
372 tp = tcp_sk(sk); 373 tp = tcp_sk(sk);
373 seq = ntohl(th->seq); 374 seq = ntohl(th->seq);
375 /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
376 fastopen = tp->fastopen_rsk;
377 snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
374 if (sk->sk_state != TCP_LISTEN && 378 if (sk->sk_state != TCP_LISTEN &&
375 !between(seq, tp->snd_una, tp->snd_nxt)) { 379 !between(seq, snd_una, tp->snd_nxt)) {
376 NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); 380 NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
377 goto out; 381 goto out;
378 } 382 }
@@ -436,8 +440,13 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
436 goto out; 440 goto out;
437 441
438 case TCP_SYN_SENT: 442 case TCP_SYN_SENT:
439 case TCP_SYN_RECV: /* Cannot happen. 443 case TCP_SYN_RECV:
440 It can, it SYNs are crossed. --ANK */ 444 /* Only in fast or simultaneous open. If a fast open socket is
445 * is already accepted it is treated as a connected one below.
446 */
447 if (fastopen && fastopen->sk == NULL)
448 break;
449
441 if (!sock_owned_by_user(sk)) { 450 if (!sock_owned_by_user(sk)) {
442 sk->sk_err = err; 451 sk->sk_err = err;
443 sk->sk_error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ 452 sk->sk_error_report(sk); /* Wake people up to see the error (see connect in sock.c) */
@@ -1760,6 +1769,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
1760 const struct inet_sock *inet = inet_sk(sp); 1769 const struct inet_sock *inet = inet_sk(sp);
1761 const struct tcp_sock *tp = tcp_sk(sp); 1770 const struct tcp_sock *tp = tcp_sk(sp);
1762 const struct inet_connection_sock *icsk = inet_csk(sp); 1771 const struct inet_connection_sock *icsk = inet_csk(sp);
1772 struct fastopen_queue *fastopenq = icsk->icsk_accept_queue.fastopenq;
1763 1773
1764 dest = &sp->sk_v6_daddr; 1774 dest = &sp->sk_v6_daddr;
1765 src = &sp->sk_v6_rcv_saddr; 1775 src = &sp->sk_v6_rcv_saddr;
@@ -1802,7 +1812,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
1802 jiffies_to_clock_t(icsk->icsk_ack.ato), 1812 jiffies_to_clock_t(icsk->icsk_ack.ato),
1803 (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong, 1813 (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
1804 tp->snd_cwnd, 1814 tp->snd_cwnd,
1805 tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh 1815 sp->sk_state == TCP_LISTEN ?
1816 (fastopenq ? fastopenq->max_qlen : 0) :
1817 (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh)
1806 ); 1818 );
1807} 1819}
1808 1820