IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

dropwatch 网络协议栈丢包检查利器

系统技术非业余研究 2013-02-27 23:17:33 累计浏览 3,500 次
本机暂存

在做网络服务器的时候,会碰到各种各样的网络问题比如说网络超时,通常一般的开发人员对于这种问题最常用的工具当然是tcpdump或者更先进的wireshark来进行抓包分析。通常这个工具能解决大部分的问题,但是比如说wireshark发现丢包,那深层次的原因就很难解释了。这不怪开发人员,要怪就怪linux网络协议栈太深。我们来看下:
network-stack

这7层里面每个层都可能由于各种各样的原因,比如说缓冲区满,包非法等,把包丢掉,这样的问题就需要特殊的工具来发现了。 好了,主角dropwatch出场.
它的官方网站在
这里

What is Dropwatch

Dropwatch is a project I am tinkering with to improve the visibility developers and sysadmins have into the Linux networking stack. Specifically I am aiming to improve our ability to detect and understand packets that get dropped within the stack.

Dropwatch定位很清晰,就是用来查看协议栈丢包的问题。

RHEL系的系统安装相当简单,yum安装下就好:

$ uname -r

2.6.32-131.21.1.tb477.el6.x86_64

$ sudo yum install dropwatch

man dropwatch下就可以得到使用的帮助,dropwatch支持交互模式, 方便随时启动和停止观测。

使用也是很简单:

$ sudo dropwatch -l kas

Initalizing kallsymsa db

dropwatch> start

Enabling monitoring...

Kernel monitoring activated.

Issue Ctrl-C to stop monitoring

1 drops at netlink_unicast+251

15 drops at unix_stream_recvmsg+32a

3 drops at unix_stream_connect+1dc

-l kas的意思是获取drop点的符号信息,这样的话针对源码就可以分析出来丢包的地方。

同学们可以参考这篇文章(Using netstat and dropwatch to observe packet loss on Linux servers):http://prefetch.net/blog/index.php/2011/07/11/using-netstat-and-dropwatch-to-observe-packet-loss-on-linux-servers/

那他的原理是什么呢?在解释原理之前,我们先看下这个工具的对等的stap脚本:

$ cat /usr/share/doc/systemtap-1.6/examples/network/dropwatch.stp

#!/usr/bin/stap

############################################################

# Dropwatch.stp

# Author: Neil Horman <nhorman@redhat.com>

# An example script to mimic the behavior of the dropwatch utility

# http://fedorahosted.org/dropwatch

############################################################

# Array to hold the list of drop points we find

global locations

# Note when we turn the monitor on and off

probe begin { printf("Monitoring for dropped packets ") }

probe end { printf("Stopping dropped packet monitor ") }

# increment a drop counter for every location we drop at

probe kernel.trace("kfree_skb") { locations[$location] <<< 1 }

# Every 1 seconds report our drop locations

probe timer.sec(1)

{

 printf(" ")

 foreach (l in locations-) {

   printf("%d packets dropped at %s ",

          @count(locations[l]), symname(l))

 }

 delete locations

}

这个脚本核心的地方就在于这行:

probe kernel.trace(“kfree_skb”) { locations[$location] <<< 1 }

当kfree_skb被调用的时候,内核就记录下这个drop协议栈的位置,同时透过netlink通知dropwatch用户态的部分收集这个位置,同时把它整理显示出来.以上就是dropwatch的工作流程。

现在的问题是内核什么地方,什么时候会调用kfree_skb这个函数呢? 我们继续追查下:

dropwatch需要对内核打patch,当然RHEL5U4以上的内核都已经打了patch。

patch在这里下载:https://fedorahosted.org/releases/d/r/dropwatch/dropwatch_kernel_patches.tbz2

通过查看里面的5个patch,我们知道drop主要修改了以下几个文件:

include/linux/netlink.h

include/trace/skb.h

net/core/Makefile

net/core/net-traces.c

include/linux/skbuff.h

net/core/datagram.c

include/linux/net_dropmon.h

net/core/drop_monitor.c

include/linux/Kbuild

net/Kconfig

net/core/Makefile

我们透过RHEL5U4的代码可以清楚的看到:

//include/linux/skbuff.h

extern void kfree_skb(struct sk_buff *skb);

extern void consume_skb(struct sk_buff *skb);

这些patch的作用是使得支持dropwatch的内核把kfree_skb分成二类: 1. 人畜无害的调用consume_skb 2. 需要丢包的调用kfree_skb 同时提供基础的netlink通信往用户空间传递位置信息。

知道了这点,我们在RHEL 5U4的源码目录下: linux-2.6.18.x86_64/net/ipv4 或者 linux-2.6.18.x86_64/net/core下 grep下

$ grep -rin kfree_skb  .

./tcp_input.c:3122:                     __kfree_skb(skb);

./tcp_input.c:3219:                     __kfree_skb(skb);

./tcp_input.c:3234:             __kfree_skb(skb);

./tcp_input.c:3318:                             __kfree_skb(skb);

...

./ip_fragment.c:166:static __inline__ void frag_kfree_skb(struct sk_buff *skb, int *work)

./ip_fragment.c:171:    kfree_skb(skb);

./ip_fragment.c:211:            frag_kfree_skb(fp, work);

./ip_fragment.c:452:            frag_kfree_skb(fp, NULL);

