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%就是全部!
---------------------
作者:dog250
来源:CSDN
原文:https://blog.csdn.net/dog250/article/details/52502623
版权声明:本文为博主原创文章,转载请附上博文链接!
------------------------------
tcpdump丢包分析
https://blog.csdn.net/blade2001/article/details/41543297
tcpdump丢包分析
通过tcpdump抓包时,结束后tcpdump会给出如下统计信息:
1552 packets captured
1586 packets received by filter
34 packets dropped by kernel
其中“captured”的计数指的是应用层捕获到的数据,“received by filter”和“dropped by kernel”的计数由内核维护,应用层通过getsockopt来获取。收到一个包,“received by filter”会加1,如果sock的接收buffer被填满时,则把这个数据包丢弃,将“dropped by kernel”加1。
if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= (unsigned)sk->sk_rcvbuf){
spin_lock(&sk->sk_receive_queue.lock);
po->stats.tp_drops++;
spin_unlock(&sk->sk_receive_queue.lock);
}
通过调节/proc/sys/net/core/rmem_default和/proc/sys/net/core/rmem_max能够改变sk_rcvbuf的大小。
正常“captured”加上“dropped by kernel”应该等于“received by filter”的大小,有的时候出现不等的情况应该是还有一些数据包在sk_rcvbuf中,还没有被应用层收到的原因。
丢包原因:
经过google以及分析,造成这种丢包的原因是由于libcap抓到包后,tcpdump上层没有及时的取出,导致libcap缓冲区溢出,从而覆盖了未处理包,此处即显示为dropped by kernel,注意,这里的kernel并不是说是被linux内核抛弃的,而是被tcpdump的内核,即libcap抛弃掉的
解决方法:
根据以上分析,可以通过改善tcpdump上层的处理效率来减少丢包率,下面的几步根据需要选用,每一步都能减少一定的丢包率
1. 最小化抓取过滤范围,即通过指定网卡,端口,包流向,包大小减少包数量
2. 添加-n参数,禁止反向域名解析
3. 添加-B参数,加大OS capture buffer size
4. 指定-s参数, 最好小于1000
5. 将数据包输出到cap文件
6. 用sysctl修改SO_REVBUF参数,增加libcap缓冲区长度:/proc/sys/net/core/rmem_default和/proc/sys/net/core/rmem_ma
-B buffer_size
Set the operating system capture buffer size to buffer_size, in units of KiB (1024 bytes).
-n
Don't convert addresses (i.e., host addresses, port numbers, etc.) to names.
-s snaplen
--snapshot-length=snaplen
Snarf snaplen bytes of data from each packet rather than the default of 65535 bytes. Packets truncated because of a limited snapshot are indicated in the output with ``[|proto]'', where proto is the name of the protocol level at which the truncation has occurred. Note that taking larger snapshots both increases the amount of time it takes to process packets and, effectively, decreases the amount of packet buffering. This may cause packets to be lost. You should limit snaplen to the smallest number that will capture the protocol information you're interested in. Setting snaplen to 0 sets it to the default of 65535, for backwards compatibility with recent older versions of tcpdump.
http://blog.tianya.cn/blogger/post_read.asp?BlogID=227219&PostID=16646525
http://blog.csdn.net/cszhouwei/article/details/17798271
https://blog.csdn.net/dog250/article/details/52502623