Wireshark 是一件有趣的事情,因为它不一定会告诉你确切的真相。如果可能的话,我建议在另一台 PC 上运行 Wireshark,这将使您更清楚地了解实际放在电线上的内容。 (从最纯粹的角度来看:禁用其他 PC 的硬件卸载,尤其是 RSC,因此其他 PC 的 NIC 不会在您捕获数据包之前对其进行修改。)
旧版本的 Wireshark 有一个名为 NPF 的 NDIS5 协议驱动程序。此人位于所有过滤器驱动程序之上,因此他通常不会看到任何 Tx 流量。但作为对这种情况的特殊让步,NDIS 会将 Tx 路径循环回 Rx 路径(设置了NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET 标志),因此像 NPF 这样的旧驱动程序可以在其 Rx 路径中看到 Tx 数据包的副本。
最近,npcap 项目将旧的 NPF 驱动程序转换为名为 NPCAP 的 NDIS6 LWF。由于多种原因,此驱动程序要好得多,但要记住的一点是,作为过滤器驱动程序,它位于过滤器堆栈中的某个位置。如果它位于您的 LWF 之上,那么它将看不到您传输(或修改)的任何数据包。
检查 !ndiskd.miniport 以查看您的机器上的 wireshark 是什么样的:它是一个名为 NPF 的协议,还是有一个名为 NPCAP 的过滤器驱动程序。如果是后者,它是在您的过滤驱动程序之上还是之下?
无论如何,这就是说您不能完全信任与您正在测试的驱动程序在同一个盒子上的wireshark。在单独的机器上进行数据包捕获会更好也更容易。
至于您的代码,请确保您的 FilterSendNetBufferListsComplete 处理程序正在查看所有 NBL 并删除 NET_BUFFER_LIST::SourceHandle 等于您的OriginalNdisFilterHandle 的那些。这些应该被释放回 NdisFreeNetBufferList (或缓存以供以后重用,但 NDIS 已经完成了不错的缓存工作)。您可能已经拥有该代码,只是没有将其放入 pastebin。
我没有看到任何会导致 Tx 始终失败的情况。您确实需要跟踪过滤器的暂停状态,并在暂停时阻止(或排队)Tx 操作。所以你的 SendData 函数可以这样写:
NTSTATUS SendData(MY_FILTER *filter) {
if (!ExAcquireRundownProtection(&filter->PauseRundown)) {
return STATUS_NDIS_PAUSED;
}
. . . allocate and send NBL . . .;
return STATUS_SUCCESS;
}
void FilterSendNetBufferListsComplete(MY_FILTER *filter, NET_BUFFER_LIST *nblChain) {
for (auto nbl = nblChain; nbl; nbl = nbl->Next) {
if (nbl->SourceHandle == filter->NdisHandle) {
. . . detach NBL from chain . . .;
. . . free NBL back to NDIS . . .;
ExReleaseRundownProtection(&filter->PauseRundown);
}
}
}
void FilterPause(MY_FILTER *filter) {
ExWaitForRundownProtectionRelease(&filter->PauseRundown);
}
void FilterRestart(MY_FILTER *filter) {
ExReInitializeRundownProtection(&filter->PauseRundown);
}
如果你弄错了,那么有时 NDIS 会在你发送数据包时崩溃。如果您不幸在数据路径暂停时发送它们,一些数据包也会悄悄地无法传输。 (修复这个问题不会神奇地导致数据包总是成功传输——这只是意味着它不会再安静了:当 NIC 尚未准备好时尝试发送数据包时,您会看到 STATUS_NDIS_PAUSED 。 )