对每个数据包使用 NdisFIndicateReceiveNetBufferLists 还是将它们链接在一起接收?

Posted

技术标签:

【中文标题】对每个数据包使用 NdisFIndicateReceiveNetBufferLists 还是将它们链接在一起接收?【英文标题】:Using NdisFIndicateReceiveNetBufferLists for every packet vs chaining them all together to receive? 【发布时间】:2021-10-16 13:46:09 【问题描述】:

我有一个 NDIS 驱动程序,我将接收到的数据包发送到用户服务,然后服务将这些数据包标记为 OK(非恶意),然后我遍历可以接收的数据包,然后我将它们一一发送通过使用一个 NetBuffer 将它们中的每一个转换回适当的 NetBufferList,然后我使用 NdisFIndicateReceiveNetBufferLists 指示它们。

这导致在通过 SMB 传输大文件(从共享中复制文件)时出现问题,从而大大降低了传输速度。

作为一种解决方法,我现在将所有正常的 NBL 链接起来(而不是一个一个地发送它们),然后通过 NdisFIndicateReceiveNetBufferLists 一次发送所有它们。

我的问题是,这种变化会导致任何问题吗?一个接一个地发送X个NBL与将它们链接在一起并一次发送所有它们之间有什么区别? (因为它们中的大多数可能与不同的流程/应用程序有关)

另外,与通过 FilterSendNetBufferLists 发送多包相比,多包接收中将包链接在一起的好处要大得多,这是为什么呢?

【问题讨论】:

【参考方案1】:

一个 NET_BUFFER 代表一个单一的网络帧。 (对 LSO/RSC 进行适当的挥手。)

NET_BUFFER_LIST 是相关 NET_BUFFER 的集合。同一个 NET_BUFFER_LIST 上的每个 NET_BUFFER 都属于同一个“流量”(稍后会详细介绍),它们具有所有相同的元数据,并将对其执行所有相同的卸载。因此,我们使用 NET_BUFFER_LIST 对相关数据包进行分组,并让它们共享元数据。

数据路径通常在多个 NET_BUFFER_LIST 的批次上运行。仅出于性能原因将整个批次组合在一起;批次中的多个 NBL 之间没有很多隐含关系。例外:大多数数据路径例程都采用一个 Flags 参数,该参数可以保存对一批中的所有 NBL 进行某些声明的标志,例如,NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE

总而言之,您确实可以安全地将多个 NET_BUFFER_LIST 分组为一个指示,这对于性能尤为重要。如果您愿意,您可以将不相关的 NBL 组合在一起。但是,如果您正在组合成批的 NBL,请确保清除任何 NDIS_XXX_FLAGS_SINGLE_XXX 样式标志。 (当然,除非您知道标志的承诺仍然有效。例如,如果您合并 2 批 NBL,它们都具有 NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE 标志,并且如果您验证第一个 NBL在每个批次中具有相同的 EtherType,那么保留 NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE 标志实际上是安全的。)

但是请注意,您通常不能将多个 NET_BUFFER 组合到同一个 NET_BUFFER_LIST 中,除非您控制生成负载的应用程序并且您知道 NET_BUFFER 的负载属于同一流量流。流量的确切语义在 NDIS 层中有点模糊,但您可以想象这意味着任何 NDIS 级别的硬件卸载都可以安全地将每个数据包视为相同。例如,IP 校验和卸载需要知道每个数据包具有相同的伪标头。如果所有的数据包都属于同一个 TCP 或 UDP 套接字,那么它们可以被视为同一个流。

另外,与通过 FilterSendNetBufferLists 发送多包相比,在多包接收中将数据包链接在一起的好处要大得多,这是为什么呢?

接收是昂贵的路径,原因有两个。首先,操作系统必须花费 CPU 来解复用来自网络的原始数据包流。网络可以从任何随机套接字向我们发送数据包,或者根本不匹配任何套接字的数据包,并且操作系统必须为任何可能性做好准备。其次,接收路径处理的是不受信任的数据,所以解析时要谨慎。

相比之下,发送路径非常便宜:数据包只是落入微型端口驱动程序,后者设置了 DMA,然后将它们发送到硬件。发送路径中的任何人都不会真正关心数据包 in 中的实际内容(防火墙在 NDIS 看到数据包之前已经运行,因此您看不到该成本;如果微型端口正在执行任何卸载,那是有偿的在硬件的内置处理器上,因此它不会显示在您可以在任务管理器中看到的任何 CPU 上。)

因此,如果您将一批 100 个数据包分解为接收路径上 1 个数据包的 100 次调用,则操作系统必须通过 100 次调用一些昂贵的解析函数。同时,通过发送路径进行 100 次调用并不是很好,但这只是接收路径 CPU 成本的一小部分。

【讨论】:

感谢 Mr.Tippet 抽出宝贵时间回答问题。如果您也能回答我关于 NDIS 5.0 的其他问题,我也将不胜感激:***.com/questions/68769114/… 不幸的是,NDIS 5.x 是我这个时代之前的古老技术,我无法提供帮助。几乎不再需要开发 NDIS 5.x 驱动程序了——微软仍然支持的所有 Windows 版本都使用 NDIS 6.0,并且只允许 NDIS 5.x 以慢速兼容层运行。

以上是关于对每个数据包使用 NdisFIndicateReceiveNetBufferLists 还是将它们链接在一起接收?的主要内容,如果未能解决你的问题,请参考以下文章

更新 GNU/Linux 内核以每个数据包发送 n 个 TCP 数据包

试图对数据包校验和/CRC/哈希进行逆向工程

1: unit test

R语言使用DALEX包的model_performance函数对h2o包生成的多个算法模型进行残差分布分析并可视化每个模型的残差反向累积分布图

R语言使用DALEX包的model_performance函数对caret包生成的多个算法模型进行残差分布分析并可视化每个模型的残差反向累积分布图

如何捕获每个 PID 的网络数据包?