七种可能 | Linux丢包故障的定位与解决

 出处:https://mp.weixin.qq.com/s?__biz=MzI1NzM3NTYxMw==&mid=2247483685&idx=1&sn=95c8bb46388dc0e40b8c48c508b34008&scene=1&srcid=0802bE2wuDrKD9zWy1zlsSJx#rdhtml

https://mp.weixin.qq.com/s?src=3&timestamp=1571033580&ver=1&signature=o3nEQrvNvSysQY*mEsaHtsDoGhjvUcM9vkrNbsqk1Wy9VbEIdC-a7yRBSPwz6EMlBUDfPNUMxlFesS7kSW29A4SeTqKjF0n22TBp9r8lCe44YxSjclmFDCx9*jOk1s8YKd0zbtmHffUfF3ccbxAHs5U5nth6tfXvGpS6-KY3I30=服务器

 


一 问题现象cookie

本次故障的反馈现象是:从办公网访问公网服务器不稳定,服务器某些端口访问常常超时,但Ping测试显示客户端与服务器的链路始终是稳定低延迟的网络

经过在服务器端抓包,咱们发现还有两个特色:app

  • 从办公网访问服务器有多个客户端,是同一个出口IP,有少部分是始终可以稳定链接的,另外一部分间歇访问超时或延迟很高tcp

  • 同一时刻的访问,不管哪一个客户端的数据包先到达,服务端会及时处理部分客户端的SYN请求,对另外一部分客户端的SYN包“视而不见”工具

  如何排查?怎样解决?测试

服务器能正常接收到数据包,问题能够限定在两种可能:优化

  • 部分客户端发出的数据包自己异常;.net

  • 服务器处理部分客户端的数触发了某种机制丢弃了数据包。

由于出问题的客户端可以正常访问公网上其余服务,后者的可能性更大。

那么,有哪些状况会致使Linux服务器丢弃数据包?

============================================ 可能缘由一 ========================================

可能缘由一:防火墙拦截

  服务器端口没法链接,一般就是查看防火墙配置了,虽然这里已经确认同一个出口IP的客户端有的可以正常访问,但也不排除配置了DROP特定端口范围的可能性。

  --->如何确认

  查看iptables Filter表,确认是否有相应规则会致使此丢包行为,容易排除防火墙拦截的可能性。

============================================可能缘由二========================================

可能缘由二:链接跟踪表溢出

除了防火墙自己配置DROP规则外,与防火墙有关的还有链接跟踪表nf_conntrack,Linux为每一个通过内核网络栈的数据包,生成一个新的链接记录项,当服务器处理的链接过多时,链接跟踪表被打满,服务器会丢弃新建链接的数据包。

如何确认

  • 经过dmesg能够确认是否有该状况发生:若是输出值中有“nf_conntrack: table full, dropping packet”,说明服务器nf_conntrack表已经被打满。

  • 经过conntrack工具或/proc文件系统查看nf_conntrack表实时状态:在本案例中,当前链接数远没有达到跟踪表最大值,所以排除这个因素。

如何解决

若是确认服务器因链接跟踪表溢出而开始丢包,首先须要查看具体链接判断是否正遭受DOS攻击,若是是正常的业务流量形成,则能够考虑调整nf_conntrack的参数:

  • nf_conntrack_max决定链接跟踪表的大小,默认值是65535,能够根据系统内存大小计算一个合理值:CONNTRACK_MAX = RAMSIZE(in bytes)/16384/(ARCH/32),如32G内存能够设置1048576

  • nf_conntrack_buckets决定存储conntrack条目的哈希表大小,默认值是nf_conntrack_max的1/4,延续这种计算方式:BUCKETS = CONNTRACK_MAX/4,如32G内存能够设置262144

  • nf_conntrack_tcp_timeout_established决定ESTABLISHED状态链接的超时时间,默认值是5天,能够缩短到1小时,即3600

============================================可能缘由三========================================

可能缘由三:Ring Buffer溢出

排除了防火墙的因素,咱们从底向上来看Linux接收数据包的处理过程,首先是网卡驱动层。

以下图所示,物理介质上的数据帧到达后首先由NIC(网络适配器)读取,写入设备内部缓冲区Ring Buffer中,再由中断处理程序触发Softirq从中消费,Ring Buffer的大小因网卡设备而异。当网络数据包到达(生产)的速率快于内核处理(消费)的速率时,Ring Buffer很快会被填满,新来的数据包将被丢弃。

  

 

如何确认

经过ethtool -S指令或查看/proc/net/dev能够获得因Ring Buffer满而丢弃的包统计,在统计项中以fifo标识:本案例中服务器的接收方向的fifo丢包数并无增长,天然也排除这个缘由。

