Commit 49213555 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

tcp: add rfc3168, section 6.1.1.1. fallback

This work as a follow-up of commit f7b3bec6 ("net: allow setting ecn
via routing table") and adds RFC3168 section 6.1.1.1. fallback for outgoing
ECN connections. In other words, this work adds a retry with a non-ECN
setup SYN packet, as suggested from the RFC on the first timeout:

  [...] A host that receives no reply to an ECN-setup SYN within the
  normal SYN retransmission timeout interval MAY resend the SYN and
  any subsequent SYN retransmissions with CWR and ECE cleared. [...]

Schematic client-side view when assuming the server is in tcp_ecn=2 mode,
that is, Linux default since 2009 via commit 255cac91 ("tcp: extend
ECN sysctl to allow server-side only ECN"):

 1) Normal ECN-capable path:

    SYN ECE CWR ----->
                <----- SYN ACK ECE
            ACK ----->

 2) Path with broken middlebox, when client has fallback:

    SYN ECE CWR ----X crappy middlebox drops packet
                      (timeout, rtx)
            SYN ----->
                <----- SYN ACK
            ACK ----->

In case we would not have the fallback implemented, the middlebox drop
point would basically end up as:

    SYN ECE CWR ----X crappy middlebox drops packet
                      (timeout, rtx)
    SYN ECE CWR ----X crappy middlebox drops packet
                      (timeout, rtx)
    SYN ECE CWR ----X crappy middlebox drops packet
                      (timeout, rtx)

In any case, it's rather a smaller percentage of sites where there would
occur such additional setup latency: it was found in end of 2014 that ~56%
of IPv4 and 65% of IPv6 servers of Alexa 1 million list would negotiate
ECN (aka tcp_ecn=2 default), 0.42% of these webservers will fail to connect
when trying to negotiate with ECN (tcp_ecn=1) due to timeouts, which the
fallback would mitigate with a slight latency trade-off. Recent related
paper on this topic:

  Brian Trammell, Mirja Kühlewind, Damiano Boppart, Iain Learmonth,
  Gorry Fairhurst, and Richard Scheffenegger:
    "Enabling Internet-Wide Deployment of Explicit Congestion Notification."
    Proc. PAM 2015, New York.
  http://ecn.ethz.ch/ecn-pam15.pdf

Thus, when net.ipv4.tcp_ecn=1 is being set, the patch will perform RFC3168,
section 6.1.1.1. fallback on timeout. For users explicitly not wanting this
which can be in DC use case, we add a net.ipv4.tcp_ecn_fallback knob that
allows for disabling the fallback.

tp->ecn_flags are not being cleared in tcp_ecn_clear_syn() on output, but
rather we let tcp_ecn_rcv_synack() take that over on input path in case a
SYN ACK ECE was delayed. Thus a spurious SYN retransmission will not prevent
ECN being negotiated eventually in that case.

