我正在开发一个NDIS过滤器驱动程序,并且在某些条件下(例如打开Wireshark或单击它的“接口列表”按钮),我永远不会调用它FilterReceiveNetBufferLists
(网络被阻止)。但是当我开始捕获时,FilterReceiveNetBufferLists
变得正常(网络恢复),这太奇怪了。
我发现当我在WinPcap驱动程序的OID发起位置(NPF_IoControl的BIOCQUERYOID& BIOCSETOID切换分支)中手动返回NDIS_STATUS_FAILURE
NdisFOidRequest
函数时,驱动程序将不会阻止网络(另外winpcap无法工作)。
NdisFOidRequest
来电是否有问题?
Packet.c中的DeviceIO例程,它发起了OID请求:
case BIOCQUERYOID:
case BIOCSETOID:
TRACE_MESSAGE(PACKET_DEBUG_LOUD, "BIOCSETOID - BIOCQUERYOID");
//
// gain ownership of the Ndis Handle
//
if (NPF_StartUsingBinding(Open) == FALSE)
{
//
// MAC unbindind or unbound
//
SET_FAILURE_INVALID_REQUEST();
break;
}
// Extract a request from the list of free ones
RequestListEntry = ExInterlockedRemoveHeadList(&Open->RequestList, &Open->RequestSpinLock);
if (RequestListEntry == NULL)
{
//
// Release ownership of the Ndis Handle
//
NPF_StopUsingBinding(Open);
SET_FAILURE_NOMEM();
break;
}
pRequest = CONTAINING_RECORD(RequestListEntry, INTERNAL_REQUEST, ListElement);
//
// See if it is an Ndis request
//
OidData = Irp->AssociatedIrp.SystemBuffer;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength == IrpSp->Parameters.DeviceIoControl.OutputBufferLength) &&
(IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(PACKET_OID_DATA)) &&
(IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(PACKET_OID_DATA) - 1 + OidData->Length))
{
TRACE_MESSAGE2(PACKET_DEBUG_LOUD, "BIOCSETOID|BIOCQUERYOID Request: Oid=%08lx, Length=%08lx", OidData->Oid, OidData->Length);
//
// The buffer is valid
//
NdisZeroMemory(&pRequest->Request, sizeof(NDIS_OID_REQUEST));
pRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST;
pRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1;
pRequest->Request.Header.Size = NDIS_SIZEOF_OID_REQUEST_REVISION_1;
if (FunctionCode == BIOCSETOID)
{
pRequest->Request.RequestType = NdisRequestSetInformation;
pRequest->Request.DATA.SET_INFORMATION.Oid = OidData->Oid;
pRequest->Request.DATA.SET_INFORMATION.InformationBuffer = OidData->Data;
pRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = OidData->Length;
}
else
{
pRequest->Request.RequestType = NdisRequestQueryInformation;
pRequest->Request.DATA.QUERY_INFORMATION.Oid = OidData->Oid;
pRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = OidData->Data;
pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = OidData->Length;
}
NdisResetEvent(&pRequest->InternalRequestCompletedEvent);
if (*((PVOID *) pRequest->Request.SourceReserved) != NULL)
{
*((PVOID *) pRequest->Request.SourceReserved) = NULL;
}
//
// submit the request
//
pRequest->Request.RequestId = (PVOID) NPF6X_REQUEST_ID;
ASSERT(Open->AdapterHandle != NULL);
Status = NdisFOidRequest(Open->AdapterHandle, &pRequest->Request);
//Status = NDIS_STATUS_FAILURE;
}
else
{
//
// Release ownership of the Ndis Handle
//
NPF_StopUsingBinding(Open);
//
// buffer too small
//
SET_FAILURE_BUFFER_SMALL();
break;
}
if (Status == NDIS_STATUS_PENDING)
{
NdisWaitEvent(&pRequest->InternalRequestCompletedEvent, 1000);
Status = pRequest->RequestStatus;
}
//
// Release ownership of the Ndis Handle
//
NPF_StopUsingBinding(Open);
//
// Complete the request
//
if (FunctionCode == BIOCSETOID)
{
OidData->Length = pRequest->Request.DATA.SET_INFORMATION.BytesRead;
TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "BIOCSETOID completed, BytesRead = %u", OidData->Length);
}
else
{
if (FunctionCode == BIOCQUERYOID)
{
OidData->Length = pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten;
if (Status == NDIS_STATUS_SUCCESS)
{
//
// check for the stupid bug of the Nortel driver ipsecw2k.sys v. 4.10.0.0 that doesn't set the BytesWritten correctly
// The driver is the one shipped with Nortel client Contivity VPN Client V04_65.18, and the MD5 for the buggy (unsigned) driver
// is 3c2ff8886976214959db7d7ffaefe724 *ipsecw2k.sys (there are multiple copies of this binary with the same exact version info!)
//
// The (certified) driver shipped with Nortel client Contivity VPN Client V04_65.320 doesn't seem affected by the bug.
//
if (pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten > pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength)
{
TRACE_MESSAGE2(PACKET_DEBUG_LOUD, "Bogus return from NdisRequest (query): Bytes Written (%u) > InfoBufferLength (%u)!!", pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten, pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength);
Status = NDIS_STATUS_INVALID_DATA;
}
}
TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "BIOCQUERYOID completed, BytesWritten = %u", OidData->Length);
}
}
ExInterlockedInsertTailList(&Open->RequestList, &pRequest->ListElement, &Open->RequestSpinLock);
if (Status == NDIS_STATUS_SUCCESS)
{
SET_RESULT_SUCCESS(sizeof(PACKET_OID_DATA) - 1 + OidData->Length);
}
else
{
SET_FAILURE_INVALID_REQUEST();
}
break;
三个过滤器OID例程:
_Use_decl_annotations_
NDIS_STATUS
NPF_OidRequest(
NDIS_HANDLE FilterModuleContext,
PNDIS_OID_REQUEST Request
)
{
POPEN_INSTANCE Open = (POPEN_INSTANCE) FilterModuleContext;
NDIS_STATUS Status;
PNDIS_OID_REQUEST ClonedRequest=NULL;
BOOLEAN bSubmitted = FALSE;
PFILTER_REQUEST_CONTEXT Context;
BOOLEAN bFalse = FALSE;
TRACE_ENTER();
do
{
Status = NdisAllocateCloneOidRequest(Open->AdapterHandle,
Request,
NPF6X_ALLOC_TAG,
&ClonedRequest);
if (Status != NDIS_STATUS_SUCCESS)
{
TRACE_MESSAGE(PACKET_DEBUG_LOUD, "FilerOidRequest: Cannot Clone Request\n");
break;
}
Context = (PFILTER_REQUEST_CONTEXT)(&ClonedRequest->SourceReserved[0]);
*Context = Request;
bSubmitted = TRUE;
//
// Use same request ID
//
ClonedRequest->RequestId = Request->RequestId;
Open->PendingOidRequest = ClonedRequest;
Status = NdisFOidRequest(Open->AdapterHandle, ClonedRequest);
if (Status != NDIS_STATUS_PENDING)
{
NPF_OidRequestComplete(Open, ClonedRequest, Status);
Status = NDIS_STATUS_PENDING;
}
}while (bFalse);
if (bSubmitted == FALSE)
{
switch(Request->RequestType)
{
case NdisRequestMethod:
Request->DATA.METHOD_INFORMATION.BytesRead = 0;
Request->DATA.METHOD_INFORMATION.BytesNeeded = 0;
Request->DATA.METHOD_INFORMATION.BytesWritten = 0;
break;
case NdisRequestSetInformation:
Request->DATA.SET_INFORMATION.BytesRead = 0;
Request->DATA.SET_INFORMATION.BytesNeeded = 0;
break;
case NdisRequestQueryInformation:
case NdisRequestQueryStatistics:
default:
Request->DATA.QUERY_INFORMATION.BytesWritten = 0;
Request->DATA.QUERY_INFORMATION.BytesNeeded = 0;
break;
}
}
TRACE_EXIT();
return Status;
}
//-------------------------------------------------------------------
_Use_decl_annotations_
VOID
NPF_CancelOidRequest(
NDIS_HANDLE FilterModuleContext,
PVOID RequestId
)
{
POPEN_INSTANCE Open = (POPEN_INSTANCE) FilterModuleContext;
PNDIS_OID_REQUEST Request = NULL;
PFILTER_REQUEST_CONTEXT Context;
PNDIS_OID_REQUEST OriginalRequest = NULL;
BOOLEAN bFalse = FALSE;
FILTER_ACQUIRE_LOCK(&Open->OIDLock, bFalse);
Request = Open->PendingOidRequest;
if (Request != NULL)
{
Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]);
OriginalRequest = (*Context);
}
if ((OriginalRequest != NULL) && (OriginalRequest->RequestId == RequestId))
{
FILTER_RELEASE_LOCK(&Open->OIDLock, bFalse);
NdisFCancelOidRequest(Open->AdapterHandle, RequestId);
}
else
{
FILTER_RELEASE_LOCK(&Open->OIDLock, bFalse);
}
}
//-------------------------------------------------------------------
_Use_decl_annotations_
VOID
NPF_OidRequestComplete(
NDIS_HANDLE FilterModuleContext,
PNDIS_OID_REQUEST Request,
NDIS_STATUS Status
)
{
POPEN_INSTANCE Open = (POPEN_INSTANCE) FilterModuleContext;
PNDIS_OID_REQUEST OriginalRequest;
PFILTER_REQUEST_CONTEXT Context;
BOOLEAN bFalse = FALSE;
TRACE_ENTER();
Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]);
OriginalRequest = (*Context);
//
// This is an internal request
//
if (OriginalRequest == NULL)
{
TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "Status= %p", Status);
NPF_InternalRequestComplete(Open, Request, Status);
TRACE_EXIT();
return;
}
FILTER_ACQUIRE_LOCK(&Open->OIDLock, bFalse);
ASSERT(Open->PendingOidRequest == Request);
Open->PendingOidRequest = NULL;
FILTER_RELEASE_LOCK(&Open->OIDLock, bFalse);
//
// Copy the information from the returned request to the original request
//
switch(Request->RequestType)
{
case NdisRequestMethod:
OriginalRequest->DATA.METHOD_INFORMATION.OutputBufferLength = Request->DATA.METHOD_INFORMATION.OutputBufferLength;
OriginalRequest->DATA.METHOD_INFORMATION.BytesRead = Request->DATA.METHOD_INFORMATION.BytesRead;
OriginalRequest->DATA.METHOD_INFORMATION.BytesNeeded = Request->DATA.METHOD_INFORMATION.BytesNeeded;
OriginalRequest->DATA.METHOD_INFORMATION.BytesWritten = Request->DATA.METHOD_INFORMATION.BytesWritten;
break;
case NdisRequestSetInformation:
OriginalRequest->DATA.SET_INFORMATION.BytesRead = Request->DATA.SET_INFORMATION.BytesRead;
OriginalRequest->DATA.SET_INFORMATION.BytesNeeded = Request->DATA.SET_INFORMATION.BytesNeeded;
break;
case NdisRequestQueryInformation:
case NdisRequestQueryStatistics:
default:
OriginalRequest->DATA.QUERY_INFORMATION.BytesWritten = Request->DATA.QUERY_INFORMATION.BytesWritten;
OriginalRequest->DATA.QUERY_INFORMATION.BytesNeeded = Request->DATA.QUERY_INFORMATION.BytesNeeded;
break;
}
(*Context) = NULL;
NdisFreeCloneOidRequest(Open->AdapterHandle, Request);
NdisFOidRequestComplete(Open->AdapterHandle, OriginalRequest, Status);
TRACE_EXIT();
}
答案 0 :(得分:5)
以下是我从杰弗里收到的邮件,我认为这是这个问题的最佳答案:)
对于LWF与协议,数据包过滤器的工作方式不同。我来给你一些背景知识。你已经知道了其中一些,我确定,但是查看基础知识总是有帮助的,所以我们可以确定我们都在同一页面上。 NDIS数据路径的组织方式如树:
数据包过滤发生在此堆栈中的两个位置:
(a)一次进入微型端口硬件,
(b)位于堆栈顶部,正好位于协议之下。
NDIS将分别跟踪每个协议的数据包过滤器,以提高效率。如果一个协议要求查看所有数据包(混杂模式),那么并非所有协议都必须对所有流量进行排序。实际上,系统中存在( P +1)不同的数据包过滤器,其中 P 是协议数量:
现在,如果有所有这些不同的数据包过滤器,OID_GEN_CURRENT_PACKET_FILTER实际上如何工作? NDIS所做的是NDIS跟踪每个协议的数据包过滤器,但也将过滤器合并在微型端口堆栈的顶部。因此假设protocol0请求A + B的数据包过滤器,protocol1请求C的数据包过滤器,protocol2请求B + D的数据包过滤器:
然后在堆栈的顶部,NDIS将数据包过滤器合并到A + B + C + D.这是从过滤器堆栈发送到最终端口的内容。
由于这种合并过程,无论协议2设置为哪种协议过滤器,协议2都不会影响其他协议。因此协议不必担心“共享”数据包过滤器。然而,对于LWF而言,情况并非如此。如果LWF1决定设置新的数据包过滤器,则不会合并:
在上图中,LWF1决定将数据包过滤器更改为C + E.这覆盖了协议的A + B + C + D数据包过滤器,这意味着标志A,B和D永远不会进入硬件。如果协议依赖于标志A,B或D,则协议的功能将被破坏。
这是设计 - LWF具有强大的功能,他们可以对堆栈做任何事情。它们被设计为有权否决所有其他协议的数据包过滤器。但在你的情况下,你不想搞乱其他协议;您希望过滤器对系统的其余部分影响最小。
所以你要做的是始终跟踪数据包过滤器是什么,并且永远不要从当前数据包过滤器中删除标志。这意味着您应该在过滤器附加时查询数据包过滤器,并在看到OID_GEN_CURRENT_PACKET_FILTER从上面下来时更新缓存值。
如果您的usermode应用程序需要的标志数多于当前数据包过滤器所具有的标志,则可以发出OID并添加其他标志。这意味着硬件的数据包过滤器将具有更多标志。但是没有协议的数据包过滤器会改变,所以协议仍然会看到相同的东西。
在上面的示例中,过滤器LWF1正在播放。尽管LWF1只关心标志E,但LWF1仍然传递了所有标志A,B,C和D,因为LWF1知道它上面的协议需要设置这些标志。
一旦你了解了管理数据包过滤器需要做些什么,管理这个的代码就不算太糟了:
始终从上述协议中跟踪最新的数据包过滤器。
永远不要让NIC看到包含比协议包过滤器少的标志的数据包过滤器。
根据需要添加您自己的标记。
好的,希望这可以让您了解数据包过滤器的内容以及如何管理它。接下来的问题是如何将“混杂模式”和“非混杂模式”映射到实际标志中?让我们仔细定义这两种模式:
非混杂模式:捕获工具只能看到操作系统通常会收到的接收流量。如果硬件过滤掉流量,那么我们不希望看到该流量。用户希望诊断处于正常状态的本地操作系统。
混杂模式:为捕获工具提供尽可能多的接收数据包 - 理想情况下,每一位都在线路上传输。数据包是否发往本地主机并不重要。用户想要诊断网络,因此希望看到网络上发生的一切。
我认为当你以这种方式看待它时,数据包过滤器标志的后果相当简单。对于非混杂模式,请勿更改数据包筛选器。只需让硬件数据包过滤器成为操作系统想要的任何东西。然后对于混杂模式,添加NDIS_PACKET_TYPE_PROMISCUOUS标志,NIC硬件将为您提供所有可能的功能。
因此,如果对于LWF来说这很简单,为什么旧的基于协议的NPF驱动程序需要更多的标志呢?旧的基于协议的驱动程序有几个问题:
无法将“非混杂模式”完全正确
无法轻松捕获其他协议的发送数据包
NPF协议的第一个问题是它无法正确地实现我们对“非混杂模式”的定义。如果NPF-the-protocol希望看到接收流量就像操作系统看到的那样,那么它应该使用什么包过滤器?如果它将数据包过滤器设置为零,则NPF将不会看到任何流量。因此,NPF可以设置定向|广播|组播的数据包过滤器。但这只是对TCPIP和其他协议设置的假设。如果TCPIP决定设置一个Promiscuous标志(某些套接字标志导致这种情况发生),那么NPF实际上会看到的数据包少于TCPIP看到的数据包,这是错误的。但是如果NPF设置了Promiscuous标志,那么它将看到比TCPIP看到的更多的流量,这也是错误的。因此,捕获协议很难决定设置哪些标志,以便它看到与操作系统其余部分完全相同的数据包。 LWF没有这个问题,因为LWF在合并所有协议的过滤器后会看到组合的OID。
NPF协议的第二个问题是它需要环回模式来捕获已发送的数据包。 LWF不需要环回 - 事实上,它将是有害的。让我们使用相同的图表来了解原因。这是NPF以混杂模式捕获接收路径:
现在让我们看看收到单播数据包时会发生什么:
由于数据包与硬件的过滤器匹配,因此数据包会出现在堆栈中。然后,当数据包到达协议层时,NDIS会将数据包提供给两个协议tcpip和npf,因为两个协议的数据包过滤器都与数据包匹配。这样运作得很好。
但是现在发送路径很棘手:
tcpip发送了一个数据包,但是npf从来没有机会看到它!为了解决这个问题,NDIS增加了“环回”数据包过滤器标志的概念。这个标志有点特殊,因为它不会去硬件。相反,环回数据包过滤器告诉NDIS将所有发送流量反弹回接收路径,以便像npf这样的诊断工具可以看到数据包。它看起来像这样:
现在环回路径实际上只用于诊断工具,因此我们没有花太多时间来优化它。并且,因为它意味着所有发送数据包都在堆栈中传输两次(一次用于正常发送路径,再次在接收路径中),它至少有两倍的CPU成本。这就是为什么我说NDIS LWF能够以比协议更高的吞吐量捕获,因为LWF不需要环回路径。
为什么不呢?为什么LWF不需要环回?好吧,如果你回过头来看看最后几张图,你会看到我们所有的LWF都看到了所有流量 - 包括发送和接收 - 没有任何环回。因此,LWF满足了查看所有流量的要求,而无需为环回而烦恼。这就是为什么LWF通常不应该设置任何环回标志。
好的,那封电子邮件比我想要的要长,但我希望能够解决围绕数据包过滤器,环回路径以及LWF与协议的不同之处的一些问题。如果有任何不清楚的地方,或者图表没有通过,请告诉我。