TCP TIME_WAIT状态与tcp_tw_recycle

#tcp #linux

最近遇到了一个与linux内核选项tcp_tw_recycle的坑, 在此记录一下相关知识点。

TIME_WAIT状态的作用

如图,TIME_WAIT状态主要用来处理数据的错误传输,比如一个连接,在断开后,又新建了相同的连接(IP和端口的四元组),上一次断开的连接中某些数据可能由于网络拥塞而延迟到来,显然延迟的数据不是属于这一个新建的连接的,为了防止这种情况,TIME_WAIT状态会等待2MSL,即两倍最大报文生存时间。

time_wait

如图的黄色箭头所代表的即为不正确的数据。

tcp_tw_recycle

当在连接量较大的服务器上时,如果大量的TIME_WAIT状态会影响服务器的性能,因此,RFC6191提出使用TCP Timestamp来减少这样的TIME_WAIT状态。

在内核中的代码实现如下:

536 bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check)
537 {
538         struct tcp_metrics_block *tm;
539         bool ret;
540
541         if (!dst)
542                 return false;
543
544         rcu_read_lock();
545         tm = __tcp_get_metrics_req(req, dst);
546         if (paws_check) {
547                 if (tm &&
548                     (u32)get_seconds() - tm->tcpm_ts_stamp < TCP_PAWS_MSL &&
549                     (s32)(tm->tcpm_ts - req->ts_recent) > TCP_PAWS_WINDOW)
550                         ret = false;
551                 else
552                         ret = true;
553         } else {
554                 if (tm && tcp_metric_get(tm, TCP_METRIC_RTT) && tm->tcpm_ts_stamp)
555                         ret = true;
556                 else
557                         ret = false;
558         }
559         rcu_read_unlock();
560
561         return ret;
562 }
563 EXPORT_SYMBOL_GPL(tcp_peer_is_proven);

在代码中,547-552行主要确保在TCP_PAWS_MSL时间内请求的时间戳是递增的(TCP_PAWS_WINDOW为1)。

ipatbles NAT

所以当后端机器在NAT后面时,由于NAT并没有改时间戳,连接中的时间戳来自不同的机器,时间戳(机器启动时间)不一定是递增的,所以就会出现后端机器drop SYN包的情况.

在NAT后的机器,关闭tcp_tw_recycle检查即可,一般该选项为disable的。

参考材料

  1. http://lxr.free-electrons.com/source/net/ipv4/tcp_metrics.c?v=3.10#L536
  2. https://tools.ietf.org/html/rfc6191#page-3
  3. http://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/
  4. https://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html#summary
  5. https://www.ietf.org/rfc/rfc1323.txt