调整 net.ipv4.tcp_tw_recycle 造成的故障

  • 背景

在 nat 的网络环境访问服务器时会有不定时连接超时的问题

  • 原因

出现此问题,可能是系统参数设置问题,即以下两个参数都设置为 1

1
2
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_recycle = 1

RFC1323中有如下一段描述:

An additional mechanism could be added to the TCP, a per-host cache of the last timestamp received from any connection. This value could then be used in the PAWS mechanism to reject old duplicate segments from earlier incarnations of the connection, if the timestamp clock can be guaranteed to have ticked at least once since the old connection was open. This would require that the TIME-WAIT delay plus the RTT together must be at least one tick of the sender’s timestamp clock. Such an extension is not part of the proposal of this RFC.

大概意思是说TCP有一种行为,可以缓存每个主机最新的时间戳,后续请求中如果时间戳小于缓存的时间戳,即视为无效,相应的数据包会被丢弃。

Linux是否启用这种行为取决于tcp_timestamps和tcp_tw_recycle,因为tcp_timestamps缺省就是开启的,所以当tcp_tw_recycle被开启后,实际上这种行为就被激活了,当客户端或服务端以NAT方式构建的时候就可能出现问题,下面以客户端NAT为例来说明:

当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。如果发生了此类问题,具体的表现通常是是客户端明明发送的SYN,但服务端就是不响应ACK,我们可以通过下面命令来确认数据包不断被丢弃的现象:

1
2
shell> netstat -s | grep timestamp
... packets rejects in established connections because of timestamp

安全起见,通常要禁止tcp_tw_recycle。说到这里,大家可能会想到另一种解决方案:把tcp_timestamps设置为0,tcp_tw_recycle设置为1,这样不就可以鱼与熊掌兼得了么?可惜一旦关闭了tcp_timestamps,那么即便打开了tcp_tw_recycle,也没有效果。

引用地址: https://huoding.com/2012/01/19/142

  • 解决办法

    解决的方法就是将这两个参数修改为:

    1
    2
    net.ipv4.tcp_timestamps = 1
    net.ipv4.tcp_tw_recycle = 0
  • 遇到的问题

    本来修改完成后再次请求就没有问题了,但如果是 docker 服务器的话,会发现容器中无法连接网络!

    因为修改 net.ipv4.tcp_tw_recycle 影响的是 nat 相关,所以如果容器是以 nat 方式上网的话,就会造成影响

  • 解决办法

    解决以上问题,需要先停掉 docker 主进程,然后修改参数,再启动

-