问题描述
公有云上面部署的服务,同时开启tcp_timestamps和tcp_tw_recycle选项时,出现了大量的syn报文。经过抓包分析,发现client端的syn报文请求没有被响应,client端又触发重传,导致大量的syn报文涌入。
问题测试
尝试修改tcp_timestamps = 0,tcp_tw_recycle = 1,该问题依然存在。
尝试修改tcp_timestamps = 1,tcp_tw_recycle = 1,该问题依然存在。
尝试修改tcp_timestamps = 0,tcp_tw_recycle = 0,该问题修复。
由此确认,tcp_tw_recycle是由引起的问题。
TCP行为分析
tcp_timestamp 是 RFC1323 定义的优化选项,主要用于 TCP 连接中 RTT(Round Trip Time) 的计算,如果没有开启tcp_timestamp,rtt的计算智能依赖于本端的发送和接收时记录的时间,不利于RTT精度的计算。而RTT不准,就会引起拥塞算法的表现下降。
ps:tcp_timestamps详情请见:https://tools.ietf.org/pdf/rfc7323.pdf
为了保证传输的安全性,内核协议栈实现了PAWS,该机制要求所有来个同一个ip+port的TCP包必须具备单调递增的timestamp。当收到一个timestamp值,小于服务端记录的该五元组最后发送的timestamp值时,则会认为这是一个非法数据数据包。
tcp_tw_recycle用于启用tcp time_wait的快速回收。
注意:这个与tcp_tw_reuse,允许重用处于time wait的连接(四元组)不同。tcp_tw_recycle,是在开启tcp_timestamps时更侵略性的回收TCP连接。
问题就处在了这个参数在NAT场景下的使用tcp_tw_recycle。由于NAT必然会导致大量的源ip+port的重复,让服务端以为还是以前的某一流,但是timetstamp却是源自client的真实数据,出现了不一致性,导致内核判断错误。
linux代码的实现
3.10.x版本的实现
if (tmp_opt.saw_tstamp &&
tcp_death_row.sysctl_tw_recycle && //这里需要同时开启tcp_timestamps和tw_recycle
(dst = inet_csk_route_req(sk, &fl4, req)) != NULL &&
fl4.daddr == saddr) {
if (!tcp_peer_is_proven(req, dst, true)) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
goto drop_and_release;
}
}
在后续的版本打入了下述
patchhttps://lists.openwall.net/netdev/2014/08/12/2
- if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) {
+ if (tcp_death_row.sysctl_tw_recycle) {//只要开启了tw_recycle,就会进入。其他流程不变
再往后,内核直接干掉了recycle的配置项,patch如下
SHA-1: 4396e46187ca5070219b81773c4e65088dac50cc
* tcp: remove tcp_tw_recycle
The tcp_tw_recycle was already broken for connections
behind NAT, since the per-destination timestamp is not
monotonically increasing for multiple machines behind
a single destination address.
After the randomization of TCP timestamp offsets
in commit 8a5bd45f6616 (tcp: randomize tcp timestamp offsets
for each connection), the tcp_tw_recycle is broken for all
types of connections for the same reason: the timestamps
received from a single machine is not monotonically increasing,
anymore.
Remove tcp_tw_recycle, since it is not functional. Also, remove
the PAWSPassive SNMP counter since it is only used for
tcp_tw_recycle, and simplify tcp_v4_route_req and tcp_v6_route_req
since the strict argument is only set when tcp_tw_recycle is
enabled.
上述patch提到了4.10版本引入的 random offset timestamps特性,后面的blog还会再研究这个特性。
综上所述,在云场景的催生下,内核协议栈已经放弃了对tcp_tw_recycle的支持。
除了上述问题以外,还需要再思考几个问题
- 当服务端某个连接处于TIME_WAIT状态时,收到了来自同源IP+源port的SYN报文,内核的流程是什么?
这个答案在标准里是有定义的,内核协议也有comment描述
/*
* Now real TIME-WAIT state.
*
* RFC 1122:
* "When a connection is [...] on TIME-WAIT state [...]
* [a TCP] MAY accept a new SYN from the remote TCP to
* reopen the connection directly, if it:
*
* (1) assigns its initial sequence number for the new
* connection to be larger than the largest sequence
* number it used on the previous connection incarnation,
* and
*
* (2) returns to TIME-WAIT state if the SYN turns out
* to be an old duplicate".
*/
2.当TIME_WAIT状态的连接数,超过tcp_tw_buckets的值时,内核的流程是什么?
在tcp_time_wait的处理函数里,有这么一段comment
/* Sorry, if we're out of memory, just CLOSE this
* socket up. We've got bigger problems than
* non-graceful socket closings.
*/