tcpdump抓包对性能的影响

版权声明:本文为博主原创,无版权,未经博主允许可以随意转载,无需注明出处,随意修改或保持可作为原创! https://blog.csdn.net/dog250/article/details/52502623
一直以来,提到这个话题,大家更多的关注的是tcpdump抓包本身的性能,比如能不能应付几十万的pps,能否在万兆网络上自运自如...我们现在知道,这些问题的答案都是否定的,即“不能”!因此你应该去关注netmap高性能抓包方案以及DPDK这样的东西...
        但本文不谈这些,本文谈的是被抓取数据包以外的东西,即tcpdump对那些未被命中抓包规则的数据包性能的影响。

接口和实现

不得不说,有的时候一些教诲是错的。比如说关注接口而不关注实现。信奉此信条的,不知踩了多少坑。对于tcpdump而言,你可能只需要关注tcpdump可以抓包就好了,而不必去关注tcpdump是怎么抓包的。事实上,对于tcpdump是怎么抓包的细节,我敢肯定大多数人,包括一些老资格的高级工程师都是不知道的,大多数情况下,很多人只是知道某个接口可以完成某件事情,对于完成该事情的能力上限却没有任何认知。
        在编程中我们也经常遇到这种事,比如Java中有HashMap,大多数人根本不管这个HashMap是怎么实现的,只知道它是一个高效的查询容器,Set了一个Value后,便可以通过其Key在任意时间Get出来,悲哀的是,即便在性能攸关的场景中,很多人还会无条件调用该接口,然后就出现了一个必须加班到深夜才可以排除的故障。我们知道,性能攸关的场景要求你对每一个步骤都了如指掌,这就站在了“关注接口而不是关注实现”的反面!
        然而,有人说“关注接口而不是关注实现”就一定正确吗?没有!这只是在软件工程某个领域的某个细节上是正确的,可以快速开发和迭代,对于训练熟练工是好的,但是对于像我这样的人,那显然是不够的。这也是我没有掌握也没兴趣掌握各种“框架”的本质原因吧。
        我来说一下我遇到过的事情。
        2011年,我遇到一个性能问题,最终的瓶颈是nf_conntrack,过滤的项目太多了,但是抛开这个具体场景不说,如果你的iptables规则设置太多了,也会影响性能,这需要你对iptables的执行机制有充分的理解。
        2013年,再次遇到一个性能问题,CPU,eth0网卡满载的情况下,pps急剧下降,后来发现是eth1网卡疯了导致,不断的up/down,导致路由表不断更新,我说这个eth1状态更新有问题,然而别的程序员却并不买账,eth1怎么可能影响eth0呢?想想也是哦...后来“只关注接口”的程序员试图默默重现该问题,写了一个脚本不断up/down这个eth1,然后观察eth0的变化,结果没有重现,我后来之处了问题所在:在模拟重现的过程中,你们的路由项加的太少了,加入10万条路由试试!问题的关键不在eth1的up/down,而是其up/down导致的路由表更新的锁操作。排查该问题,需要你对路由查找的算法,路由表更新的锁操作有充分的理解,只了解路由表的增删改查接口以及网卡的up/down接口是没有用的。
        近日,我被要求在10万级pps(来自百万级的IP地址)不衰减的情况,同步执行tcpdump抓取特定数据包。预研阶段我认为瓶颈可能会在BPF Filter,因为几年前我曾经研究过它的执行细节,按照线性过滤的执行规则,这明显是一个O(n)算法,且还要受到CPU Cache抖动的影响....高速网络中任意的O(n)操作都会指数级拉低性能!于是我考虑采用HiPAC多维树匹配代替BPF,最终由于耗时久,动作场面大讨论没通过而作罢。本着数据说话的原则,还是花了两天时间来验证tcpdump一下子过滤几千个IP地址是不是真的影响性能,结果是,和料想的一样,真的影响性能。

感官的印象

我不想通过iperf,netperf,hping3之类的玩意儿来验证,因为这些都是基于socket的,万一在数据发送端遇到协议栈的瓶颈,就很悲哀,因此,我试图通过一种绕开发送端协议栈的发包方案,这样显然可以节省几乎一半的排查工作量。感谢有pktgen!
        以下是发包脚本:
