请慎重使用tcp_tw_recycle毒药

  • 2019 年 12 月 26 日
  • 笔记

上图这个服务器“优化”是不是似曾相识,网上有太多太多这样的文章,核心调优方案就是开启 tcp_timestamps 和 tcp_tw_recycle。诚然这个“调优“,确实如毒品一般让业务瞬间得到虚幻一般的高潮,但是他的危害也如毒品一般让业务一步步陷入深渊。

然后突然某一天,你发现业务出现端口间歇性出现不通。如幽灵一般出现,又很快消失得无影无踪,让人百思不得其解。

WHY?

如linux-3.16.80 中已经给出警示⚠️

 542 tcp_tw_recycle - BOOLEAN   543         Enable fast recycling TIME-WAIT sockets. Default value is 0.   544         It should not be changed without advice/request of technical   545         experts.

It should not be changed without advice/request of technical experts. 翻译一下除非得到技术专家的建议或要求﹐请不要随意修改这个值

说得更直白点,就是除非你知道你在做什么,否则不要乱动。

为什么会这么劝告呢?

Talk is cheap. Show me the code.

//include/net/tcp.h:  #define TCP_PAWS_MSL	60  #define TCP_PAWS_WINDOW 1    /*------------------------------------  // net/ipv4/tcp_metrics.c  */  bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check)  {      struct tcp_metrics_block *tm;      bool ret;        if (!dst)          return false;        rcu_read_lock();      tm = __tcp_get_metrics_req(req, dst);      if (paws_check) {          if (tm &&              (u32)get_seconds() - tm->tcpm_ts_stamp < TCP_PAWS_MSL &&              (s32)(tm->tcpm_ts - req->ts_recent) > TCP_PAWS_WINDOW)              ret = false;          else              ret = true;      } else {          if (tm && tcp_metric_get(tm, TCP_METRIC_RTT) && tm->tcpm_ts_stamp)              ret = true;          else              ret = false;      }      rcu_read_unlock();        return ret;  }    /*------------------------------------  // net/ipv4/tcp_ipv4.c  */  int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)  {       ……          /* VJ's idea. We save last timestamp seen           * from the destination in peer table, when entering           * state TIME-WAIT, and check against it before           * accepting new connection request.           *           * If "isn" is not zero, this request hit alive           * timewait bucket, so that all the necessary checks           * are made in the function processing timewait state.           */          if (tmp_opt.saw_tstamp &&              tcp_death_row.sysctl_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;              }          }        ……  }

tcp_v4_conn_request函数是tcp层三次握手SYN包的处理函数(服务端)

tmp_opt.saw_tstamp:socket 开启了 tcp_timestamp

tcp_death_row.sysctl_tw_recycle:系统内核开启了 tcp_tw_recycle

在系统打开sysctl_tw_recycle后,如果来自同一个源IP(Source IP)的请求,在60s内出现timestamp未递增的包,报文(比如SYN)将丢弃,导致无法建立连接。

所以,如果Client有可能处于NAT环境的情况下,服务器一定不要开启sysctl_tw_recycle。另外,必须开启tcp_timestamp后开启sysctl_tw_recycle才能生效。所以tcp_timestamp=0,sysctl_tw_recycle=1的骚操作也别想了。

PS:Linux 从4.12内核版本开始移除了 tcp_tw_recycle 配置