WALL-E / tcp-ip-labs

tcp ip labs
2 stars 1 forks source link

TIME_WAIT和2MLS #7

Open WALL-E opened 7 years ago

WALL-E commented 7 years ago

MSL

MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为tcp报文(segment)是ip数据报(datagram)的数据部分,具体称谓请参见《数据在网络各层中的称呼》一文,而ip头中有一个TTL域,TTL是time to live的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个ip数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。

实际上,对于当前的网络环境,这个值已经变得没有意义了,一个数据包不可能在网络中存活2分钟(120s),这个时间内,数据包几乎可以从北京到旧金山跑120个来回。

另外,IP层使用了另一个变量来表示数据包的存活时间,就是TTL,这个值和时间无关,而是发送端初始化,然后每经过一个路由器就减1,当TTL为零的时候,路由器丢弃这个数据包。

现在为什么大家还用这个名词来表示时长,仅仅是习惯了而已。

结论:2MLS只是一个时间长度,只是一个名词,已经没有实际意义啦。

猜想: ip协议和tcp协议设计之初的目的是要在不同的协议层来独立控制数据包的存活时间,显然,TCP层的MSL没有被广泛应用,至于为什么和TCP状态TIME_WAIT纠缠不清,这只是一个副作用。

那为什么是2MSL,而不是3MSL或者是4MSL,这个原因用中国话来讲,和有再一再二,没有再三再四,是一个意思。

RFC 793 requires the TIME-WAIT state to last twice the time of the MSL. On Linux, this duration is not tunable and is defined in include/net/tcp.h as one minute:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
* state, about 60 seconds     */

请忘记2MSL吧, 记住1分钟就可以啦!

WALL-E commented 7 years ago

TIME-WAIT

为什么TCP状态迁移需要TIME-WAIT这个状态

坏处

相关参数

不像Windows 可以修改注册表修改2MSL 的值,linux 是没有办法修改MSL的,tcp_fin_timeout 不是2MSL 而是Fin-WAIT-2状态。

WALL-E commented 7 years ago

TIME-WAIT内存占用

[appuser@bjsh19-33-237 ~]$ ss -s
Total: 168 (kernel 249)
TCP:   7625 (estab 86, closed 7526, orphaned 0, synrecv 0, timewait 7526/0), ports 312

slabtop
  8925   7515  84%    0.25K    595   15  2380K tw_sock_TCP

每个TIME-WAIT大概占用0.3k字节。1M个TIME-WAIT,也就占用300M内存而已。

WALL-E commented 7 years ago

ref: http://www.cnblogs.com/lulu/p/4149312.html

WALL-E commented 7 years ago

内网状况比tw_reuse 稍快,公网尤其移动网络大多要比tw_reuse 慢,优点就是能够回收服务端TIME_WAIT数量。

对内网服务器而言(除LVS和防火墙),开启这两个参数是安全的。

WALL-E commented 7 years ago

TCP传输路径中存在NAT设备

客户端A  <==> [NAT设备B] === [NAT设备C] <==> 服务端D

客户端A和服务端D开启tw_reuse 和 tw_recycle 是有风险的,服务端D上风险相对会更大一些,因为他的连接数要大的多。

这种情况下,尽管tcp_timestamp已经开启,迷途的TCP分节还是有可能会插入到正常的TCP连接中,导致TCP传输发生异常,本质的原因是多个客户端的系统时间不一致导致的。假如,所有客户端的时间是完全一致的话,TCP传输依然还是很安全。对服务端来说,原理基本一样。

WALL-E commented 7 years ago

结论

总的来说,在高负载的服务器上出现大量的TIME-WAIT,十几万或是更多,这并不是一个问题,或者说并不一定需要优化。换句话说,即使是调整了内核参数,减小了TIME-WAIT的数量,对服务器的性能来说,改变是微不足道的。

WALL-E commented 7 years ago

用反证法来证明:假如TIME-WAIT有问题的话,或者在多数情况下都需要优化的话,为什么linux内核的那帮黑客不解决呢,TCP协议可是已经出现了30多年了。

WALL-E commented 7 years ago

内核源码分析

核心数据结构 @include/net/inet_timewait_sock.h

struct inet_timewait_death_row {
        /* Short-time timewait calendar */
        int                     twcal_hand;
        unsigned long           twcal_jiffie;
        struct timer_list       twcal_timer;
        struct hlist_head       twcal_row[INET_TWDR_RECYCLE_SLOTS];

        spinlock_t              death_lock;
        int                     tw_count;
        int                     period;
        u32                     thread_slots;
        struct work_struct      twkill_work;
        struct timer_list       tw_timer;
        int                     slot;
        struct hlist_head       cells[INET_TWDR_TWKILL_SLOTS];
        struct inet_hashinfo    *hashinfo;
        int                     sysctl_tw_recycle;
        int                     sysctl_max_tw_buckets;
};
WALL-E commented 7 years ago

TCP: time wait bucket table overflow

void dccp_time_wait(struct sock *sk, int state, int timeo)
{
        struct inet_timewait_sock *tw = NULL;

        if (dccp_death_row.tw_count < dccp_death_row.sysctl_max_tw_buckets)
                tw = inet_twsk_alloc(sk, state);

        if (tw != NULL) {
                /*......*/
        } else {
                /* Sorry, if we're out of memory, just CLOSE this
                 * socket up.  We've got bigger problems than
                 * non-graceful socket closings.
                 */
                DCCP_WARN("time wait bucket table overflow\n");
        }

        dccp_done(sk);
}

从代码的逻辑上看,不管是timewait超出限制,还是内存用尽,系统都会打印错误日志。

WALL-E commented 7 years ago

关于tcp_tw_recycle和tcp_tw_reuse的源码逻辑比较复杂,暂且不读。

WALL-E commented 7 years ago

测量timewait持续时间的脚本 https://github.com/WALL-E/tcp-ip-labs/blob/master/timewait/t.sh

WALL-E commented 7 years ago

include/net/inet_hashtables.h sourcecode