繁体   English   中英

如何使用 NDIS 过滤器驱动程序读取接收到的数据包?

[英]How can I read the received packets with a NDIS filter driver?

我目前正在试验NDIS 驱动程序示例 我正在尝试打印数据包内容(包括 MAC 地址、EtherType 和数据)。

我的第一个猜测是在 function FilterReceiveNetBufferLists中实现这个。 不幸的是,我不确定如何从NetBufferLists中提取数据包内容。

这是正确的起点。 考虑这段代码:

void FilterReceiveNetBufferLists(..., NET_BUFFER_LIST *nblChain, ...)
{
    UCHAR buffer[14];
    UCHAR *header;

    for (NET_BUFFER_LIST *nbl = nblChain; nbl; nbl = nbl->Next) {
        header = NdisGetDataBuffer(nbl->FirstNetBuffer, sizeof(buffer), buffer, 1, 1);
        if (!header)
            continue;

        DbgPrint("MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n",
            header[0], header[1], header[2],
            header[3], header[4], header[5]);
    }

    NdisFIndicateReceiveNetBufferLists(..., nblChain, ...);
}

关于这段代码有几点需要考虑。

NDIS 数据路径使用 NET_BUFFER_LIST (nbl) 作为其主要数据结构。 一个 nbl 表示一组具有相同元数据的数据包。 对于接收路径,没有人真正了解元数据,因此该集合中始终只有 1 个数据包。 换句话说,nbl 是一个长度为 1 的列表。对于接收路径,您可以指望它。

nbl 是一个或多个 NET_BUFFER (nb) 结构的列表。 nb 表示单个网络帧(受 LSO 或 RSC 约束)。 因此,nb 最接近于您认为的数据包。 它的元数据存储在包含它的 nbl 上。

在一个 nb 中,实际的数据包有效负载存储为一个或多个缓冲区,每个缓冲区表示为一个 MDL。 从心理上讲,您应该假装 MDL 只是串联在一起。 例如,网络标头可能在一个 MDL 中,而有效负载的 rest 可能在另一个 MDL 中。

最后,为了性能,NDIS 为您的 LWF 提供尽可能多的 NBL。 这意味着有一个或多个 NBL 的列表。

把它们放在一起,你有:

  • 您的 function 收到 NBL 列表。
  • 每个 NBL 恰好包含 1 个 NB(在接收路径上)。
  • 每个 NB 都包含一个 MDL 列表。
  • 每个 MDL 指向一个有效负载缓冲区。

所以在我们上面的示例代码中,for 循环沿着第一个要点进行迭代:NBL 链。 在循环中,我们只需要查看nbl->FirstNetBuffer ,因为我们可以安全地假设除了第一个 nb 之外没有其他 nb。

必须直接摆弄所有这些 MDL 很不方便,因此我们使用辅助例程NdisGetDataBuffer 你告诉这个人你想查看多少字节的有效载荷,他会给你一个指向连续有效载荷范围的指针。

  • 在好的情况下,您的缓冲区包含在单个 MDL 中,因此 NdisGetDataBuffer 只是为您提供指向该 MDL 缓冲区的指针。
  • 在较慢的情况下,您的缓冲区跨越多个 MDL,因此 NdisGetDataBuffer 会小心地将有效负载的相关位复制到您提供的暂存缓冲区中。

如果您尝试检查多个字节,则后一种情况可能很繁琐。 如果您正在读取数据包的所有 1500 字节,则不能只在堆栈上分配 1500 字节(内核堆栈空间稀缺,与用户模式不同),因此您必须从池中分配它。 一旦弄清楚这一点,请注意,将所有 1500 字节的数据复制到每个数据包的暂存缓冲区中会减慢速度。 减速太多了吗? 这取决于您的需求。 如果您只是偶尔检查数据包,或者如果您将 LWF 部署在低吞吐量 NIC 上,则无关紧要。 如果你试图超过 1Gbps,你不应该存储这么多数据。

另请注意,如果您最终要修改数据包,则需要警惕 NdisGetDataBuffer。 它可以为您提供数据的副本(存储在本地暂存缓冲区中),因此如果您修改有效负载,这些更改实际上不会粘在数据包上。

如果您确实需要扩展到高吞吐量或修改有效负载怎么办? 然后你需要弄清楚如何操作 MDL 链。 起初这有点令人困惑,但花一点时间阅读文档并为自己画一些白板图。

我建议首先从了解 MDL 开始。 从网络的角度来看,MDL 只是保存 { char * buffer, size_t length } 以及指向下一个 MDL 的链接的一种奇特方式。

接下来,考虑 NB 的 DataOffset 和 DataLength。 这些在概念上将缓冲区边界从缓冲区的开头和结尾移入。 他们并不真正关心 MDL 边界——例如,您可以通过递减 DataLength 来减少数据包有效负载的长度,如果这意味着一个或多个 MDL 不再为数据包有效负载提供任何缓冲区空间,那么它不是大不了,他们只是被忽略了。

最后,在顶部添加 CurrentMdl 和 CurrentMdlOffset。 这些对于上述所有内容都是多余的,但它们的存在是为了(微基准)性能。 如果您正在阅读 NB,您甚至不需要考虑它们,但如果您正在编辑 NB 的大小,则需要更新它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM