linux开启tcp_timestamps和tcp_tw_recycle引发的问题研究

环境:centos7.4 内核版本3.10

最近看内核参数tcp_tw_recycle(该参数在内核 4.12 之后被移除),它用于快速回收处理TIME_WAIT状态的socket。搜索该参数相关的资料,发现同时启用该参数和tcp_timestamps后有可能在NAT环境下导致客户端始连接失败,抓包表现为:客户端一直发送SYN报文,但服务端不响应。但这些文章中只给出了如何解决问题,并没有给出如何复现问题。特别怪异的是,服务端是被动关闭的,并不会进入TIME_WAIT状态,到底怎么产生的呢?

先使用如下拓扑复现该场景,其中10.85.3.51机器为NAT服务器,10.85.1.2和10.85.3.52通过NAT服务器访问server 10.85.3.111:19090

+-------------+
|  10.85.1.2  +------------+
+-------------+            |
                     +-----+-------+         +----------------------+
                     |  10.85.3.51 +---------+  10.85.3.111:19090   |
                     +-----+-------+         +----------------------+
+-------------+            |
|  10.85.3.52 +------------+
+-------------+

在10.85.3.51机器上配置如下iptables表项,用于转发client和server之间的TCP报文。(10.85.3.51需要开启net.ipv4.ip_forward功能)

# iptables -t nat -I PREROUTING -d 10.85.3.51 -p tcp -m tcp --dport 29090 -j DNAT --to 10.85.3.111:19090# iptables -t nat -I POSTROUTING -d 10.85.3.111 -p tcp -m tcp --dport 19090 -j SNAT --to 10.85.3.51
  • 首先开启tcp_timestamps,关闭tcp_tw_recycle

在10.85.3.111上进行抓包并且启动10.85.1.2和10.85.3.52进行连接。报文如下,其中第4和第7条为两个连接的TCP SYN报文,后续server都进行了回复,两条连接正常建链

1 # tcpdump -i eth0 src port 19090 or dst port 19090
2 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
3 listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
4 17:39:27.970358 IP 10.85.3.51.57104 > 10.85.3.111.19090: Flags [S], seq 2466985868, win 25200, options [mss 1260,sackOK,TS val 3075335984 ecr 0,nop,wscale 7], length 0
5 17:39:27.970417 IP 10.85.3.111.19090 > 10.85.3.51.57104: Flags [S.], seq 2846609535, ack 2466985869, win 24960, options [mss 1260,sackOK,TS val 2612548200 ecr 3075335984,nop,wscale 7], length 0
6 17:39:27.970783 IP 10.85.3.51.57104 > 10.85.3.111.19090: Flags [.], ack 1, win 197, options [nop,nop,TS val 3075335985 ecr 2612548200], length 0
7 17:39:29.059890 IP 10.85.3.51.34230 > 10.85.3.111.19090: Flags [S], seq 2892210420, win 25200, options [mss 1260,sackOK,TS val 1740811766 ecr 0,nop,wscale 7], length 0
8 17:39:29.059949 IP 10.85.3.111.19090 > 10.85.3.51.34230: Flags [S.], seq 3434079625, ack 2892210421, win 24960, options [mss 1260,sackOK,TS val 2612549289 ecr 1740811766,nop,wscale 7], length 0
9 17:39:29.060623 IP 10.85.3.51.34230 > 10.85.3.111.19090: Flags [.], ack 1, win 197, options [nop,nop,TS val 1740811767 ecr 2612549289], length 0

启用tcp_tw_recycle,重复上面操作。发现及时后面一个连接的SYN报文的时间戳小于前面一个连接的SYN报文中的时间戳,也能够正常建链,并没有出现连接异常。