./ip_fragment.c:578:                    frag_kfree_skb(free_it, NULL);

./ip_fragment.c:607:    kfree_skb(skb);

./ip_fragment.c:732:    kfree_skb(skb);

./udp.c:1028:   kfree_skb(skb);

./udp.c:1049:           kfree_skb(skb);

./udp.c:1069:                   kfree_skb(skb);

./udp.c:1083:                   kfree_skb(skb);

./udp.c:1134:                                   kfree_skb(skb1);

./udp.c:1229:   kfree_skb(skb);

./udp.c:1242:   kfree_skb(skb);

./udp.c:1258:   kfree_skb(skb);

./udp.c:1418:                           kfree_skb(skb);

./ip_sockglue.c:286:            kfree_skb(skb);

./ip_sockglue.c:322:            kfree_skb(skb);

./ip_sockglue.c:398:    kfree_skb(skb);

./devinet.c:1140:               kfree_skb(skb);

./xfrm4_ninput.c:29:    kfree_skb(skb);

./xfrm4_ninput.c:122:   kfree_skb(skb);

./tunnel4.c:85: kfree_skb(skb);

./icmp.c:47: *                                  and moved all kfree_skb() up to

./icmp.c:1046:  kfree_skb(skb);

./ip_forward.c:121:     kfree_skb(skb);

./netfilter.c:73:               kfree_skb(*pskb);

./netfilter.c:114:              kfree_skb(*pskb);

./netfilter/ip_queue.c:277:             kfree_skb(skb);

./netfilter/ip_queue.c:335:     kfree_skb(nskb);

./netfilter/ip_queue.c:373:                     kfree_skb(e->skb);

./netfilter/ip_queue.c:556:             kfree_skb(skb);

...

./netfilter/ipt_TCPMSS.c:153:           kfree_skb(*pskb);

./netfilter/ipt_ULOG.c:435:                     kfree_skb(ub->skb);

./tcp.c:1458:                           kfree_skb(skb);

./tcp.c:1577:           __kfree_skb(skb);

./ip_gre.c:482:         kfree_skb(skb2);

./ip_gre.c:497:                 kfree_skb(skb2);

./ip_gre.c:504:                 kfree_skb(skb2);

...

./ip_gre.c:892: dev_kfree_skb(skb);

./raw.c:244:            kfree_skb(skb);

./raw.c:254:            kfree_skb(skb);

./raw.c:329:    kfree_skb(skb);

./tcp_ipv4.c:1039:      kfree_skb(skb);

./tcp_ipv4.c:1151:      kfree_skb(skb);

./ipvs/ip_vs_xmit.c:213:        kfree_skb(skb);

./ipvs/ip_vs_xmit.c:290:        kfree_skb(skb);

./ipvs/ip_vs_xmit.c:374:                        kfree_skb(skb);

./ipvs/ip_vs_xmit.c:378:                kfree_skb(skb);

./ipvs/ip_vs_xmit.c:423:        kfree_skb(skb);

./ipvs/ip_vs_xmit.c:480:        kfree_skb(skb);

./ipvs/ip_vs_xmit.c:553:        dev_kfree_skb(skb);

./ipvs/ip_vs_core.c:197:        kfree_skb(*pskb);

./ipvs/ip_vs_core.c:829:        kfree_skb(*pskb);

./ipconfig.c:504:       kfree_skb(skb);

./ipconfig.c:1019:      kfree_skb(skb);

./xfrm4_input.c:50:     kfree_skb(skb);

./xfrm4_input.c:162:    kfree_skb(skb);

./fib_semantics.c:292:          kfree_skb(skb);

./ipip.c:414:           kfree_skb(skb2);

./ipip.c:429:                   kfree_skb(skb2);

./ipip.c:436:                   kfree_skb(skb2);

./ipip.c:444:                   kfree_skb(skb2);

./ipip.c:458:   kfree_skb(skb2);

./ipip.c:482:                   kfree_skb(skb);

./ipip.c:609:                   dev_kfree_skb(skb);

./ipip.c:615:           dev_kfree_skb(skb);

./ipip.c:654:   dev_kfree_skb(skb);

./ipmr.c:185:   kfree_skb(skb);

...

./ip_output.c:763:      kfree_skb(skb);

./ip_output.c:964:                              kfree_skb(skb);

./ip_output.c:1313:             kfree_skb(skb);

我们可以看到一堆可以丢包的点。

当我们观察到dropwatch发现丢包的时候,可以根据符号信息结合以上的源码轻松的分析出来问题所在。

小结: 工具是知识的积累。

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. Linux如何统计进程的CPU利用率 (累计阅读 16,308)
  2. 我的 RHCA 之路 (累计阅读 14,013)
  3. Linux内存点滴 用户进程内存空间 (累计阅读 13,231)
  4. 给程序员新手的一些建议 (累计阅读 13,089)
  5. Linux 性能监控、测试、优化工具 (累计阅读 13,011)
  6. 关于linux内存free的一些事情 (累计阅读 12,867)
  7. ps - 按进程消耗内存多少排序 (累计阅读 12,688)
  8. Google怎么用linux (累计阅读 12,581)
  9. Linux Used内存到底哪里去了? (累计阅读 11,868)
  10. find命令的一点注意事项 (累计阅读 11,866)