16年在房多多的时候参与开发nids,用bro[1]来做抓包、解包和规则引擎。
当时对bro解析http的成功率做测试,生产环境中发现差不多有10%-20%的http请求并没有解析成功。
我当时的测试思路如下:
10%-20%的解析失败,意味着有可能有攻击行为没有检测到。因此需要提升http流量解析的成功率。
当时在网上也搜到一些提升抓包性能的方案,比如pf_ring、dpdk等。不过当时理解不了原理,最近补了一些网络方面的知识,所以就来回顾一下这个案例。
我主要想弄清楚以下问题:
基于以下两点原因,本文只讨论pf_ring、不讨论dpdk:
另外本文只讨论 pf_ring的三种模式[2] 中的默认模式
先理解一下"问题背景"里场景的性能
从网卡上的流量到bro将http请求写到日志中,可以大概分成三个部分:
* 抓包(包从网卡到用户空间)
* 解包(解析出http、dns、ssh、ftp等应用层协议)
* 写磁盘
这每一步都会有性能损耗,但是性能损耗的占比可能不同。所以仅提升"抓包"这一部分的性能有可能并不能提升整体性能。
用一些性能工具(比如perf)结合代码 应该可以看出来bro每个阶段的cpu消耗占比。
这篇文章只关注"抓包"这个环节的性能。
为什么会丢包?
丢包的原因太多了,在网络传输 -> 网卡 -> 驱动 -> 内核sk_buff -> tcp/ip协议栈 -> 用户态
每一个环节都有可能丢包。
可以看 图解Linux网络包接收过程[3] 这篇文章了解完整的收包过程。
关于"排查各个环节丢包的工具和思路",由于没有实践过多少,这里就不推荐啥文章了。
抓包过程中的性能和哪些因素有关?
目前我理解的主要和三个方面有关:
* 内存拷贝的次数
* 内核处理网卡中断的频率
* 系统调用产生的中断次数
所以pf_ring做了以下事情:
* 通过mmap映射,减少了一次内存拷贝
* 通过napi机制减少内核响应网卡硬中断的次数
* 也因为mmap映射,用户层不需要频繁地通过recvfrom等系统调用获取网络包
如果想了解mmap怎么实现内核态和用户态的内存映射,可以参考 文章中的示例1[4] ,有demo代码可以跑起来。
pf_ring的实现方式和文章中的差不多:
* 内核同样会注册mmap函数供用户态调用
* 内核同样会调用`remap_pfn_range`函数来做内存映射
如果想了解napi机制,可以看 napi官方文档[5]。
我对napi的理解:
* 它的作用是:将包从网卡放到内核内存
* 如果每次都是网卡产生中断告诉cpu有数据包来了,在流量大的请求下就需要响应中断很多次。napi减少了硬中断的响应次数,所以提升了性能。
* 它由内核驱动实现
如果想了解napi和非napi的实现上的区别,可以参考 Linux kernel 链路层帧接收[6] 并对照内核源码分析。此文章简要分析了两个驱动,分别代表了napi实现和非napi实现。
pf_ring真的可以提高libpcap性能么?
新版本的libpcap采用PACKET_MMAP方式收包。
PACKET_MMAP是内核实现的一种机制,发包和收包都可以用这个技术。它同样使用mmap
减少了内存拷贝和系统调用,因此提升了性能。
PACKET_MMAP的详细介绍推荐看内核文档[7],写得比较全(内容比较多,我也没完整过一遍)。
我对"基于pf_ring非零拷贝的libpcap"和"基于packet_mmap的libpcap"对比,结论如下:
* 两者内存拷贝次数一样
* 两者使用相同的驱动,都用到了napi
* 用户态读数据时,基本都不需要经过系统调用
根据这个对比,我觉得"pf_ring非零拷贝模式"应该不会大幅度提高抓包性能。
为了验证自己的结论,就做个实验来看看。
实验验证
在自己实验前,看到 PF_RING与NAPI结合的捕包性能优化和仿真.pdf[8] 这篇实践文章,推荐阅读。此文章结论是pf_ring可以提高抓包性能
我做的实验过程见公众号中另一篇文章"验证pf_ring性能",结论是pf_ring可以提高抓包成功率。
实验结果上虽然可以看出来提高抓包成功率,但是看不出来是否有大幅度提升。
实验的结论和我自己对原理的分析并没有对应上,也并没有完全解决我一开始想弄清楚的问题。
因为文章中的很多技术我在研究案例之前不怎么知道,另外也没有找到搞网络的大佬愿意帮我校对文章,所以不能保证文章中的结论都是对的。
如果读者发现有什么错误的地方,请公众号后台私信我。
bro: https://github.com/bro/bro
[2]pf_ring的三种模式: https://www.ntop.org/pf_ring/pf_ring-and-transparent-mode/
[3]图解Linux网络包接收过程: https://zhuanlan.zhihu.com/p/256428917
[4]文章中的示例1: https://nieyong.github.io/wiki_cpu/mmap%E8%AF%A6%E8%A7%A3.html
[5]napi官方文档: https://wiki.linuxfoundation.org/networking/napi
[6]Linux kernel 链路层帧接收: http://www.hyuuhit.com/2018/07/25/receive-packet/
[7]内核文档: https://github.com/torvalds/linux/blob/master/Documentation/networking/packet_mmap.rst
[8]PF_RING与NAPI结合的捕包性能优化和仿真.pdf: http://cea.ceaj.org/CN/article/downloadArticleFile.do?attachType=PDF&id=30309