1 # tcpdump -i eth0 src port 19090 or dst port 19090
2 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
3 listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
4 17:49:12.111152 IP 10.85.3.51.58164 > 10.85.3.111.19090: Flags [S], seq 2599215624, win 25200, options [mss 1260,sackOK,TS val 3075920126 ecr 0,nop,wscale 7], length 0
5 17:49:12.111221 IP 10.85.3.111.19090 > 10.85.3.51.58164: Flags [S.], seq 795235982, ack 2599215625, win 24960, options [mss 1260,sackOK,TS val 2613132341 ecr 3075920126,nop,wscale 7], length 0
6 17:49:12.111766 IP 10.85.3.51.58164 > 10.85.3.111.19090: Flags [.], ack 1, win 197, options [nop,nop,TS val 3075920127 ecr 2613132341], length 0
7 17:49:12.871092 IP 10.85.3.51.34234 > 10.85.3.111.19090: Flags [S], seq 3696139072, win 25200, options [mss 1260,sackOK,TS val 1741395578 ecr 0,nop,wscale 7], length 0
8 17:49:12.871149 IP 10.85.3.111.19090 > 10.85.3.51.34234: Flags [S.], seq 3928136503, ack 3696139073, win 24960, options [mss 1260,sackOK,TS val 2613133101 ecr 1741395578,nop,wscale 7], length 0
9 17:49:12.871697 IP 10.85.3.51.34234 > 10.85.3.111.19090: Flags [.], ack 1, win 197, options [nop,nop,TS val 1741395579 ecr 2613133101], length 0
  • 后来在这篇文章中找到灵感。正常TCP TIME_WATI时长为2MSL,用于挥手阶段最后一个ACK报文的重传,以及防止当前连接上滞留的报文影响到下一个连接。当启用tcp_tw_recycle后,系统会在一个RTO的极短时间内回收处于TIME_WAIT状态的socket,但仍然无法杜绝接收到上一个连接在链路上滞留的报文。为了防止这种情况的发生,在启用tcp_tw_recycle的情况下,由于已经释放了socket,系统无法使用socket来标记一条连接,只能退而求其次,通过判断对端IP发过来的报文的时间戳来判断该报文是新产生的还是老的报文,如果是老报文,则丢弃且不回复。

因此复现场景为:服务端主动断开与客户端的一条连接,在后续的TCP_PAWS_MSL(60s)时间内,如果客户端发过来的SYN报文的TSVal时间戳小于系统保留的上一个连接的时间戳,则该SYN报文会被丢弃,实际表现为客户端连接超时或很慢(60s之后可正常连接)

  1. 首先server使用命令 telnet 10.85.3.51 22 连接NAT机器,并立即断开连接,此时server会很快回收一个TIME_WAIT的socket。在server端抓包,可以看到保存的该连接上对端发来的最后一个时间戳为3035582641
 1 # tcpdump -i eth0 src host 10.85.3.51 or dst host 10.85.3.51
 2 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
 3 listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
 4 22:45:10.015335 IP 10.85.3.111.49416 > 10.85.3.51.ssh: Flags [S], seq 1039611753, win 25200, options [mss 1260,sackOK,TS val 2630890245 ecr 0,nop,wscale 7], length 0
 5 22:45:10.016055 IP 10.85.3.51.ssh > 10.85.3.111.49416: Flags [S.], seq 489573340, ack 1039611754, win 24960, options [mss 1260,sackOK,TS val 3035577005 ecr 2630890245,nop,wscale 7], length 0
 6 22:45:10.016074 IP 10.85.3.111.49416 > 10.85.3.51.ssh: Flags [.], ack 1, win 197, options [nop,nop,TS val 2630890246 ecr 3035577005], length 0
 7 22:45:10.023482 IP 10.85.3.51.ssh > 10.85.3.111.49416: Flags [P.], seq 1:22, ack 1, win 195, options [nop,nop,TS val 3035577013 ecr 2630890246], length 21
 8 22:45:10.023507 IP 10.85.3.111.49416 > 10.85.3.51.ssh: Flags [.], ack 22, win 197, options [nop,nop,TS val 2630890253 ecr 3035577013], length 0
 9
