我读到了WinPcap如何过滤数据包here并从GitHub(Microsoft / Windows-driver-samples)检查ndis/filter project。我提供了WinPcap页面的主要数据,因为它们与问题有关。
我的主要问题:如果NPF丢弃一个数据包(数据包),这意味着不会捕获数据包或者不会发送/接收数据包?例如(正如我所见):
接收数据包的情况相同(通过ReceiveNetBufferListsHandler and ReturnNetBufferListsHandler函数)。
我是否正确理解有机会通过NPF丢弃数据包,向/从网络发送/接收数据包,并将其从“捕获数据包列表”中删除?
如果是,如何正确实现丢包?
我没有找到通过SendNetBufferListsHandler / SendNetBufferListsCompleteHandler和ReceiveNetBufferListsHandler / ReturnNetBufferListsHandler函数丢弃数据包的代码示例。
如果NPF丢弃一个数据包(数据包),这意味着不会捕获数据包或者不会发送/接收数据包?
数据包将不会被捕获,但数据包仍将被传递到网络堆栈的其余部分。我想这是出于两个原因:
nmap项目有一个NPF分支,它将其实现为NDIS过滤器驱动程序。这种驱动程序能够阻止,延迟,重写或注入流量。因此,如果你有兴趣改变上面的哲学#1,你应该从nmap的fork而不是“官方”winpcap开始。
(而且,一般来说,我个人会建议使用nmap的fork,即使你不需要丢弃流量。过滤驱动程序会比将网络适配器置于二层环回模式的协议驱动程序快得多。)
一旦你看了nmap-npf,你就能从NDIS样本过滤器驱动程序中找到回调,比如FilterReceiveNetBufferLists。
丢包真的很容易;)但是有一些问题,所以让我们看看一些例子。
在传输路径上,我们有一个NBL的链表,我们希望将它分成两个列表,一个是丢弃,另一个是继续发送。单个NBL可以包含多个分组,但是保证每个分组具有相同的“流”(例如,TCP套接字)。所以通常你可以做出简化的假设,即NBL中的每个数据包总是以相同的方式处理:如果你想丢弃一个,你想要将它们全部丢弃。
如果这种假设不正确,即如果你确实想要从TCP套接字中选择性地丢弃一些数据包,而不是所有数据包,那么你需要做一些更复杂的事情。您不能直接从NET_BUFFER_LIST中删除单个NET_BUFFER;相反,您必须克隆NET_BUFFER_LIST并复制您要保留的NET_BUFFER。
由于这是一个免费的论坛,我只会给你一个简单和常见案例的例子;)
void
FilterSendNetBufferLists(NET_BUFFER_LIST *nblChain, ULONG sendFlags)
{
NET_BUFFER_LIST *drop = NULL;
NET_BUFFER_LIST *keep = NULL;
NET_BUFFER_LIST *next = NULL;
NET_BUFFER_LIST *nbl = NULL;
for (nbl = nblChain; nbl != NULL; nbl = next) {
next = nbl->Next;
// If the first NB in the NBL is drop-worthy, then all NBs are
if (MyShouldDropPacket(nbl->FirstNetBuffer)) {
nbl->Next = drop;
drop = nbl;
nbl->Status = NDIS_STATUS_FAILURE; // tell the protocol
} else {
nbl->Next = keep;
keep = nbl;
}
}
// Above would reverse the order of packets; let's undo that here.
keep = ReverseNblChain(keep);
. . . do something with the NBLs you want to keep. . .;
// Send the keepers down the stack to be transmitted by the NIC.
NdisFSendNetBufferLists(context, keep, portNumber, sendFlags);
// Return the dropped packets back up to whoever tried to send them.
NdisFSendCompleteNetBufferLists(context, drop, 0);
}
在接收路径上,您可以保证每个NET_BUFFER_LIST只有一个NET_BUFFER。 (NIC无法完全知道哪些数据包是同一个数据流的一部分,因此尚未进行任何分组。)所以这个小问题已经消失,但是有一个新问题:你必须检查NDIS_RECEIVE_FLAGS_RESOURCES标志。不检查此标志是在过滤器驱动程序中追逐错误的第一个原因,因此我必须对此做出重大决定。
void
FilterReceiveNetBufferLists(NET_BUFFER_LIST *nblChain, ULONG count, ULONG receiveFlags)
{
NET_BUFFER_LIST *drop = NULL;
NET_BUFFER_LIST *keep = NULL;
NET_BUFFER_LIST *next = NULL;
NET_BUFFER_LIST *nbl = NULL;
for (nbl = nblChain; nbl != NULL; nbl = next) {
next = nbl->Next;
// There's only one packet in the NBL
if (MyShouldDropPacket(nbl->FirstNetBuffer)) {
nbl->Next = drop;
drop = nbl;
count -= 1; // decrement the NumberOfNetBufferLists
} else {
nbl->Next = keep;
keep = nbl;
}
}
keep = ReverseNblChain(keep);
. . . do something with the NBLs you want to keep. . .;
// Pass the keepers up the stack to be processed by protocols.
NdisFIndicateReceiveNetBufferLists(context, keep, portNumber, count, receiveFlags);
// Checking this flag is critical; never ever call
// NdisFReturnNetBufferLists if the flag is set.
if (0 == (NDIS_RECEIVE_FLAGS_RESOURCES & receiveFlags)) {
NdisFReturnNetBufferLists(context, keep, 0);
}
}
请注意,我使用了一个名为ReverseNblChain
的辅助函数。逆转数据包的顺序在技术上是合法的,但它会破坏性能。当数据包通常按顺序到达时,TCPIP只能达到最佳性能。示例代码中的链接列表操作循环具有反转NBL列表的副作用,因此我们使用ReverseNblChain
撤消损坏。我们不需要反转丢弃链,因为没有人试图重新组合丢弃的数据包;你可以按任何顺序留下它们。
NET_BUFFER_LIST * ReverseNblChain(NET_BUFFER_LIST *nblChain)
{
NET_BUFFER_LIST *head = NULL;
NET_BUFFER_LIST *next = NULL;
NET_BUFFER_LIST *nbl = NULL;
for (nbl = nblChain; nbl != NULL; nbl = next) {
next = nbl->Next;
nbl->Next = head;
head = nbl;
}
return head;
}
最后,如果您将来读取这些内容,我建议您查找Microsoft名为nblutil.h
的示例头文件。 (我们还没有发表它,但我正在研究它。)它有一个非常好的例程,名为ndisClassifyNblChain
,几乎可以为你完成所有的工作。它是为高可扩展性而设计的,并且使用了一些技巧来获得更好的性能,而不是你已经发现的已经过时的StackOverflow答案。