diff options
author | Yuchung Cheng <ycheng@google.com> | 2014-05-11 23:22:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-13 17:53:03 -0400 |
commit | 0a672f74131dd682087dfd5f45bf61f95804772e (patch) | |
tree | 4e3781451c7828a66c8d49771bcb4c5f26818709 | |
parent | 843f4a55e336e6d0c7bb92e7f9621535bc8d5fcd (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.c | 35 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 22 |
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 | ||