Ubuntu OpenWrt 开启 TCP Fast Open,检查是否启用

什么是 TCP Fast Open - TFO

TCP Fast Open,简称TFO, 意思是TCP快速打开,是对计算机网络中传输控制协议(TCP)连接的一种简化握手手续的拓展,用于提高两端点间连接的打开速度

二个人只有在认识的基础上才可能深入交流。客户端和服务要传递数据,也需要先“认识”,称为“握手”,握手成功才正式开始传送数据

服务端和客户端传递数据前需要握手三次,这会导致延时,启用 TFO 后,如果验证成功,它可以在三次握手最终的ACK包收到之前就开始发送数据,这样便跳过了一个绕路的行为,于是在传输开始时就降低了延迟

也就是说,TFO 降低了握手阶段的延迟,至于握手成功后数据传递的速度,和 TFO 是没有关系的

开启 TCP Fast Open 快速打开的先决条件

Linux kernel 3.7 及以上才支持 TCP Fast Open

在服务端的Ubuntu 检查一下:

uname -r
4.15.0-34-generic

再登录客户端 OpenWrt 路由器确认一下是否可以打开 TCP Fast Open

uname -r
4.9.120

Linux kernel 3.13 及以上默认已经开启了TFO,Linux服务器检查TCP Fast Open是否开启的方法:

cat /proc/sys/net/ipv4/tcp_fastopen
1

如果返回 0 表示没有开启TFO,非0则是默认开启了

tcp_fastopen三个数值选项的含义

tcp_fastopen选项是二进制位掩码,其中第一位启用或禁用客户端支持(默认开启),第二位设置服务端支持(默认关闭),第3位设置是否允许SYN数据包中的数据而不使用TFO cookie选项

  • tcp_fastopen = 1

    只能在传出连接上启用(仅限客户端)

  • tcp_fastopen = 2

    仅在侦听套接字(服务端)上允许TFO

  • tcp_fastopen = 3

    客户端和服务端都启用TFO

请注意,即使启用了这些选项,也必须启用应用程序级支持。我们在服务端和客户端的操作系统上启用了 tcp fast open,如果软件层面不支持,还是起不到TCP通信加速的作用

临时启用双向 tcp_fastopen

$ sudo sysctl -w net.ipv4.tcp_fastopen=3
$ cat net.ipv4.tcp_fastopen
3

查看一下 sysctl -w的用法:

$ sysctl --help | grep write
-w, --write          enable writing a value to variable

-w 意思是把指定值写入变量

Ubuntu系统开启 TFO 加速

通过 sysctl 我们可以定义内核参数