10 22:45:15.648562 IP 10.85.3.111.49416 > 10.85.3.51.ssh: Flags [F.], seq 1, ack 22, win 197, options [nop,nop,TS val 2630895878 ecr 3035577013], length 0
11 22:45:15.649128 IP 10.85.3.51.ssh > 10.85.3.111.49416: Flags [.], ack 2, win 195, options [nop,nop,TS val 3035582639 ecr 2630895878], length 0
12 22:45:15.651394 IP 10.85.3.51.ssh > 10.85.3.111.49416: Flags [F.], seq 22, ack 2, win 195, options [nop,nop,TS val 3035582641 ecr 2630895878], length 0
13 22:45:15.651411 IP 10.85.3.111.49416 > 10.85.3.51.ssh: Flags [.], ack 23, win 197, options [nop,nop,TS val 2630895881 ecr 3035582641], length 0

在断开连接的TCP_PAWS_MSL时间内启动10.85.1.2通过NAT连接到server,server端抓包可以看到该连接的SYN报文的时间戳1759176699远小于保存的时间戳3035582641,此时server端丢弃接收到的所有SYN报文,客户端连接超时。

1 22:45:33.942378 IP 10.85.3.51.34264 > 10.85.3.111.19090: Flags [S], seq 668096838, win 25200, options [mss 1260,sackOK,TS val 1759176699 ecr 0,nop,wscale 7], length 0
2 22:45:34.942300 IP 10.85.3.51.34264 > 10.85.3.111.19090: Flags [S], seq 668096838, win 25200, options [mss 1260,sackOK,TS val 1759177700 ecr 0,nop,wscale 7], length 0
3 22:45:36.946320 IP 10.85.3.51.34264 > 10.85.3.111.19090: Flags [S], seq 668096838, win 25200, options [mss 1260,sackOK,TS val 1759179704 ecr 0,nop,wscale 7], length 0
  • 结合上述测试可以得出结论:同时启动tcp_timestamps和tcp_tw_recycle可能会导致客户端连接不上前提条件是server主动断开过与客户端的连接(可能是服务重启等原因),导致server处于TIME_WAIT状态的socket被快速回收,如果在TCP_PAWS_MSL时间内接收到客户端经NAT发过来的报文的时间戳小于前一个连接保存的时间戳,该报文会被认为是老链路残留的报文而丢弃。进而可以得出:
    1. 在NAT场景下一定不能启用tcp_tw_recycle;
    2. NAT场景下单独启动tcp_timestamps不会影响正常使用,连接断链后会在2MSL过后回收socket;
    3. 生产中不要使用tcp_tw_recycle,即使没有使用到NAT设备,但当前虚拟化环境下用到NAT的地方很多,如kubernetes的service等

TIPS

  • 为了复现如上问题,曾尝试过使用1.17.0版本的nginx作为NAT服务。但发现经过nginx的所有连接的SYN报文的时间戳都会被nginx修改,且后面连接SYN报文的时间戳一定大于前面连接的SYN报文中的时间戳,因此nginx下面不会出现客户端方式失败的场景

参考

10.85.3.111

原文地址:https://www.cnblogs.com/charlieroro/p/11593410.html

时间: 2024-10-13 23:35:36

linux开启tcp_timestamps和tcp_tw_recycle引发的问题研究的相关文章

tcp_timestamps,tcp_tw_reuse,tcp_tw_recycle,tcp_fin

解决这类问题,方法很重要,最好的做法其实是阅读官方的RFC,源码,然后进行实际测试验证. tcp_timestamps,tcp_tw_reuse,tcp_tw_recycle 几篇比较好的解释这三个参数的文章: https://serverfault.com/questions/502305/linux-networking-port-exhaustionhttp://perthcharles.github.io/2015/08/27/timestamp-intro/http://perthch

Linux开启mysql远程连接

