网络丢包故障定位全景指南

liaocj 2022-08-29 14:36:15
Categories: > > Tags:

引言

本期分享一个比较常见的⽹络问题–丢包。例如我们去ping⼀个⽹站,如果能ping通,且⽹站返回信息全⾯,则说明与⽹站服务器的通信是畅通的,如果ping不通,或者⽹站返回的信息不全等,则很可能是数据被丢包了,类似情况想必⼤家都不陌⽣。针对⽹络丢包,本⽂提供⼀些常见的丢包故障定位⽅法,希望能够帮助⼤家对⽹络丢包有更多的认识,遇到丢包莫要慌,且跟着⼀起来涨姿(知)势(识)···

数据包接收、发送原理


##发送数据包:

  1. 应⽤程序的数据包,在TCP层增加TCP报⽂头,形成可传输的数据包。
  2. 在IP层增加IP报头,形成IP报⽂。
  3. 经过数据⽹卡驱动程序将IP包再添加14字节的MAC头,构成frame(暂⽆CRC),frame(暂⽆CRC)中含有发送端和接收端的MAC地址。
  4. 驱动程序将frame(暂⽆CRC)拷贝到⽹卡的缓冲区,由⽹卡处理。
  5. ⽹卡为frame(暂⽆CRC)添加头部同步信息和CRC校验,将其封装为可以发送的packet,然后再发送到⽹线上,这样说就完成了⼀个IP报⽂的发送了,所有连接到这个⽹线上的⽹卡都可以看到该packet。

接收数据包:

  1. ⽹卡收到⽹线上的packet,⾸先检查packet的CRC校验,保证完整性,然后将packet头去掉,得到frame。(⽹卡会检查MAC包内的⽬的MAC地址是否和本⽹卡的MAC地址⼀样,不⼀样则会丢弃。)
  2. ⽹卡将frame拷贝到预分配的ring buffer缓冲。
  3. ⽹卡驱动程序通知内核处理,经过TCP/IP协议栈层层解码处理。
  4. 应⽤程序从socket buffer 中读取数据。

核心思路:

了解了收发包的原理,可以了解到丢包原因主要会涉及⽹卡设备、⽹卡驱动、内核协议栈三⼤类。以下我们将遵循“从下到上分层分析(各层可能性出现的丢包场景),然后查看关键信息,最终得出分析结果”的原则展开介绍。

目录–网络丢包情形概览

硬件网卡丢包
网卡驱动丢包
以太网链路层丢包
网络IP层丢包
传输层UDP/TCP丢包
应用层socket丢包

硬件网卡丢包

Ring Buffer溢出:


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

查看:

通过ethtool或/proc/net/dev可以查看因Ring Buffer满而丢弃的包统计,在统计项中以fifo标识:


$ ethtool -S eth0|grep rx_fifo
rx_fifo_errors: 0
$ cat /proc/net/dev
Inter-|Receive | Transmitface |bytes packets errs drop fifo frame compressed
multicast|bytes packets errs drop fifo colls carrier compressed
eth0: 17253386680731 42839525880 0 0 0 0 0 244182022 14879545018057 41657801805 0 0 0 0 0 0

查看eth0网卡Ring Buffer最大值和当前设置

$ ethtool -g eth0

解决方案:修改网卡eth0接收与发送硬件缓存区大小

$ ethtool -G eth0 rx 4096 tx 4096

网卡端口协商丢包

  1. 查看网卡丢包统计:ethtool -S eth1/eth0
  2. 查看网卡配置状态:ethtool eth1/eth0

    主要查看网卡和上游网络设备协商速率和模式是否符合预期;

解决方案:

  1. 重新自协商: ethtool -r eth1/eth0;
  2. 如果上游不支持自协商,可以强制设置端口速率:
ethtool -s eth1 speed 1000 duplex full autoneg off

网卡流控丢包

  1. 查看流控统计:
ethtool -S eth1 | grep control


rx_flow_control_xon是在网卡的RX Buffer满或其他网卡内部的资源受限时,给交换机端口发送的开启流控的pause帧计数。对应的,tx_flow_control_xoff是在资源可用之后发送的关闭流控的pause帧计数。
2. 查看网络流控配置:ethtool -a eth1

解决方案:关闭网卡流控

ethtool -A ethx autoneg off //自协商关闭
ethtool -A ethx tx off //发送模块关闭
ethtool -A ethx rx off //接收模块关闭