modprobe pktgen
echo rem_device_all >/proc/net/pktgen/kpktgend_0
echo max_before_softirq 1 >/proc/net/pktgen/kpktgend_0
echo clone_skb 1000 >/proc/net/pktgen/lo
echo add_device eth2 >/proc/net/pktgen/kpktgend_0
echo clone_skb 1000 >/proc/net/pktgen/eth2
echo pkt_size 550 >/proc/net/pktgen/eth2
echo src_mac 00:0C:29:B0:CE:CF >/proc/net/pktgen/eth2
echo flag IPSRC_RND >/proc/net/pktgen/eth2
echo src_min 10.0.0.2 >/proc/net/pktgen/eth2
echo src_max 10.0.0.255 >/proc/net/pktgen/eth2
echo dst 1.1.1.1 >/proc/net/pktgen/eth2
echo dst_mac  00:0C:29:A9:F3:DD >/proc/net/pktgen/eth2
echo count 0 >/proc/net/pktgen/eth2
echo pkt_size 100 >/proc/net/pktgen/eth2
echo start >/proc/net/pktgen/pgctrl 
以上脚本运行在机器A上,在与其直连的机器B上用sysstat来观察:
sar -n DEV 1
然后在机器B上执行下面的脚本:
# 此例子中,我仅仅是启2个tcpdump,每一个过滤600个IP地址,而已!循环参数可以任意调。
i=0
j=0
k=0

for ((k=1;k<2;k++)); do
        src='tcpdump -i any src '

        for ((i=1;i<30;i++)); do
                for ((j=20;j<50;j++)); do
                        temp=$src" 192.$k.$i.$j or src"
                        src=$temp
                done
        done

        src=$src" 192.168.11.11 -n"


        echo $src
        $src &
done

然后,sar的输出结果变化了吗?什么原因导致的呢?

PS:当然上面的pktgen发包方式是我默默做的,在实际中,我还是要使用程序员认可的东西,比如iperf,netperf,hping3之类低效的东西。我并不否认iperf,netperf,hping是好东西,我只是觉得它们在这个测试场景中大材小用了。

tcpdump抓包的架构

上一节的测试我希望看到此文的人自己去测试,这样感官印象更深刻些,不然太多的结果贴图,难免有凑篇幅之嫌了。

        本节,我给出目前tcpdump底层pcap的抓包原理,如下图所示:




这是标准的libpcap/tcpdump的结构,一个串行的,同步的抓包结构,当然,也许你已经接触过DPDK,netmap等,这些当然比libpcap/tcpdump更加优秀,但是面临的问题是开发周期太长,以netmap为例,如果你想用它最高效的模式,那就要牺牲对传统协议栈的兼容,反之,如果你想不patch驱动,不编译内核就能用,那你得到的也仅仅是一个兼容PACKET套接字的libpcap/tcpdump的兼容方案。

        另外,上述流程图中的很多细节我并没有叙述,比如缓冲区的组织形式,是使用copy还是mmap等等,但是这并不阻碍我们对其的理解,在微秒,甚至毫秒级的BPF开销下,内存操作开销真的不算什么了。
        总之,和之前遇到的iptables,路由表等问题一样,抓包也是一个有其能力极限的机制,它是好东西,但不要指望它在哪里都能帮你的忙-如果不是帮倒忙的话!
        我们来看一下高性能网络上的一些数据包分析的方案。比如在汇聚层,甚至核心层,我要是想对数据包进行审计,该怎么办?用tcpdump吗?....可以这么说,这个层次上,同步的抓包几乎是不可能的,一般都是使用端口镜像的方式,然后在另一台机器上去处理,这台专门处理数据包审计的机器一般都是众核机器,超多的CPU,超高的并行并发处理能力,当然,这是需要花钱的,这也是一种负责人的做法,另外一种不负责任的做法是什么呢?是类似tcpdump的做法,完全串行同步处理,这样会慢,但慢不是问题,问题是一定不能漏,这种思维其实是错误的,特别是在基于统计的互联网络上,任何事情都不精确,任何事情都不绝对。
        在网络上,80%就是全部!
阅读更多

没有更多推荐了,返回首页