Linux开启mysql远程连接的设置步骤 . mysql远程连接linuxserviceserveruserMysql默认root用户只能本地访问,不能远程连接管理mysql数据库,Linux如何开启mysql远程连接?设置步骤如下: 1.GRANT命令创建远程连接mysql授权用户test mysql -u root -p mysql>GRANT ALL PRIVILEGES ON *.* TO [email protected] IDENTIFIED BY 'test' WITH GRAN

Linux开启和关闭防火墙

一.即时生效,重启后失效: 1.启动:service iptables start 2.关闭:service iptables stop Linux开启和关闭防火墙,布布扣,bubuko.com

[原创]ios单元测试引发的一系列研究(一)

最近在研究单元测试,希望引入一个好用的单元测试工具,提高开发团队的产出质量. 不过我本人对单元测试没有什么知识,所以,笼统的从ios上的测试开始研究. 现在想想,本文的主题monkeytalk好像与单元测试木有关系.orz 目前可用的测试框架虽然没有其他开发平台的那样自成体系,也还算是百花齐放. ------------------------------------------------------- 老大自然是OCUnit+XCTool和GHUnit+OCMock.这两种都是需要自己写te

从Linux系统磁盘空间不足引发的Zabbix服务器数据库迁移

之前一直没有去关心Zabbbix服务器存储空间问题,最近Zabbix报警提示/根目录磁盘空间不足,于是登录Zabbix看了一下,发现根目录只有1.3MB了,这怎么办了?第一个想到的是扩展根目录,结果发现不是用的LVM,采用的是标准的,心想也还可以救,就和Windows一样将D盘.E盘删除,再将D盘的空间扩展给C盘就OK啊,于是开始着手操作(注意提前备份虚拟机,最好做一个快照,出问题了好恢复) [[email protected] /]# df -h 文件系统 容量 已用 可用 已用% 挂载点 /

linux开启慢查询

Linux查看mysql 安装路径 一.查看文件安装路径 由于软件安装的地方不止一个地方,所有先说查看文件安装的所有路径(地址). 这里以mysql为例.比如说我安装了mysql,但是不知道文件都安装在哪些地方.放在哪些文件夹里,可以用下面的命令查看所有的文件路径在终端输入: whereis mysql 回车,如果你安装好了mysql,就会显示文件安装的地址,例如我的显示(安装地址可能会不同) [[email protected] ~]# whereis mysql mysql: /usr/bi

linux开启内部路由转发功能

新增了一批机器,急需部署环境,在做完所有初始化后,发现安装一些软件,内部机器还是需要连接外网.对外公网就有两台,故选其中一台,开启路由转么功能 1.A这台机器  [[email protected] ~]# echo 1 > /proc/sys/net/ipv4/ip_forward 但一般不建议,下次开机它又会变为0 修改它的配置文件,把net.ipv4.ip_forward = 1 [[email protected] ~]# vim /etc/sysctl.conf # Kernel sy

Linux开启端口

一.Centos CentOS 7 默认没有使用iptables,所以无法通过编辑iptables的配置文件来开启端口. CentOS 7 采用了 firewalld 防火墙,首先使用如下命令开启firewalld. #systemctl start firewalld 假设我们要查询是否开启了21端口则: # firewall-cmd --query-port=21/tcp 默认情况下端口没有开启,下面命令可以开启21端口: # firewall-cmd --add-port=21/tcp 二

Linux 开启VNCserver

尽管我们可以使用 SSH连接远程通过字符界面来操作Linux,但是对于更多熟悉图形人来说是很不方便的,因此开启Linux的远程桌面还是很有必要的.目前有两种比较流 行的方式:XDM(X display manager)方案和VNC方案,而我个人比较倾向于VNC方案,一是因为VNC方案配置起来相对比较容易,二是VNC方案支持多种连接方式,比如通过 浏览器访问Linux桌面,免去需要安装客户端的麻烦. 接下来进入具体配置说明: 一, 确认及安装VNCSERVER. 1,首先确认你服务器是否配置了VN