报文mac地址丢包
一般计算机网卡都工作在非混杂模式下,此时网卡只接受来自网络端口的目的地址指向自己的数据,如果报文的目的mac地址不是对端的接口的mac地址,一般都会丢包,一般这种情况很有可能是源端设置静态arp表项或者动态学习的arp表项没有及时更新,但目的端mac地址已发生变化(换了网卡),没有更新通知到源端(比如更新报文被丢失,中间交换机异常等情况);

查看:

  1. 目的端抓包,tcpdump可以开启混杂模式,可以抓到对应的报文,然后查看mac地址;
  2. 源端查看arp表或者抓包(上一跳设备),看发送的mac地址是否和下一跳目的端的mac地址一致;

解决方案:

  1. 刷新arp表然后发包触发arp重新学习(可能影响其他报文,增加延时,需要小心操作);
  2. 可以在源端手动设置正确的静态的arp表项;

其他网卡异常丢包
这类异常比少见,但如果都不是上面哪些情况,但网卡统计里面任然有丢包计数,可以试着排查一下:
网卡firmware版本:
排查一下网卡phy芯片firmware是不是有bug,安装的版本是不是符合预期,查看 ethtool -i eth1:

和厂家提case询问是不是已知问题,有没有新版本等;

网线接触不良:
如果网卡统计里面存在crc error 计数增长,很可能是网线接触不良,可以通知网管排查一下:

ethtool -S eth0

解决方案:一般试着重新插拔一下网线,或者换一根网线,排查插口是否符合端口规格等;
报文长度丢包
网卡有接收正确报文长度范围,一般正常以太网报文长度范围:64-1518,发送端正常情况会填充或者分片来适配,偶尔会发生一些异常情况导致发送报文不正常丢包;

查看:


ethtool -S eth1|grep length_errors


解决方案:
1 调整接口MTU配置,是否开启支持以太网巨帧;
2 发送端开启PATH MTU进行合理分片;
简单总结一下网卡丢包:

网卡驱动丢包

查看:ifconfig eth1/eth0 等接口

1.RX errors: 表示总的收包的错误数量,还包括too-long-frames错误,Ring Buffer 溢出错误,crc 校验错误,帧同步错误,fifo overruns 以及 missed pkg 等等。

2.RX dropped: 表示数据包已经进入了 Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。

3.RX overruns: 表示了 fifo 的 overruns,这是由于 Ring Buffer(aka Driver Queue) 传输的 IO 大于 kernel 能够处理的 IO 导致的,而 Ring Buffer 则是指在发起 IRQ 请求之前的那块 buffer。很明显,overruns 的增大意味着数据包没到 Ring Buffer 就被网卡物理层给丢弃了,而 CPU 无法即使的处理中断是造成 Ring Buffer 满的原因之一,上面那台有问题的机器就是因为 interruprs 分布的不均匀(都压在 core0),没有做 affinity 而造成的丢包。

  1. RX frame: 表示 misaligned 的 frames。
  2. 对于 TX 的来说,出现上述 counter 增大的原因主要包括 aborted transmission, errors due to carrirer, fifo error, heartbeat erros 以及 windown error,而 collisions 则表示由于 CSMA/CD 造成的传输中断。

驱动溢出丢包
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来解决:

$ sysctl -w net.core.netdev_max_backlog=2000

单核负载高导致丢包

单核CPU软中断占有高, 导致应用没有机会收发或者收包比较慢,即使调整netdev_max_backlog队列大小仍然会一段时间后丢包,处理速度跟不上网卡接收的速度;

查看:mpstat -P ALL 1

单核软中断占有100%,导致应用没有机会收发或者收包比较慢而丢包;

解决方案:

1.调整网卡RSS队列配置:

查看:ethtool -x ethx;

调整:ethtool -X ethx xxxx;

2.看一下网卡中断配置是否均衡 cat /proc/interrupts

调整:


1) irqbalance 调整;
# 查看当前运行情况
service irqbalance status
# 终止服务
service irqbalance stop
2) 中断绑CPU核 echo mask > /proc/irq/xxx/smp_affinity

3.根据CPU和网卡队列个数调整网卡多队列和RPS配置
-CPU大于网卡队列个数:
查看网卡队列 ethtool -x ethx;
协议栈开启RPS并设置RPS;

echo $mask(CPU配置)> /sys/class/net/$eth/queues/rx-$i/rps_cpus
echo 4096(网卡buff)> /sys/class/net/$eth/queues/rx-$i/rps_flow_cnt
2)CPU小于网卡队列个数,绑中断就可以,可以试着关闭RPS看一下效果:
echo 0 > /sys/class/net/<dev>/queues/rx-<n>/rps_cpus