如何解决

若是发现服务器上某个网卡的fifo数持续增大,能够去确认CPU中断是否分配均匀,也能够尝试增长Ring Buffer的大小,经过ethtool -g eth0能够查看网卡设备Ring Buffer最大值,ethtool -G修改Ring Buffer当前设置。

============================================可能缘由四========================================

可能缘由四:netdev_max_backlog溢出

netdev_max_backlog是内核从NIC收到包后,交由协议栈(如IP、TCP)处理以前的缓冲队列。每一个CPU核都有一个backlog队列,与Ring Buffer同理,当接收包的速率大于内核协议栈处理的速率时,CPU的backlog队列不断增加,当达到设定的netdev_max_backlog值时,数据包将被丢弃。

如何确认

经过查看/proc/net/softnet_stat能够肯定是否发生了netdev backlog队列溢出。其中:

  • 每一行表明每一个CPU核的状态统计,从CPU0依次往下

  • 每一列表明一个CPU核的各项统计:第一列表明中断处理程序收到的包总数;第二列即表明因为netdev_max_backlog队列溢出而被丢弃的包总数

在本案例的服务器统计中,并无由于netdev_max_backlog致使的丢包。

如何解决

netdev_max_backlog的默认值是1000,在高速链路上,可能会出现上述第二列统计不为0的状况,能够适当调大内核参数net.core.netdev_max_backlog到2000来解决。

============================================可能缘由五========================================

可能缘由五:反向路由过滤

反向路由过滤机制是Linux经过反向路由查询,检查收到的数据包源IP是否可路由(Loose mode)、是否最佳路由(Strict mode),若是没有经过验证,则丢弃数据包,设计的目的是防范IP地址欺骗攻击。rp_filter提供了三种模式供配置:

  • 0 - 不验证

  • 1 - RFC3704定义的严格模式:对每一个收到的数据包,查询反向路由,若是数据包入口和反向路由出口不一致,则不经过

  • 2 - RFC3704定义的松散模式:对每一个收到的数据包,查询反向路由,若是任何接口都不可达,则不经过

  • 查看和配置:sysctl -a | grep rp_filter 
  • 关于rp_filter :https://www.cnblogs.com/lipengxiang2009/p/7446388.html

如何确认

查看当前rp_filter策略配置,若是设置为1,就须要查看主机的网络环境和路由策略是否可能会致使客户端的入包没法经过反向路由验证。

从原理来看这个机制工做在网络层,所以,若是客户端可以Ping通服务器,就可以排除这个因素了

解决办法:
1.修改路由表,使响应数据包从eth1出,即保证请求数据包进的网卡和响应数据包出的网卡为同一个网卡。
2.关闭rp_filter参数。(注意all和default的参数都要改)
1)修改/etc/sysctl.conf文件,而后sysctl -p刷新到内存。
2)使用sysctl -w直接写入内存:sysctl -w net.ipv4.conf.all.rp_filter=0
3)修改/proc文件系统: echo "0">/proc/sys/net/ipv4/conf/all/rp_filter

如何解决

根据实际网络环境将rp_filter设置为0或2。

============================================可能缘由六========================================

可能缘由六:半链接队列溢出

半链接队列指的是TCP传输中服务器收到SYN包但还未完成三次握手的链接队列,队列大小由内核参数tcp_max_syn_backlog定义。

当服务器保持的半链接数量达到tcp_max_syn_backlog后,内核将会丢弃新来的SYN包

如何确认

经过dmesg能够确认是否有该状况发生:若是输出值中有“TCP: drop open request from”,说明半链接队列已被打满。

半链接队列的链接数量能够经过netstat统计SYN_RECV状态的链接得知。大多数状况下这个值应该是0或很小,由于半链接状态从第一次握手完成时进入,第三次握手完成后退出,正常的网络环境中这个过程发生很快,若是这个值较大,服务器极有可能受到了SYN Flood攻击。

如何解决

tcp_max_syn_backlog的默认值是256,一般推荐内存大于128MB的服务器能够将该值调高至1024,内存小于32MB的服务器调低到128,一样,该参数经过sysctl修改。

另外,上述行为受到内核参数tcp_syncookies的影响,若启用syncookie机制,当半链接队列溢出时,并不会直接丢弃SYN包,而是回复带有syncookie的SYC+ACK包,设计的目的是防范SYN Flood形成正常请求服务不可用。

============================================可能缘由七========================================

可能缘由七:PAWS :内核参数/proc/sys/net/ipv4/tcp_tw_recycle 控制

PAWS全名Protect Againest Wrapped Sequence numbers,目的是解决在高带宽下,TCP序列号在一次会话中可能被重复使用而带来的问题。

 

 

