TCP Fast Open

From Wikitech

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


References