c - NDIS筛选器驱动程序的FilterReceiveNetBufferLists处理程序未调用

标签 c windows driver function-calls ndis

我正在开发NDIS筛选器驱动程序,并且发现在一定条件下(例如,打开Wireshark或单击它的“接口(interface)列表”按钮),永远不会调用它的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();
}

最佳答案

以下是我从杰弗里收到的邮件,我认为这是此问题的最佳答案:)

数据包过滤器对LWF和协议(protocol)的工作方式不同。让我给你一些背景。我敢肯定,您已经了解了其中的一些内容,但是复习基础知识总是有帮助的,因此我们可以确保我们都在同一页面上。 NDIS数据路径的结构像一棵树:

数据包过滤发生在此堆栈中的两个位置:

(a)在微型端口硬件中一次,并且

(b)在协议(protocol)栈的顶部,紧靠协议(protocol)的下方。

NDIS将分别跟踪每个协议(protocol)的数据包筛选器,以提高效率。如果一种协议(protocol)要求查看所有数据包(混杂模式),则并非所有协议(protocol)都必须对所有流量进行分类。因此,实际上,系统中有( P +1)个不同的数据包筛选器,其中 P 是协议(protocol)的数量:

现在,如果所有这些包过滤器都不同,那么OID_GEN_CURRENT_PACKET_FILTER实际如何工作? NDIS的工作是NDIS跟踪每个协议(protocol)的数据包筛选器,但也将筛选器合并到微型端口堆栈的顶部。因此,假设协议(protocol)0请求A + B的数据包过滤器,而协议(protocol)1请求C的数据包过滤器,而协议(protocol)2请求B + D的数据包过滤器:

然后,在堆栈的顶部,NDIS将数据包筛选器合并到A + B + C + D。这就是从过滤器堆栈发送到最终到微型端口的内容。

由于此合并过程,无论将什么协议(protocol)2设置为其包过滤器,协议(protocol)2都不会影响其他协议(protocol)。因此,协议(protocol)不必担心“共享”数据包过滤器。但是,LWF并非如此。如果LWF1决定设置新的数据包筛选器,则不会合并:

在上图中,LWF1决定将数据包过滤器更改为C + E。这会覆盖协议(protocol)的A + B + C + D数据包过滤器,这意味着标志A,B和D永远不会进入硬件。如果协议(protocol)依赖于标志A,B或D,则协议(protocol)的功能将被破坏。

这是设计使然– LWF具有强大的功能,并且可以对堆栈执行任何操作。它们被设计为可以否决所有其他协议(protocol)的数据包过滤器。但是,就您而言,您不想与其他协议(protocol)混为一谈;您希望过滤器对系统的其余部分影响最小。

因此,您要做的是始终跟踪数据包过滤器是什么,而不要从当前数据包过滤器中删除标志。这意味着您应该在附加过滤器时查询数据包过滤器,并在看到OID_GEN_CURRENT_PACKET_FILTER从上方下降时更新缓存的值。

如果您的usermode应用程序需要的标志比当前数据包筛选器需要的标志更多,则可以发出OID并添加其他标志。这意味着硬件的数据包过滤器将具有更多标志。但是协议(protocol)的数据包过滤器不会更改,因此协议(protocol)仍会看到相同的内容。

在上面的示例中,过滤器LWF1运行良好。即使LWF1只关心标志E,LWF1仍然向下传递所有标志A,B,C和D,因为LWF1知道它上面的协议(protocol)希望设置这些标志。

一旦您了解了管理数据包过滤器需要做什么的想法,用于管理此问题的代码就不会太糟糕:

  • 始终跟踪上述协议(protocol)中的最新数据包筛选器。
  • 永远不要让NIC看到一个包过滤器,其标志少于协议(protocol)的包过滤器。
  • 根据需要添加自己的标志。

  • 好的,希望可以使您对什么是数据包过滤器以及如何对其进行管理有所了解。下一个问题是如何将“混杂模式”和“非混杂模式”映射到实际标志中?让我们仔细定义这两种模式:

    非混杂模式:捕获工具仅查看操作系统通常已接收的接收流量。如果硬件可以过滤流量,那么我们就不想看到这些流量。用户想要诊断处于正常状态的本地操作系统。

    混杂模式:为捕获工具提供尽可能多的接收数据包-理想情况下是通过网络传输的每一位。数据包是否发往本地主机都没有关系。用户想要诊断网络,因此想查看网络上发生的所有事情。

    我认为,以这种方式查看时,数据包过滤器标志的后果非常简单。对于非混杂模式,请勿更改数据包过滤器。只要让硬件数据包过滤器成为操作系统想要的形式即可。然后对于混杂模式,添加NDIS_PACKET_TYPE_PROMISCUOUS标志,然后NIC硬件将为您提供可能的一切。

    因此,对于LWF而言,如此简单,为什么旧​​的基于协议(protocol)的NPF驱动程序需要更多的标志?旧的基于协议(protocol)的驱动程序有两个问题:
  • 无法完全正确地实现“非混杂模式”
  • 无法轻松捕获其他协议(protocol)的发送数据包

  • NPF协议(protocol)的第一个问题是,它不能轻易正确地实现我们对“非混杂模式”的定义。如果NPF协议(protocol)希望像OS一样看到接收流量,那么应该使用什么数据包过滤器?如果将数据包过滤器设置为零,则NPF将看不到任何流量。因此,NPF可以设置Directed | Broadcast | Multicast的包过滤器。但这只是对TCPIP和其他协议(protocol)正在设置的假设。如果TCPIP决定设置混杂标志(某些套接字标志会导致这种情况发生),则NPF实际上看到的数据包少于TCPIP看到的数据包,这是错误的。但是,如果NPF设置了Promiscuous标志,则它将看到比TCPIP看到更多的流量,这也是错误的。因此,捕获协议(protocol)很难决定要设置哪些标志,以使其能够看到与操作系统其余部分完全相同的数据包。 LWF没有这个问题,因为LWF在合并所有协议(protocol)的过滤器后才能看到合并的OID。

    NPF协议(protocol)的第二个问题是它需要环回模式来捕获发送的数据包。 LWF不需要环回-实际上,这将是有害的。让我们使用同一张图查看原因。这是NPF以混杂模式捕获的接收路径:

    现在,我们来看看收到单播数据包时会发生什么:

    由于数据包与硬件过滤器匹配,因此数据包进入堆栈。然后,当数据包到达协议(protocol)层时,NDIS会将数据包同时提供给tcpip和npf这两个协议(protocol),因为这两个协议(protocol)的数据包过滤器都匹配该数据包。这样就足够好了。

    但是现在发送路径很棘手:

    tcpip发送了一个数据包,但是npf从来没有机会看到它!为了解决此问题,NDIS添加了“回送”包过滤器标志的概念。该标志有点特殊,因为它不会传递给硬件。取而代之的是,回送数据包过滤器告诉NDIS将所有发送流量反弹回接收路径,以便诸如npf之类的诊断工具可以看到数据包。看起来像这样:

    现在,环回路径实际上仅用于诊断工具,因此我们没有花太多时间对其进行优化。而且,由于这意味着所有发送数据包在堆栈中传输两次(一次是正常发送路径,另一次是接收路径),因此CPU成本至少翻了一番。这就是为什么我说NDIS LWF能够以比协议(protocol)更高的吞吐量捕获的原因,因为LWF不需要环回路径。

    为什么不? LWF为什么不需要环回?好吧,如果您回头看一下最后几张图,您会发现我们所有的LWF都看到了所有流量-包括发送和接收-没有任何环回。因此,LWF可以满足查看所有流量的要求,而无需打扰环回。这就是LWF通常不应设置任何环回标志的原因。

    好的,那封电子邮件比我想要的要长,但是我希望可以解决有关数据包过滤器,环回路径以及LWF与协议(protocol)有何不同的一些问题。如果有任何不清楚的地方,或者没有通过图表,请告诉我。

    关于c - NDIS筛选器驱动程序的FilterReceiveNetBufferLists处理程序未调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18257048/

    相关文章:

    c - 如何正确使用 lseek() 来扩展文件大小?

    c++ - array[100] = {0} 如何将整个数组设置为 0?

    c - exec ftp -vn 系统返回错误

    c - 在 Windows 7 中最大化控制台窗口

    c - Hook - 热修补

    1Mbaud 上的 Linux FTDI USB 到串行溢出错误 FTDI_RS_OE

    android - 如何从 ubuntu 12.04 操作系统在真实设备中运行 Android 应用程序?

    c - linux和c中两个不同独立程序之间的共享内存页

    c++ - 为什么 _beginthreadex 因 ERROR_INVALID_ACCESS 而失败?

    java - 找不到适合 jdbc :postgresql://192. 168.1.8:5432/NexentaSearch 的驱动程序