如上图所示,客户端发送的序列号为A的数据包A1因某些缘由在网络中“迷路”,在必定时间没有到达服务端,客户端等待ACK超时后重发序列号为A的数据包A2,接下来的通讯用尽了序列号空间,从新回到A。此时,服务端等待的是序列号为A的数据包A3,而恰巧此时前面“迷路”的A1到达服务端,若是服务端仅靠序列号A就判断数据包合法,就会将错误的数据传递到用户态程序,形成程序异常。

PAWS要解决的就是上述问题,它依赖于timestamp机制,理论依据是:在一条正常的TCP流中,按序接收到的全部TCP数据包中的timestamp都应该是单调非递减的,这样就能判断那些timestamp小于当前TCP流已处理的最大timestamp值的报文是延迟到达的重复报文,能够予以丢弃。在上文的例子中,服务器已经处理数据包Z,然后到来的A1包的timestamp必然小于Z包的timestamp,所以服务端会丢弃迟到的A1包,等待正确的报文到来。

PAWS机制的实现关键是内核保存了Per-Connection的最近接收时间戳,若是加以改进,就能够用来优化服务器TIME_WAIT状态的快速回收。

TIME_WAIT状态是TCP四次挥手中主动关闭链接的一方须要进入的最后一个状态,而且一般须要在该状态保持2*MSL(报文最大生存时间),它存在的意义有两个:

  • 可靠地实现TCP全双工链接的关闭:关闭链接的四次挥手过程当中,最终的ACK由主动关闭链接的一方(称为A)发出,若是这个ACK丢失,对端(称为B)将重发FIN,若是A不维持链接的TIME_WAIT状态,而是直接进入CLOSED,则没法重传ACK,B端的链接所以不能及时可靠释放。

  • 等待“迷路”的重复数据包在网络中因生存时间到期消失:通讯双方A与B,A的数据包因“迷路”没有及时到达B,A会重发数据包,当A与B完成传输并断开链接后,若是A不维持TIME_WAIT状态2*MSL时间,便有可能与B再次创建相同源端口和目的端口的“新链接”,而前一次链接中“迷路”的报文有可能在这时到达,并被B接收处理,形成异常,维持2*MSL的目的就是等待前一次链接的数据包在网络中消失。

TIME_WAIT状态的链接须要占用服务器内存资源维持,Linux内核提供了一个参数来控制TIME_WAIT状态的快速回收:tcp_tw_recycle,它的理论依据是:

在PAWS的理论基础上,若是内核保存Per-Host的最近接收时间戳,接收数据包时进行时间戳比对,就能避免TIME_WAIT意图解决的第二个问题:前一个链接的数据包在新链接中被当作有效数据包处理的状况。这样就没有必要维持TIME_WAIT状态2*MSL的时间来等待数据包消失,仅须要等待足够的RTO(超时重传),解决ACK丢失须要重传的状况,来达到快速回收TIME_WAIT状态链接的目的。

但上述理论在多个客户端使用NAT访问服务器时会产生新的问题:同一个NAT背后的多个客户端时间戳是很难保持一致的(timestamp机制使用的是系统启动相对时间),对于服务器来讲,两台客户端主机各自创建的TCP链接表现为同一个对端IP的两个链接,按照Per-Host记录的最近接收时间戳会更新为两台客户端主机中时间戳较大的那个,而时间戳相对较小的客户端发出的全部数据包对服务器来讲都是这台主机已过时的重复数据,所以会直接丢弃。

如何确认

经过netstat -s能够获得因PAWS机制timestamp验证被丢弃的数据包统计,其中的“passive connections rejected because of time stamp”和“packets rejects in established connections because of timestamp”分别表明了因时间戳验证失败而拒绝的链接数统计和已有链接中拒绝的包数量统计。

经过sysctl查看是否启用了tcp_tw_recycle和tcp_timestamp,若是这两个选项同时开启,则有可能会致使上述现象。咱们此次的问题正是由于服务器作了这个配置,而客户端正是使用NAT来访问服务器,形成启动时间相对较短的客户端得不到服务器的正常响应。

如何解决

若是服务器做为服务端提供服务,且明确客户端会经过NAT网络访问,或服务器以前有7层转发设备会替换客户端源IP时,是不该该开启tcp_tw_recycle的,而timestamps除了支持tcp_tw_recycle外还被其余机制依赖,推荐继续开启。

操做:

  临时设置生效:echo 0 > /proc/sys/net/ipv4/tcp_tw_recycle

  永久生效修改配置文件:echo ‘net.ipv4.tcp_tw_recycle = 0’  >>  /etc/sysctl.conf  ; sysctl -p 

相关文章
相关标签/搜索