TCP Fast Open
Appearance
Metrics
/proc/net/netstat provides a bunch of TFO-related metrics:
- TCPFastOpenActive: number of successful outbound TFO connections
- TCPFastOpenActiveFail: number of SYN-ACK packets received that did not acknowledge data sent in the SYN packet and caused a retransmissions without SYN data. Note that the original SYN packet contained a cookie + data, this is not the number of connections to servers that didn’t support TFO
- TCPFastOpenPassive: number of successful inbound TFO connections
- TCPFastOpenPassiveFail: number of inbound SYN packets with TFO cookie that was invalid
- TCPFastOpenCookieReqd: number of inbound SYN packets requesting TFO with TFO set but no cookie
- TCPFastOpenListenOverflow: number of inbound SYN packets that will have TFO disabled because the socket has exceeded the max queue length
Other interesting metrics are:
- TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down retransmissions into SYN, fast-retransmits, timeout retransmits, etc.
- TCPOrigDataSent: number of outgoing packets with original data (excluding retransmission but including data-in-SYN). This counter is different from TcpOutSegs because TcpOutSegs also tracks pure ACKs. TCPOrigDataSent is more useful to track the TCP retransmission rate.
Detecting server-side TFO support
The initial portion of the 3WHS can be used to check whether a remote TCP server supports TFO. For example, with scapy we can craft a SYN packet with the TFO option set and check whether the SYN/ACK response from the server includes the TFO option as well. If that is the case, TFO is enabled on the target server.
from scapy.all import sr1, IP, TCP
dst = "en.wikipedia.org"
dport = 443
res = sr1(IP(dst=dst)/TCP(dport=dport,flags="S",options=[('TFO', '')]), verbose=False)
print 'TFO' in dict(res[1].options)
Python client using BSD sockets
#!/usr/bin/env python
import sys
import socket
TCP_FASTOPEN = 0x20000000
dest_name = sys.argv[1]
port = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.sendto("HEAD / HTTP/1.0\r\n\r\n", TCP_FASTOPEN, (dest_name, port))
data = s.recv(1024)
s.close()
print data
CLI tools
tshark allows to easily filter packets with TFO option set:
tshark -f 'tcp[tcpflags] & tcp-syn != 0' -Y 'tcp.option_kind == 34'
curl version 7.49.0 or greater allows establishing a TCP connection with TFO, albeit not in combination with TLS as of July 2016.
curl --tcp-fastopen http://en.wikipedia.org
Server key generation
RAND=$(openssl rand -hex 16)
NEWKEY=${RAND:0:8}-${RAND:8:8}-${RAND:16:8}-${RAND:24:8}
echo "net.ipv4.tcp_fastopen_key=$NEWKEY" > /etc/sysctl.d/50-tcp_fastopen_key.conf
chmod 600 /etc/sysctl.d/50-tcp_fastopen_key.conf; chown root /etc/sysctl.d/50-tcp_fastopen_key.conf
sysctl -p /etc/sysctl.d/50-tcp_fastopen_key.conf
unset RAND NEWKEY