Commit ad2b9b0f authored by Priyaranjan Jha's avatar Priyaranjan Jha Committed by David S. Miller

tcp: skip DSACKs with dubious sequence ranges

Currently, we use length of DSACKed range to compute number of
delivered packets. And if sequence range in DSACK is corrupted,
we can get bogus dsacked/acked count, and bogus cwnd.

This patch put bounds on DSACKed range to skip update of data
delivery and spurious retransmission information, if the DSACK
is unlikely caused by sender's action:
- DSACKed range shouldn't be greater than maximum advertised rwnd.
- Total no. of DSACKed segments shouldn't be greater than total
  no. of retransmitted segs. Unlike spurious retransmits, network
  duplicates or corrupted DSACKs shouldn't be counted as delivery.
Signed-off-by: default avatarPriyaranjan Jha <priyarjha@google.com>
Signed-off-by: default avatarNeal Cardwell <ncardwell@google.com>
Signed-off-by: default avatarYuchung Cheng <ycheng@google.com>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1ec8e748
...@@ -288,6 +288,7 @@ enum ...@@ -288,6 +288,7 @@ enum
LINUX_MIB_TCPTIMEOUTREHASH, /* TCPTimeoutRehash */ LINUX_MIB_TCPTIMEOUTREHASH, /* TCPTimeoutRehash */
LINUX_MIB_TCPDUPLICATEDATAREHASH, /* TCPDuplicateDataRehash */ LINUX_MIB_TCPDUPLICATEDATAREHASH, /* TCPDuplicateDataRehash */
LINUX_MIB_TCPDSACKRECVSEGS, /* TCPDSACKRecvSegs */ LINUX_MIB_TCPDSACKRECVSEGS, /* TCPDSACKRecvSegs */
LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */
__LINUX_MIB_MAX __LINUX_MIB_MAX
}; };
......
...@@ -293,6 +293,7 @@ static const struct snmp_mib snmp4_net_list[] = { ...@@ -293,6 +293,7 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TcpTimeoutRehash", LINUX_MIB_TCPTIMEOUTREHASH), SNMP_MIB_ITEM("TcpTimeoutRehash", LINUX_MIB_TCPTIMEOUTREHASH),
SNMP_MIB_ITEM("TcpDuplicateDataRehash", LINUX_MIB_TCPDUPLICATEDATAREHASH), SNMP_MIB_ITEM("TcpDuplicateDataRehash", LINUX_MIB_TCPDUPLICATEDATAREHASH),
SNMP_MIB_ITEM("TCPDSACKRecvSegs", LINUX_MIB_TCPDSACKRECVSEGS), SNMP_MIB_ITEM("TCPDSACKRecvSegs", LINUX_MIB_TCPDSACKRECVSEGS),
SNMP_MIB_ITEM("TCPDSACKIgnoredDubious", LINUX_MIB_TCPDSACKIGNOREDDUBIOUS),
SNMP_MIB_SENTINEL SNMP_MIB_SENTINEL
}; };
......
...@@ -885,21 +885,34 @@ struct tcp_sacktag_state { ...@@ -885,21 +885,34 @@ struct tcp_sacktag_state {
struct rate_sample *rate; struct rate_sample *rate;
}; };
/* Take a notice that peer is sending D-SACKs */ /* Take a notice that peer is sending D-SACKs. Skip update of data delivery
* and spurious retransmission information if this DSACK is unlikely caused by
* sender's action:
* - DSACKed sequence range is larger than maximum receiver's window.
* - Total no. of DSACKed segments exceed the total no. of retransmitted segs.
*/
static u32 tcp_dsack_seen(struct tcp_sock *tp, u32 start_seq, static u32 tcp_dsack_seen(struct tcp_sock *tp, u32 start_seq,
u32 end_seq, struct tcp_sacktag_state *state) u32 end_seq, struct tcp_sacktag_state *state)
{ {
u32 seq_len, dup_segs = 1; u32 seq_len, dup_segs = 1;
if (before(start_seq, end_seq)) { if (!before(start_seq, end_seq))
seq_len = end_seq - start_seq; return 0;
if (seq_len > tp->mss_cache)
dup_segs = DIV_ROUND_UP(seq_len, tp->mss_cache); seq_len = end_seq - start_seq;
} /* Dubious DSACK: DSACKed range greater than maximum advertised rwnd */
if (seq_len > tp->max_window)
return 0;
if (seq_len > tp->mss_cache)
dup_segs = DIV_ROUND_UP(seq_len, tp->mss_cache);
tp->dsack_dups += dup_segs;
/* Skip the DSACK if dup segs weren't retransmitted by sender */
if (tp->dsack_dups > tp->total_retrans)
return 0;
tp->rx_opt.sack_ok |= TCP_DSACK_SEEN; tp->rx_opt.sack_ok |= TCP_DSACK_SEEN;
tp->rack.dsack_seen = 1; tp->rack.dsack_seen = 1;
tp->dsack_dups += dup_segs;
state->flag |= FLAG_DSACKING_ACK; state->flag |= FLAG_DSACKING_ACK;
/* A spurious retransmission is delivered */ /* A spurious retransmission is delivered */
...@@ -1153,6 +1166,11 @@ static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb, ...@@ -1153,6 +1166,11 @@ static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb,
} }
dup_segs = tcp_dsack_seen(tp, start_seq_0, end_seq_0, state); dup_segs = tcp_dsack_seen(tp, start_seq_0, end_seq_0, state);
if (!dup_segs) { /* Skip dubious DSACK */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDSACKIGNOREDDUBIOUS);
return false;
}
NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPDSACKRECVSEGS, dup_segs); NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPDSACKRECVSEGS, dup_segs);
/* D-SACK for already forgotten data... Do dumb counting. */ /* D-SACK for already forgotten data... Do dumb counting. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment