Linux服务器收到SYN请求包没有回应ACK导致客户端无法建立TCP连接
通过netstat命令查看系统上协议统计信息,发现很多请求由于时间戳的问题被rejected
通过百度来协助发现有同样的人遇见这个问题:
是通过调整sysctl -w net.ipv4.tcp_tw_recycle=0来解决这个问题
发现是 Linux tcp_tw_recycle/tcp_timestamps设置导致的问题。 因为在linux kernel源码中发现tcp_tw_recycle/tcp_timestamps都开启的条件下,60s内同一源ip主机的socket connect请求中的timestamp必须是递增的。
我这边和其它同事通过公司出口(NAT网关只有1个ip地址)访问问题server,由于timestamp时间为系统启动到当前的时间,故我和其它同事的timestamp肯定不相同;根据上述SYN包处理源码,在tcp_tw_recycle和tcp_timestamps同时开启的条件下,timestamp大的主机访问serverN成功,而timestmap小的主机访问失败。
解决:
# echo "0" > /proc/sys/net/ipv4/tcp_tw_recycle
1、net.ipv4.tcp_timestamps
tcp_timestamps的本质是记录数据包的发送时间。基本的步骤如下:
发送方在发送数据时,将一个timestamp(表示发送时间)放在包里面
接收方在收到数据包后,在对应的ACK包中将收到的timestamp返回给发送方(echo back)
发送发收到ACK包后,用当前时刻now - ACK包中的timestamp就能得到准确的RTT
timestamps一个双向的选项,当一方不开启时,两方都将停用timestamps。比如client端发送的SYN包中带有timestamp选项,但server端并没有开启该选项。则回复的SYN-ACK将不带timestamp选项,同时client后续回复的ACK也不会带有timestamp选项。当然,如果client发送的SYN包中就不带timestamp,双向都将停用timestamp。
tcp数据包中timestamps的value是系统开机时间到现在时间的(毫秒级)时间戳。
参数:
0:停用
1:启用(系统默认值)
2、net.ipv4.tcp_tw_recycle
TCP规范中规定的处于TIME_WAIT的TCP连接必须等待2MSL时间。但在linux中,如果开启了tcp_tw_recycle,TIME_WAIT的TCP连接就不会等待2MSL时间(而是rto或者60s),从而达到快速重用(回收)处于TIME_WAIT状态的tcp连接的目的。这就可能导致连接收到之前连接的数据。为此,linux在打开tcp_tw_recycle的情况下,会记录下TIME_WAIT连接的对端(peer)信息,包括IP地址、时间戳等。这样,当内核收到同一个IP的SYN包时,就会去比较时间戳,检查SYN包的时间戳是否滞后,如果滞后,就将其丢掉(认为是旧连接的数据)。这在绝大部分情况下是没有问题的,但是对于我们实际的client-server的服务,访问我们服务的用户一般都位于NAT之后,如果NAT之后有多个用户访问同一个服务,就有可能因为时间戳滞后的连接被丢掉。
参数:
0:停用(系统默认值)
1:启用
默认值:
使用默认值就ok
查看内核参数
方法一:通过“/proc/sys”目录,使用cat命令查看对应文件的内容。“/proc/sys/”目录是Linux内核启动后生成的伪目录,其目录下的net文件夹中存放了当前系统中生效的所有内核参数、目录树结构与参数的完整名称相关,如net.ipv4.tcp_tw_recycle,它对应的文件是 /proc/sys/net/ipv4/tcp_tw_recycle,文件的内容就是参数值。cat /proc/sys/net/ipv4/tcp_tw_recycle
方法二:通过“/etc/sysctl.conf”文件进行查看。执行以下命令,查看当前系统中生效的所有参数。/usr/sbin/sysctl -a
修改内核参数
方法一:通过“/proc/sys”目录,使用echo命令修改内核参数对应的文件。该方法修改的参数值仅在当次运行中生效,重启后会重置为原参数值,一般用于临时性验证。如需永久生效,请参考方法二。echo "0" > /proc/sys/net/ipv4/tcp_tw_recycle。
方法二:通过“/etc/sysctl.conf”文件进行修改。该方法修改的参数值,永久生效。(建议第二种)