So explicit congestion notification (ECN for short) is a nifty method to allow to decouple congestion signalling from packet loss (in typical situations, once sh!t hits the fan packet loss will happen and needs to be interpreted even with ECN active). So instead of dropping a packet on overload, we now can 'mark' a packet with a congestion experienced (CE) mark, which the receiver will interpret as sign of congestion and it will signal back to the sender to slow down. That has the obvious advantages of quicker congestion signalling and often without packet loss and hence the need to retransmit the dropped packet.
Linux qdisc's like OpenWrt's default fq_codel and cake support ECN signalling making it easy to employ this on an OpenWrt router. However ECN is something the end points need to negotiate and evaluate to actually help. Which brings me to the reason for this thread, give shoret instructions how to do that for different operating systems and how to confirm it is working as intended:
Enabling active (rfc3168) ECN negotiation (inbound and outbound):
windows:
netsh interface tcp show global
as admin:
netsh interface tcp set global ecn=enabled
while you are at it consider enabling TCP timestamps:
netsh interface tcp set global timestamps=enabled
macos:*
sysctl -w net.inet.tcp.ecn_initiate_out=1
sysctl -w net.inet.tcp.ecn_negotiate_in=1
sysctl -w net.inet.tcp.disable_tcp_heuristics=1 # if this is not disabled macos will decide when to use ecn and when not to
linux# (see https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt):
sysctl -w net.ipv4.tcp_ecn=1
Confirming ECN on the IP level:
Note, this command is intended to be run on your OpenWrt router (might require to install tcpdump first: opkg update ; opkg install tcpdump
) my wan interface is pppoe-wan, so replace with your own interface of interest. When trying to confirm CE marks generated by an AQM on your OpenWrt router, please note that you will see outgoing/egress CE marks on the wan interface, but incoming/ingress only on a lan interface, so consider capturing on br-lan for those:
# ECN IPv4/6
tcpdump -i pppoe-wan -v -n '(ip6 and (ip6[0:2] & 0x30) >> 4 != 0)' or '(ip and (ip[1] & 0x3) != 0)' # NOT Not-ECT
tcpdump -i pppoe-wan -v -n '(ip6 and (ip6[0:2] & 0x30) >> 4 == 1)' or '(ip and (ip[1] & 0x3) == 1)' # ECT(1)
tcpdump -i pppoe-wan -v -n '(ip6 and (ip6[0:2] & 0x30) >> 4 == 2)' or '(ip and (ip[1] & 0x3) == 2)' # ECT(0)
tcpdump -i pppoe-wan -v -n '(ip6 and (ip6[0:2] & 0x30) >> 4 == 3)' or '(ip and (ip[1] & 0x3) == 3)' # CE
Confirming ECN on the TCP level:
Most ECN action will happen on TCP connections, so it can be helpful to also capture ECN related TCP packets. Note, since the endpoints set the TCP flags, these should both be visible independent on whether you capture on the WAN or a LAN interface.:
# TCP ECN IPv4/6: (for IPv6 see see https://ask.wireshark.org/question/27153/i-am-trying-to-capture-tcp-syn-on-ipv6-packets-but-i-only-get-ipv4/)
tcpdump -i pppoe-wan -v -n '(tcp[tcpflags] & (tcp-ece|tcp-cwr) != 0)' or '((ip6[6] = 6) and (ip6[53] & 0xC0 != 0))' # TCP ECN flags, ECN in action
tcpdump -i pppoe-wan -v -n '(tcp[tcpflags] & tcp-ece != 0)' or '((ip6[6] = 6) and (ip6[53] & 0x40 != 0))' # TCP ECN flags, ECE: ECN-Echo (reported as E)
tcpdump -i pppoe-wan -v -n '(tcp[tcpflags] & tcp-cwr != 0)' or '((ip6[6] = 6) and (ip6[53] & 0x80 != 0))' # TCP ECN flags, CWR: Congestion Window Reduced (reported as W)
Quick note of the expected action sequence for an individual flow for rfc3168 ECN:
- AQM marks a packet CE in the IP header (the packet will have been either ECT(0) or ECT(1), the AQM will not mark packets with Not-ECT, instead they will be simply dropped on congestion)
- The receiver TCP stack will see the CE mark and assert the ECN Echo TCP flag (short ECE) on all outgoing reverse packets (ACK or data*ACK)
- the original sender will receive that ECE mark and in response will reduce its congestion window (CWIN) and set the Congestion window reduced TCP flag (short CWR)
- the receiver will stop asserting ECE once it receives that CWR.
rfc3168 has a more detailed description, but this here should suffice to get an idea about what to look for and what sequence of flags to expect.
*) Macos allows to query some TCP statistics with the following command sudo netstat -sp tcp
(note: without sudo
this will return all zeros). Here is an example of the ECN related data points:
128984 client connections attempted to negotiate ECN
29652 client connections successfully negotiated ECN
86977 times graceful fallback to Non-ECN connection
10011 times lost ECN negotiating SYN, followed by retransmission
2157 server connections attempted to negotiate ECN
2157 server connections successfully negotiated ECN
0 time lost ECN negotiating SYN-ACK, followed by retransmission
6498 times received congestion experienced (CE) notification
190 times CWR was sent in response to ECE
117716 times sent ECE notification
261 connections received CE atleast once
81 connections received ECE atleast once
7216 connections using ECN have seen packet loss but no CE
204 connections using ECN have seen packet loss and CE
136 connections using ECN received CE but no packet loss
91 connections fell back to non-ECN due to SYN-loss
156 connections fell back to non-ECN due to reordering
0 connection fell back to non-ECN due to excessive CE-markings
2 connections fell back caused by connection drop due to RST
5 connections fell back due to drop after multiple retransmits
0 connection fell back due to RST after SYN
#) Linux offers some ECN related information in the output of netstat -s
(thanks @timur.davletshin ):
IpExt:
InMcastPkts: 2310212
OutMcastPkts: 143473
InBcastPkts: 380057
InOctets: 45238861245
OutOctets: 76413395773
InMcastOctets: 960413814
OutMcastOctets: 13513741
InBcastOctets: 126574293
InNoECTPkts: 107561727
InECT0Pkts: 1016946
InCEPkts: 432