tcp: Discard segments that ack data not yet sent

Discard incoming packets whose ack field iincludes data not yet sent.
This is consistent with RFC 793 Section 3.9.

Change tcp_ack() to distinguish between too-small and too-large ack
field values.  Keep segments with too-large ack fields out of the fast
path, and change slow path to discard them.

Reported-by:  Oliver Zheng <mailinglists+netdev@oliverzheng.com>
Signed-off-by: John Dykstra <john.dykstra1@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
John Dykstra 2009-03-22 21:49:57 -07:00 committed by David S. Miller
parent 763dccdc8e
commit 96e0bf4b51

View File

@ -3585,15 +3585,18 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
int prior_packets; int prior_packets;
int frto_cwnd = 0; int frto_cwnd = 0;
/* If the ack is newer than sent or older than previous acks /* If the ack is older than previous acks
* then we can probably ignore it. * then we can probably ignore it.
*/ */
if (after(ack, tp->snd_nxt))
goto uninteresting_ack;
if (before(ack, prior_snd_una)) if (before(ack, prior_snd_una))
goto old_ack; goto old_ack;
/* If the ack includes data we haven't sent yet, discard
* this segment (RFC793 Section 3.9).
*/
if (after(ack, tp->snd_nxt))
goto invalid_ack;
if (after(ack, prior_snd_una)) if (after(ack, prior_snd_una))
flag |= FLAG_SND_UNA_ADVANCED; flag |= FLAG_SND_UNA_ADVANCED;
@ -3683,6 +3686,10 @@ no_queue:
tcp_ack_probe(sk); tcp_ack_probe(sk);
return 1; return 1;
invalid_ack:
SOCK_DEBUG(sk, "Ack %u after %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
return -1;
old_ack: old_ack:
if (TCP_SKB_CB(skb)->sacked) { if (TCP_SKB_CB(skb)->sacked) {
tcp_sacktag_write_queue(sk, skb, prior_snd_una); tcp_sacktag_write_queue(sk, skb, prior_snd_una);
@ -3690,8 +3697,7 @@ old_ack:
tcp_try_keep_open(sk); tcp_try_keep_open(sk);
} }
uninteresting_ack: SOCK_DEBUG(sk, "Ack %u before %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
SOCK_DEBUG(sk, "Ack %u out of %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
return 0; return 0;
} }
@ -5141,7 +5147,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
*/ */
if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags && if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
TCP_SKB_CB(skb)->seq == tp->rcv_nxt) { TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {
int tcp_header_len = tp->tcp_header_len; int tcp_header_len = tp->tcp_header_len;
/* Timestamp header prediction: tcp_header_len /* Timestamp header prediction: tcp_header_len
@ -5294,8 +5301,8 @@ slow_path:
return -res; return -res;
step5: step5:
if (th->ack) if (th->ack && tcp_ack(sk, skb, FLAG_SLOWPATH) < 0)
tcp_ack(sk, skb, FLAG_SLOWPATH); goto discard;
tcp_rcv_rtt_measure_ts(sk, skb); tcp_rcv_rtt_measure_ts(sk, skb);
@ -5632,7 +5639,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
/* step 5: check the ACK field */ /* step 5: check the ACK field */
if (th->ack) { if (th->ack) {
int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH); int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH) > 0;
switch (sk->sk_state) { switch (sk->sk_state) {
case TCP_SYN_RECV: case TCP_SYN_RECV: