linux 丢包排查思路简述

  • 概述
  • 网络包接收流程
  • 网络包发送流程
  • 丢包排查的思路
  • tcp排查方法
  • rdma排查方法
  • 网络工具汇总
  • 参考链接



概述

我们首先以tcp网络为例,谈谈linux系统如何收发网络包

centos7 查看丢包 linux查看是否丢包_丢包

在进行网络传输时,数据包就会按照协议栈,对上一层发来的数据进行逐层处理;然后封装上该层的协议头,再发送给下一层。

  • 传输层在应用程序数据前面增加了 TCP 头;
  • 网络层在 TCP 数据包前增加了 IP 头;
  • 而网络接口层,又在 IP 数据包前后分别增加了帧头和帧尾。

这些新增的头部和尾部,都按照特定的协议格式填充,想了解具体格式,你可以查看协议的文档。

比如,你可以查看这里,了解 TCP 头的格式。这些新增的头部和尾部,增加了网络包的大小,但我们都知道,物理链路中并不能传输任意大小的数据包。网络接口配置的最大传输单元(MTU),就规定了最大的 IP 包大小。在我们最常用的以太网中,MTU 默认值是 1500(这也是 Linux 的默认值)。一旦网络包超过 MTU 的大小,就会在网络层分片,以保证分片后的 IP 包不大于 MTU 值。显然,MTU 越大,需要的分包也就越少,自然,网络吞吐能力就越好。

如下图所示,就是 Linux 通用 IP 网络栈的示意图:(图片参考《性能之巅》图 10.7 通用 IP 网络栈绘制)

centos7 查看丢包 linux查看是否丢包_centos7 查看丢包_02

我们从上到下来看这个网络栈,可以发现,

  • 最上层的应用程序,需要通过系统调用,来跟套接字接口进行交互;
  • 套接字的下面,就是我们前面提到的传输层、网络层和网络接口层;
  • 最底层,则是网卡驱动程序以及物理网卡设备。

网卡,作为发送和接收网络包的基本设备。在系统启动过程中,网卡通过内核中的网卡驱动程序注册到系统中。而在网络收发过程中,内核通过中断跟网卡进行交互。再结合前面提到的 Linux 网络栈,可以看出,网络包的处理非常复杂。所以,网卡硬中断只处理最核心的网卡数据读取或发送,而协议栈中的大部分逻辑,都会放到软中断中处理。(如果top时发现系统的si比较高,可以通过定位/proc/softirqs中增长较快的软中断从而定位问题。)

理解了上述架构,我们就不难理解,为什么tcp的收发过程会牵涉到如下几个缓冲区:

  • 网卡收发网络包时,通过 DMA 方式交互的环形缓冲区
  • 网卡中断处理程序为网络帧分配的,内核数据结构 sk_buff 缓冲区
  • 应用程序通过套接字接口,与网络协议栈交互时的套接字缓冲区

网络包接收流程

  1. 当一个网络帧到达网卡后,网卡会通过 DMA 方式,把这个网络包放到收包队列中;然后通过硬中断,告诉中断处理程序已经收到了网络包。
  2. 接着,网卡中断处理程序会为网络帧分配内核数据结构(sk_buff),并将其拷贝到 sk_buff 缓冲区中;然后再通过软中断,通知内核收到了新的网络帧。
  3. 接下来,内核协议栈从缓冲区中取出网络帧,并通过网络协议栈,从下到上逐层处理这个网络帧。比如,
  • 在链路层检查报文的合法性,找出上层协议的类型(比如 IPv4 还是 IPv6),再去掉帧头、帧尾,然后交给网络层。
  • 网络层取出 IP 头,判断网络包下一步的走向,比如是交给上层处理还是转发。当网络层确认这个包是要发送到本机后,就会取出上层协议的类型(比如 TCP 还是 UDP),去掉 IP 头,再交给传输层处理。
  • 传输层取出 TCP 头或者 UDP 头后,根据 < 源 IP、源端口、目的 IP、目的端口 > 四元组作为标识,找出对应的 Socket,并把数据拷贝到 Socket 的接收缓存中。
  1. 最后,应用程序就可以使用 Socket 接口,读取到新接收到的数据了。

网络包发送流程

了解网络包的接收流程后,就很容易理解网络包的发送流程。网络包的发送流程就是上图的右半部分,很容易发现,网络包的发送方向,正好跟接收方向相反。

  1. 首先,应用程序调用 Socket API(比如 sendmsg)发送网络包。由于这是一个系统调用,所以会陷入到内核态的套接字层中。套接字层会把数据包放到 Socket 发送缓冲区中。
  2. 接下来,网络协议栈从 Socket 发送缓冲区中,取出数据包;再按照 TCP/IP 栈,从上到下逐层处理。比如,传输层和网络层,分别为其增加 TCP 头和 IP 头,执行路由查找确认下一跳的 IP,并按照 MTU 大小进行分片。
  3. 分片后的网络包,再送到网络接口层,进行物理地址寻址,以找到下一跳的 MAC 地址。然后添加帧头和帧尾,放到发包队列中。这一切完成后,会有软中断通知驱动程序:发包队列中有新的网络帧需要发送。
  4. 最后,驱动程序通过 DMA ,从发包队列中读出网络帧,并通过物理网卡把它发送出去。

丢包排查的思路

理解了linux网络栈的原理,我们就不难去定位丢包问题,

  1. 如果是出向丢包,那么肯定是网络包发送流程出了问题,所以排查第一步是找到什么网络包出现了丢包重传。然后再根据网络包发送流程分析丢包原因
  2. 如果是入向丢包,那么肯定是网络包接收流程出了问题,常见的问题如网络缓冲器满了等

关于丢包及其分析设计网络栈、内核栈的各个层面,笔者在也仅仅稍懂皮毛,欢迎读者踊跃补充。

我们可以使用ifconfig可以查看网络丢包情况

$ ifconfig net0
net0: flags=6211<UP,BROADCAST,RUNNING,SLAVE,MULTICAST>  mtu 1500
        ether 98:03:9b:8c:63:3a  txqueuelen 1000  (Ethernet)
        RX packets 46725983  bytes 9200932091 (8.5 GiB)
        RX errors 0  dropped 48193  overruns 0  frame 0
        TX packets 127710842  bytes 127005910250 (118.2 GiB)
        TX errors 1  dropped 0 overruns 0  carrier 1  collisions 0
  • RX errors: 表示总的收包的错误数量,这包括 too-long-frames 错误,Ring Buffer 溢出错误,crc 校验错误,帧同步错误,fifo overruns 以及 missed pkg 等等。
  • RX dropped: 表示数据包已经进入了 Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。
  • RX overruns: 表示了 fifo 的 overruns,这是由于 Ring Buffer(aka Driver Queue) 传输的 IO 大于 kernel 能够处理的 IO 导致的,而 Ring Buffer 则是指在发起 IRQ 请求之前的那块 buffer。很明显,overruns 的增大意味着数据包没到 Ring Buffer 就被网卡物理层给丢弃了,而 CPU 无法即使的处理中断是造成 Ring Buffer 满的原因之一
  • RX frame: 表示 misaligned 的 frames。

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

droppedoverruns的区别:

  • dropped,表示这个数据包已经进入到网卡的接收缓存fifo队列,并且开始被系统中断处理准备进行数据包拷贝(从网卡缓存fifo队列拷贝到系统内存),但由于此时的系统原因(比如内存不够等)导致这个数据包被丢掉,即这个数据包被Linux系统丢掉。
  • overruns,表示这个数据包还没有被进入到网卡的接收缓存fifo队列就被丢掉,因此此时网卡的fifo是满的。为什么fifo会是满的?因为系统繁忙,来不及响应网卡中断,导致网卡里的数据包没有及时的拷贝到系统内存,fifo是满的就导致后面的数据包进不来,即这个数据包被网卡硬件丢掉。所以,个人觉得遇到overruns非0,需要检测cpu负载与cpu中断情

tcp排查方法

tcp丢包排查方法和工具集比较健全,手段也比较多,一般情况下,我们分析tcp丢包主要分析出向丢包重传,通过在出向丢包的服务器上抓包,找到出问题的网络包和对端服务器。

这里仅举几个例子:

  1. 通过tcpdump抓包,然后使用wireshark使用过滤器tcp.analysis.retransmission找到重传的网络包。
  • wireshark抓重传包的原理是:由于tcp字段中没有重传相关的资源,因此wireshark通过扫描网络包中SEQ/ACK number, IP ID, source and destination IP address, TCP Port等字段,找到重复包,这些包就是丢包重传的包。
  1. tcpdump作为用户态进程,势必要消耗很多系统资源,如果丢包概率很低,丢包出现的时间也很短,使用tcpdump往往会无功而返,Brendan Gregg依托eBPF开发出了轻量级的tcp重传抓取工具: tcpretrans(包含在bcc工具集中)。这个工具它只抓取所有重传的包,具体的原理是借助Linux Kernel的ftrace机制在tcp_retransmit_skb这个函数加了一个hook来获取重传包的skb地址,然后以这个skb的地址作为hash key去/proc/net/tcp这个文件里面匹配tcp连接信息,然后把发生重传的这个tcp连接的信息(time,src,sport,dst,dport,state)给打印出来。由于它只抓取该服务器发出的重传包,所以它的系统开销是很小的,我们完全可以把它部署到服务器上一直运行着去抓包。

rdma排查方法

目前我遇到的系统(mlx cx4/cx5/cx6),在rdma上主要的指标是入向丢包(out_of_order),其根因有如下几种情况:

  1. 入向丢包服务器的rdma网卡缓冲区拥塞,单凭ecn已经无法限制流量,因此网卡产生出向pfc,伴随有可能产生入向丢包。有关pfc可以查看笔者之前的文章 浅谈RDMA流控设计
  2. Roce 2网络链路上dscp配置出错,RDMA流量跑到了其他的网卡队里里,而其他的队列有可能是lossy模式,不能保证无损转发,因此导致丢包。排查步骤如下:
  • 打开roce v2的tcpdump支持ethtool --set-priv-flags net0 sniffer on(注意,使用完成之后记得关闭ethtool --set-priv-flags net0 sniffer off
  • 使用tcpdump进行抓包,在wireshark中使用过滤器ip.dsfield.dscp,过滤出dscp错误的rdma包。如果存在,说明有dscp入错队列的rdma流量。
  • 也可以在抓包的时候使用参数过滤,如:tcpdump -i net2 '(ip and (ip[1] & 0xfc) >> 2 != 46)' -w rdma_retrans.pcap,该语句过滤掉了dscp等于46的网络包。
  • 抓到包后我们就知道了问题流量的src和dest在哪里,我们可以通过perftest(如ib_send_bw)工具打流确认。

网络工具汇总

tcp

centos7 查看丢包 linux查看是否丢包_centos7 查看丢包_03