tcp: add rfc3168, section 6.1.1.1. fallback
This work as a follow-up of commitf7b3bec6f5
("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 commit255cac91c3
("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.pdf Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Mirja Kühlewind <mirja.kuehlewind@tik.ee.ethz.ch> Signed-off-by: Brian Trammell <trammell@tik.ee.ethz.ch> Cc: Eric Dumazet <edumazet@google.com> Cc: Dave That <dave.taht@gmail.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
134e0dbe72
commit
492135557d
7 changed files with 38 additions and 1 deletions
|
@ -8,6 +8,7 @@ the data center network to provide multi-bit feedback to the end hosts.
|
|||
To enable it on end hosts:
|
||||
|
||||
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
|
||||
marking and be configured for marking when reaching defined switch buffer
|
||||
|
|
|
@ -267,6 +267,15 @@ tcp_ecn - INTEGER
|
|||
but do not request ECN on outgoing connections.
|
||||
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
|
||||
Enable FACK congestion avoidance and fast retransmission.
|
||||
The value is not used, if tcp_sack is not enabled.
|
||||
|
|
|
@ -77,6 +77,8 @@ struct netns_ipv4 {
|
|||
struct local_ports ip_local_ports;
|
||||
|
||||
int sysctl_tcp_ecn;
|
||||
int sysctl_tcp_ecn_fallback;
|
||||
|
||||
int sysctl_ip_no_pmtu_disc;
|
||||
int sysctl_ip_fwd_use_pmtu;
|
||||
int sysctl_ip_nonlocal_bind;
|
||||
|
|
|
@ -712,6 +712,8 @@ static inline u32 tcp_skb_timestamp(const struct sk_buff *skb)
|
|||
#define TCPHDR_ECE 0x40
|
||||
#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
|
||||
* TCP per-packet control information to the transmission code.
|
||||
* We also store the host-order sequence numbers in here too.
|
||||
|
|
|
@ -820,6 +820,13 @@ static struct ctl_table ipv4_net_table[] = {
|
|||
.mode = 0644,
|
||||
.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",
|
||||
.maxlen = sizeof(init_net.ipv4.ip_local_ports.range),
|
||||
|
|
|
@ -2411,12 +2411,15 @@ static int __net_init tcp_sk_init(struct net *net)
|
|||
goto fail;
|
||||
*per_cpu_ptr(net->ipv4.tcp_sk, cpu) = sk;
|
||||
}
|
||||
|
||||
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_probe_threshold = TCP_PROBE_THRESHOLD;
|
||||
net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL;
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
tcp_sk_exit(net);
|
||||
|
||||
|
|
|
@ -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
|
||||
tcp_ecn_make_synack(const struct request_sock *req, struct tcphdr *th,
|
||||
struct sock *sk)
|
||||
|
@ -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);
|
||||
|
||||
/* Make a copy, if the first transmission SKB clone we made
|
||||
|
|
Loading…
Reference in a new issue