Reference: https://www.ietf.org/proceedings/92/slides/slides-92-iccrg-1.pdf
Reference: https://www.ietf.org/proceedings/89/slides/slides-89-tsvarea-1.pdfSigned-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarMirja Kühlewind <mirja.kuehlewind@tik.ee.ethz.ch>
Signed-off-by: default avatarBrian Trammell <trammell@tik.ee.ethz.ch>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Dave That <dave.taht@gmail.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 134e0dbe
...@@ -8,6 +8,7 @@ the data center network to provide multi-bit feedback to the end hosts. ...@@ -8,6 +8,7 @@ the data center network to provide multi-bit feedback to the end hosts.
To enable it on end hosts: To enable it on end hosts:
sysctl -w net.ipv4.tcp_congestion_control=dctcp sysctl -w net.ipv4.tcp_congestion_control=dctcp
sysctl -w net.ipv4.tcp_ecn_fallback=0 (optional)
All switches in the data center network running DCTCP must support ECN All switches in the data center network running DCTCP must support ECN
marking and be configured for marking when reaching defined switch buffer marking and be configured for marking when reaching defined switch buffer
......
...@@ -267,6 +267,15 @@ tcp_ecn - INTEGER ...@@ -267,6 +267,15 @@ tcp_ecn - INTEGER
but do not request ECN on outgoing connections. but do not request ECN on outgoing connections.
Default: 2 Default: 2
tcp_ecn_fallback - BOOLEAN
If the kernel detects that ECN connection misbehaves, enable fall
back to non-ECN. Currently, this knob implements the fallback
from RFC3168, section 6.1.1.1., but we reserve that in future,
additional detection mechanisms could be implemented under this
knob. The value is not used, if tcp_ecn or per route (or congestion
control) ECN settings are disabled.
Default: 1 (fallback enabled)
tcp_fack - BOOLEAN tcp_fack - BOOLEAN
Enable FACK congestion avoidance and fast retransmission. Enable FACK congestion avoidance and fast retransmission.
The value is not used, if tcp_sack is not enabled. The value is not used, if tcp_sack is not enabled.
......
...@@ -77,6 +77,8 @@ struct netns_ipv4 { ...@@ -77,6 +77,8 @@ struct netns_ipv4 {
struct local_ports ip_local_ports; struct local_ports ip_local_ports;
int sysctl_tcp_ecn; int sysctl_tcp_ecn;
int sysctl_tcp_ecn_fallback;
int sysctl_ip_no_pmtu_disc; int sysctl_ip_no_pmtu_disc;
int sysctl_ip_fwd_use_pmtu; int sysctl_ip_fwd_use_pmtu;
int sysctl_ip_nonlocal_bind; int sysctl_ip_nonlocal_bind;
......
...@@ -712,6 +712,8 @@ static inline u32 tcp_skb_timestamp(const struct sk_buff *skb) ...@@ -712,6 +712,8 @@ static inline u32 tcp_skb_timestamp(const struct sk_buff *skb)
#define TCPHDR_ECE 0x40 #define TCPHDR_ECE 0x40
#define TCPHDR_CWR 0x80 #define TCPHDR_CWR 0x80
#define TCPHDR_SYN_ECN (TCPHDR_SYN | TCPHDR_ECE | TCPHDR_CWR)
/* This is what the send packet queuing engine uses to pass /* This is what the send packet queuing engine uses to pass
* TCP per-packet control information to the transmission code. * TCP per-packet control information to the transmission code.
* We also store the host-order sequence numbers in here too. * We also store the host-order sequence numbers in here too.
......
...@@ -820,6 +820,13 @@ static struct ctl_table ipv4_net_table[] = { ...@@ -820,6 +820,13 @@ static struct ctl_table ipv4_net_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec .proc_handler = proc_dointvec
}, },
{
.procname = "tcp_ecn_fallback",
.data = &init_net.ipv4.sysctl_tcp_ecn_fallback,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec
},
{ {
.procname = "ip_local_port_range", .procname = "ip_local_port_range",
.maxlen = sizeof(init_net.ipv4.ip_local_ports.range), .maxlen = sizeof(init_net.ipv4.ip_local_ports.range),
......
...@@ -2411,12 +2411,15 @@ static int __net_init tcp_sk_init(struct net *net) ...@@ -2411,12 +2411,15 @@ static int __net_init tcp_sk_init(struct net *net)
goto fail; goto fail;
*per_cpu_ptr(net->ipv4.tcp_sk, cpu) = sk; *per_cpu_ptr(net->ipv4.tcp_sk, cpu) = sk;
} }
net->ipv4.sysctl_tcp_ecn = 2; net->ipv4.sysctl_tcp_ecn = 2;
net->ipv4.sysctl_tcp_ecn_fallback = 1;
net->ipv4.sysctl_tcp_base_mss = TCP_BASE_MSS; net->ipv4.sysctl_tcp_base_mss = TCP_BASE_MSS;
net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD; net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD;
net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL; net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL;
return 0;
return 0;
fail: fail:
tcp_sk_exit(net); tcp_sk_exit(net);
......
...@@ -350,6 +350,15 @@ static void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb) ...@@ -350,6 +350,15 @@ static void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)
} }
} }
static void tcp_ecn_clear_syn(struct sock *sk, struct sk_buff *skb)
{
if (sock_net(sk)->ipv4.sysctl_tcp_ecn_fallback)
/* tp->ecn_flags are cleared at a later point in time when
* SYN ACK is ultimatively being received.
*/
TCP_SKB_CB(skb)->tcp_flags &= ~(TCPHDR_ECE | TCPHDR_CWR);
}
static void static void
tcp_ecn_make_synack(const struct request_sock *req, struct tcphdr *th, tcp_ecn_make_synack(const struct request_sock *req, struct tcphdr *th,
struct sock *sk) struct sock *sk)
...@@ -2615,6 +2624,10 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) ...@@ -2615,6 +2624,10 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
} }
} }
/* RFC3168, section 6.1.1.1. ECN fallback */
if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN_ECN) == TCPHDR_SYN_ECN)
tcp_ecn_clear_syn(sk, skb);
tcp_retrans_try_collapse(sk, skb, cur_mss); tcp_retrans_try_collapse(sk, skb, cur_mss);
/* Make a copy, if the first transmission SKB clone we made /* Make a copy, if the first transmission SKB clone we made
......
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