日常问题1::timestamp与NAT的千丝万缕

问题描述

公有云上面部署的服务,同时开启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的支持。
除了上述问题以外,还需要再思考几个问题

  1. 当服务端某个连接处于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.
     */

你可能感兴趣的:(日常问题1::timestamp与NAT的千丝万缕)