diff options
author | Eric Dumazet | 2013-04-19 02:19:48 -0500 |
---|---|---|
committer | Greg Kroah-Hartman | 2013-05-01 11:46:19 -0500 |
commit | 72b1b8da7cb01424cbafd36bf3a7c35215eb4ec7 (patch) | |
tree | b47dee1eda0535e44bb10dfd058ae4d000c152ec /net | |
parent | b0914393f1e376bc55fce1bb48972966a1761d5c (diff) | |
download | kernel-video-72b1b8da7cb01424cbafd36bf3a7c35215eb4ec7.tar.gz kernel-video-72b1b8da7cb01424cbafd36bf3a7c35215eb4ec7.tar.xz kernel-video-72b1b8da7cb01424cbafd36bf3a7c35215eb4ec7.zip |
tcp: call tcp_replace_ts_recent() from tcp_ack()
[ Upstream commit 12fb3dd9dc3c64ba7d64cec977cca9b5fb7b1d4e ]
commit bd090dfc634d (tcp: tcp_replace_ts_recent() should not be called
from tcp_validate_incoming()) introduced a TS ecr bug in slow path
processing.
1 A > B P. 1:10001(10000) ack 1 <nop,nop,TS val 1001 ecr 200>
2 B < A . 1:1(0) ack 1 win 257 <sack 9001:10001,TS val 300 ecr 1001>
3 A > B . 1:1001(1000) ack 1 win 227 <nop,nop,TS val 1002 ecr 200>
4 A > B . 1001:2001(1000) ack 1 win 227 <nop,nop,TS val 1002 ecr 200>
(ecr 200 should be ecr 300 in packets 3 & 4)
Problem is tcp_ack() can trigger send of new packets (retransmits),
reflecting the prior TSval, instead of the TSval contained in the
currently processed incoming packet.
Fix this by calling tcp_replace_ts_recent() from tcp_ack() after the
checks, but before the actions.
Reported-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/tcp_input.c | 64 |
1 files changed, 31 insertions, 33 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 9841a716370..b4e8b797a09 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -116,6 +116,7 @@ int sysctl_tcp_early_retrans __read_mostly = 2; | |||
116 | #define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ | 116 | #define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ |
117 | #define FLAG_NONHEAD_RETRANS_ACKED 0x1000 /* Non-head rexmitted data was ACKed */ | 117 | #define FLAG_NONHEAD_RETRANS_ACKED 0x1000 /* Non-head rexmitted data was ACKed */ |
118 | #define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ | 118 | #define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ |
119 | #define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */ | ||
119 | 120 | ||
120 | #define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) | 121 | #define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) |
121 | #define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) | 122 | #define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) |
@@ -3572,6 +3573,27 @@ static void tcp_send_challenge_ack(struct sock *sk) | |||
3572 | } | 3573 | } |
3573 | } | 3574 | } |
3574 | 3575 | ||
3576 | static void tcp_store_ts_recent(struct tcp_sock *tp) | ||
3577 | { | ||
3578 | tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; | ||
3579 | tp->rx_opt.ts_recent_stamp = get_seconds(); | ||
3580 | } | ||
3581 | |||
3582 | static void tcp_replace_ts_recent(struct tcp_sock *tp, u32 seq) | ||
3583 | { | ||
3584 | if (tp->rx_opt.saw_tstamp && !after(seq, tp->rcv_wup)) { | ||
3585 | /* PAWS bug workaround wrt. ACK frames, the PAWS discard | ||
3586 | * extra check below makes sure this can only happen | ||
3587 | * for pure ACK frames. -DaveM | ||
3588 | * | ||
3589 | * Not only, also it occurs for expired timestamps. | ||
3590 | */ | ||
3591 | |||
3592 | if (tcp_paws_check(&tp->rx_opt, 0)) | ||
3593 | tcp_store_ts_recent(tp); | ||
3594 | } | ||
3595 | } | ||
3596 | |||
3575 | /* This routine deals with incoming acks, but not outgoing ones. */ | 3597 | /* This routine deals with incoming acks, but not outgoing ones. */ |
3576 | static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) | 3598 | static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) |
3577 | { | 3599 | { |
@@ -3624,6 +3646,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) | |||
3624 | prior_fackets = tp->fackets_out; | 3646 | prior_fackets = tp->fackets_out; |
3625 | prior_in_flight = tcp_packets_in_flight(tp); | 3647 | prior_in_flight = tcp_packets_in_flight(tp); |
3626 | 3648 | ||
3649 | /* ts_recent update must be made after we are sure that the packet | ||
3650 | * is in window. | ||
3651 | */ | ||
3652 | if (flag & FLAG_UPDATE_TS_RECENT) | ||
3653 | tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); | ||
3654 | |||
3627 | if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { | 3655 | if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { |
3628 | /* Window is constant, pure forward advance. | 3656 | /* Window is constant, pure forward advance. |
3629 | * No more checks are required. | 3657 | * No more checks are required. |
@@ -3940,27 +3968,6 @@ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th) | |||
3940 | EXPORT_SYMBOL(tcp_parse_md5sig_option); | 3968 | EXPORT_SYMBOL(tcp_parse_md5sig_option); |
3941 | #endif | 3969 | #endif |
3942 | 3970 | ||
3943 | static inline void tcp_store_ts_recent(struct tcp_sock *tp) | ||
3944 | { | ||
3945 | tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; | ||
3946 | tp->rx_opt.ts_recent_stamp = get_seconds(); | ||
3947 | } | ||
3948 | |||
3949 | static inline void tcp_replace_ts_recent(struct tcp_sock *tp, u32 seq) | ||
3950 | { | ||
3951 | if (tp->rx_opt.saw_tstamp && !after(seq, tp->rcv_wup)) { | ||
3952 | /* PAWS bug workaround wrt. ACK frames, the PAWS discard | ||
3953 | * extra check below makes sure this can only happen | ||
3954 | * for pure ACK frames. -DaveM | ||
3955 | * | ||
3956 | * Not only, also it occurs for expired timestamps. | ||
3957 | */ | ||
3958 | |||
3959 | if (tcp_paws_check(&tp->rx_opt, 0)) | ||
3960 | tcp_store_ts_recent(tp); | ||
3961 | } | ||
3962 | } | ||
3963 | |||
3964 | /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM | 3971 | /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM |
3965 | * | 3972 | * |
3966 | * It is not fatal. If this ACK does _not_ change critical state (seqs, window) | 3973 | * It is not fatal. If this ACK does _not_ change critical state (seqs, window) |
@@ -5556,14 +5563,9 @@ slow_path: | |||
5556 | return 0; | 5563 | return 0; |
5557 | 5564 | ||
5558 | step5: | 5565 | step5: |
5559 | if (tcp_ack(sk, skb, FLAG_SLOWPATH) < 0) | 5566 | if (tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) < 0) |
5560 | goto discard; | 5567 | goto discard; |
5561 | 5568 | ||
5562 | /* ts_recent update must be made after we are sure that the packet | ||
5563 | * is in window. | ||
5564 | */ | ||
5565 | tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); | ||
5566 | |||
5567 | tcp_rcv_rtt_measure_ts(sk, skb); | 5569 | tcp_rcv_rtt_measure_ts(sk, skb); |
5568 | 5570 | ||
5569 | /* Process urgent data. */ | 5571 | /* Process urgent data. */ |
@@ -5997,7 +5999,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
5997 | 5999 | ||
5998 | /* step 5: check the ACK field */ | 6000 | /* step 5: check the ACK field */ |
5999 | if (true) { | 6001 | if (true) { |
6000 | int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH) > 0; | 6002 | int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | |
6003 | FLAG_UPDATE_TS_RECENT) > 0; | ||
6001 | 6004 | ||
6002 | switch (sk->sk_state) { | 6005 | switch (sk->sk_state) { |
6003 | case TCP_SYN_RECV: | 6006 | case TCP_SYN_RECV: |
@@ -6148,11 +6151,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
6148 | } | 6151 | } |
6149 | } | 6152 | } |
6150 | 6153 | ||
6151 | /* ts_recent update must be made after we are sure that the packet | ||
6152 | * is in window. | ||
6153 | */ | ||
6154 | tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); | ||
6155 | |||
6156 | /* step 6: check the URG bit */ | 6154 | /* step 6: check the URG bit */ |
6157 | tcp_urg(sk, skb, th); | 6155 | tcp_urg(sk, skb, th); |
6158 | 6156 | ||