自定义 TFO 有两种途径:

  • /etc/sysctl.conf
  • /etc/sysctl.d/*.conf

这两种途径有什么区别?

/etc/sysctl.d/README:

This directory contains settings similar to those found in /etc/sysctl.conf. In general, files in the 10-.conf range come from the procps package and serve as system defaults. Other packages install their files in the 30-.conf range, to override system defaults. End-users can use 60-*.conf and above, or use /etc/sysctl.conf directly, which overrides anything in this directory.

/etc/sysctl.d/目录包含与/etc/sysctl.conf中类似的设置。通常,10-.conf范围内的文件来自procps包和 作为系统默认值。 其他包安装他们的文件30-.conf范围,覆盖系统默认值。 最终用户可以使用60 -*.conf 以上,或直接使用/etc/sysctl.conf,它会覆盖任何内容 这个目录

也就是开头数字小的.conf先加载,后面的覆盖前面,最后加载/etc/sysctl.conf

网上的教程一般是把 TFO 设置写在 /etc/sysctl.conf,那么哪一种方式最佳呢

有少数的软件包建议直接编辑/etc/sysctl.conf,但这可能是为了与旧的GNU / Linux发行版的兼容,最近软件包一般建议使用.d配置目录,这样更加灵活

用的.d目录(如/etc/sysctl.d/)的重点是允许应用程序/管理员在那里添加文件,这比添加或删除单个文件中的条目更容易,更安全。 例如,可以将TFO的参数放在文件/etc/sysctl.d/98-tcp_fastopen.conf中,一看就知道这些参数是针对tcp_fastopen的。 任何自动化工具(包括安装程序)也更容易将文件放在目录中而不是附加到现有文件

明白了原理以后,自然尽量不去编辑 /etc/sysctl.conf 了

$ su
# echo 'net.ipv4.tcp_fastopen=3' > /etc/sysctl.d/98-tcp_fastopen.conf
# reboot
$ cat /proc/sys/net/ipv4/tcp_fastopen
3

上面命令,我们切换到 root 用户,把设置写入文件,然后重启Ubuntu验证,返回3说明设置正确

服务端生成 TCp fast open 持久化密钥

Linux 内核3.13开始,在应用程序首次设置相关的setsockopt系统调用选项时生成密钥。 在设置密钥之前,proc值全为零

默认情况下,由于密钥在系统重新启动之后不会持续存在,因此正规地使用TFO,应该包括通过sysctl安全地保存密钥,比如生成随机密钥,设置限制性文件权限。 这将确保客户端可以使用现有cookie而无需生成新密钥

要生成新密钥并通过sysctl进行持久化,可以执行以下命令:

$ su
# 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/98-tcp_fastopen_key.conf
# chmod 600 /etc/sysctl.d/98-tcp_fastopen_key.conf; chown root /etc/sysctl.d/98-tcp_fastopen_key.conf
# sysctl -p /etc/sysctl.d/98-tcp_fastopen_key.conf
# unset RAND NEWKEY

转换 TCP fast open 密钥

在Linux环境下,有二种方法可以显示当TFO密钥:

  • $ sudo cat/proc/sys/net/ipv4/tcp_fastopen_key
  • $ sudo sysctl net.ipv4.tcp_fastopen_key

同理,以可以用之改变密钥

TFO密钥是16个字节,表示为32个字符的十六进制字符串,分为4个8个字符的块,用短划线分隔,类似这样:

32100e0a-9876daaf-7654b836-21096051

可以使用前述方法实现密钥的转换

在多服务器服务器环境中,您需要随机生成一次密钥,并在所有服务器上设置相同的密钥

如果有多个翻墙VPS,需要随机切换使用,建议所有VPS使用相同密钥

OpenWrt系统开启 TFO 流量加速

vi /etc/sysctl.conf

# add line
net.ipv4.tcp_fastopen = 3

执行如下命令使之生效:

sysctl -p

Shadowsocks 软件启用 TCP Fast Open 加速

在服务端和 OpenWrt 路由器的 shadowsocks-libev 配置文件 config.json / shadowsocks.json 中加上:

"fast_open": true

重启服务端和客户端 shadowsocks-libev

测试 shadowsocks 服务端 TFO 有没有正常工作

登录shadowsocks服务端 Ubnutu 后,运行命令测试:

# 清空 IP 记录
sudo ip tcp_metrics flush all
ip tcp_metrics
# 应该是空

这时你打开 https://www.youtube.com

然后运行命令:

ip tcp_metrics

如果正常,会看到数条类似下面的记录

TCP Fast OPen fo_cookie

216.58.200.277 age 5.356sec fo_mss 1380 fo_cookie 87aaa1f0c8761336 source 210.98.76

发现了 fo_mssfo_cookie 字段没有,这是 TCP Fast Open 特有的字段,其中 fo_cookie 确认我们与行开头的IP地址216.58.200.277 的通信使用了TFO

如果系统还提供除了翻墙的其他网络服务,那么ip记录可能很多,其中一些记录并没有 fo_cookie 字段,可以运行命令把含有 fo_cookie的条目找出来:

ip tcp_metrics | grep fo_cookie

如果一条 fo_cookie 记录也找不到,说明开启 TFO 没有成功

查看 TCP fast open的详细工作状态

root用户用这条命令查看TCP fast open的详细状态:

root@ubuntu:~# grep '^TcpExt:' /proc/net/netstat | cut -d ' ' -f 87-92  | column -t

我得到的值是:

TCPOFOMerge  TCPChallengeACK  TCPSYNChallenge  TCPFastOpenActive  TCPFastOpenActiveFail  TCPFastOpenPassive  TCPFastOpenPassiveFail  TCPFastOpenListenOverflow  TCPFastOpenCookieReqd  TCPFastOpenBlackhole  TCPSpuriousRtxHostQueues  BusyPollRxPackets  TCPAutoCorking  TCPFromZeroWindowAdv  TCPToZeroWindowAdv  TCPWantZeroWindowAdv
1            12               5                79                 23                     621                 1                       70                         2                      1                     0                        0                  0               48                    48                  84

如果非 root 用户执行,结果是:

TCPFastOpenPassiveFail  TCPFastOpenListenOverflow  TCPFastOpenCookieReqd  TCPFastOpenBlackhole  TCPSpuriousRtxHostQueues  BusyPollRxPackets
1                       70                         2                      1                     0                         0

一定要注意,root用户才能查看 TCPFastOpenPassive 的值

测试 shadowsocks-libev 客户端有没有启用 TFO 加速

在shadowsocks-libev客户端 ss-rrdir | ss-tunnel 的启动参数中加上 -v 会在控制台显示 TFO 是否启用

kige@openwrt:~# /etc/init.d/shadowsocks stop
/usr/bin/ss-redir -v -b 0.0.0.0 -c /etc/shadowsocks-libev/config.json -f /var/run/shadowsocks.pid

TCP fast open 状态值含义

  • TCPFastOpenActive - 成功的出站TFO连接数
  • TCPFastOpenActiveFail - 收到的SYN-ACK数据包的数量,这些数据包未确认SYN数据包中发送的数据并导致无SYN数据的重传。 请注意原始SYN数据包包含cookie + 数据,这不是连接到不支持TFO的服务器的数量
  • TCPFastOpenPassive - 成功的入站TFO连接数
  • TCPFastOpenPassiveFail - TFO cookie无效的入站SYN数据包数
  • TCPFastOpenCookieReqd - 请求TFO设置但没有cookie的入站SYN数据包数
  • TCPFastOpenListenOverflow - 由于套接字已超过最大队列长度而将禁用TFO的入站SYN数据包的数量

上面检测 TCP fast open 状态,得到:

TCPFastOpenPassive
621

成功的入站TFO连接数621,这是各项值中的最大值,说明 TFO 工作正常

常见问题

  • TFO丢包

    当带有TFO的包经过路由器 可能会被丢包 不同运营商的策略也不同 如果配置了NAT地址池 在第二次连接时可能会成功 取决于NAT表老化时间 客户端IP改变和NAT之后的公网IP改变都会影响TFO的正常使用 服务端收到不正确的包后依旧可以发送syn-ack 且退回为3WHS 可以选择在服务端单方面将模式改为1或者客户端改为0 不影响服务端对外连接的性能

相关资源: