Question

everybody. I have ported the famous packet capture software WinPcap from the NDIS 5 protocol to an NDIS 6 LWF. Everything is OK under Win7. However, the FilterAttach routine is never called under Win8. I found NdisFRegisterFilterDriver invoke in DriverEntry returns NDIS_STATUS_SUCCESS, this is so strange. Can anyone help me? thx!

Here's the code of DriverEntry

_Use_decl_annotations_
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    NDIS_FILTER_DRIVER_CHARACTERISTICS FChars;
    NTSTATUS Status = STATUS_SUCCESS;
//  NDIS_STRING FriendlyName = NDIS_STRING_CONST("WinPcap NDIS LightWeight Filter");
//  NDIS_STRING UniqueName   = NDIS_STRING_CONST("{5cbf81bd-5055-47cd-9055-a76b2b4e2637}"); //unique name, quid name
//  NDIS_STRING ServiceName = NDIS_STRING_CONST("npf6x"); //this to match the service name in the INF
    NDIS_STRING FriendlyName = RTL_CONSTANT_STRING(L"WinPcap NDIS LightWeight Filter");
    NDIS_STRING UniqueName   = RTL_CONSTANT_STRING(L"{5cbf81bd-5055-47cd-9055-a76b2b4e2637}"); //unique name, quid name
    NDIS_STRING ServiceName = RTL_CONSTANT_STRING(L"npf6x"); //this to match the service name in the INF
    WCHAR* bindT;
    PKEY_VALUE_PARTIAL_INFORMATION tcpBindingsP;
    UNICODE_STRING macName;
    ULONG OsMajorVersion, OsMinorVersion;

    TRACE_ENTER();

    UNREFERENCED_PARAMETER(RegistryPath);

    FilterDriverObject = DriverObject;

    //
    // Get OS version and store it in a global variable. 
    //
    // Note: both RtlGetVersion() and PsGetVersion() are documented to always return success.
    //
    //  OsVersion.dwOSVersionInfoSize = sizeof(OsVersion);
    //  RtlGetVersion(&OsVersion);
    //
    PsGetVersion(&OsMajorVersion, &OsMinorVersion, NULL, NULL);
    TRACE_MESSAGE2(PACKET_DEBUG_INIT, "OS Version: %d.%d\n", OsMajorVersion, OsMinorVersion);


    NdisInitUnicodeString(&g_NPF_Prefix, g_NPF_PrefixBuffer);

    //
    // Get number of CPUs and save it
    //
#ifdef NDIS620
    g_NCpu = NdisGroupMaxProcessorCount(ALL_PROCESSOR_GROUPS);
#else
    g_NCpu = NdisSystemProcessorCount();
#endif

    //
    // TODO: Most handlers are optional, however, this sample includes them
    // all for illustrative purposes.  If you do not need a particular 
    // handler, set it to NULL and NDIS will more efficiently pass the
    // operation through on your behalf.
    //

    //
    // Register as a service with NDIS
    //
    NdisZeroMemory(&FChars, sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS));
    FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
    FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS);
#if NDIS_SUPPORT_NDIS61
    FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_2;
#else
    FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1;
#endif

    FChars.MajorNdisVersion = NDIS_FILTER_MAJOR_VERSION;
    FChars.MinorNdisVersion = NDIS_FILTER_MINOR_VERSION;
    FChars.MajorDriverVersion = 1;
    FChars.MinorDriverVersion = 0;
    FChars.Flags = 0;

    FChars.FriendlyName = FriendlyName;
    FChars.UniqueName = UniqueName;
    FChars.ServiceName = ServiceName;

    FChars.SetOptionsHandler = NPF_RegisterOptions;
    FChars.AttachHandler = NPF_Attach;
    FChars.DetachHandler = NPF_Detach;
    FChars.RestartHandler = NPF_Restart;
    FChars.PauseHandler = NPF_Pause;
    FChars.SetFilterModuleOptionsHandler = NPF_SetModuleOptions;
    FChars.OidRequestHandler = NPF_OidRequest;
    FChars.OidRequestCompleteHandler = NPF_OidRequestComplete;
    FChars.CancelOidRequestHandler = NPF_CancelOidRequest;

    FChars.SendNetBufferListsHandler = NPF_SendEx;
    FChars.ReturnNetBufferListsHandler = NPF_ReturnEx;
    FChars.SendNetBufferListsCompleteHandler = NPF_SendCompleteEx;
    FChars.ReceiveNetBufferListsHandler = NPF_TapEx;
    FChars.DevicePnPEventNotifyHandler = NPF_DevicePnPEventNotify;
    FChars.NetPnPEventHandler = NPF_NetPnPEvent;
    FChars.StatusHandler = NPF_Status;
    FChars.CancelSendNetBufferListsHandler = NPF_CancelSendNetBufferLists;

    DriverObject->DriverUnload = NPF_Unload;

    //
    // Initialize spin locks
    //
    //NdisAllocateSpinLock(&FilterListLock);

    //InitializeListHead(&FilterModuleList);


    // 
    // Standard device driver entry points stuff.
    //
    DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_OpenAdapter;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = NPF_CloseAdapter;
    DriverObject->MajorFunction[IRP_MJ_CLEANUP] = NPF_Cleanup; 
    DriverObject->MajorFunction[IRP_MJ_READ] = NPF_Read;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = NPF_Write;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NPF_IoControl;

    bindP = getAdaptersList();

    if (bindP == NULL)
    {
        TRACE_MESSAGE(PACKET_DEBUG_INIT, "Adapters not found in the registry, try to copy the bindings of TCP-IP.");

        tcpBindingsP = getTcpBindings();

        if (tcpBindingsP == NULL)
        {
            TRACE_MESSAGE(PACKET_DEBUG_INIT, "TCP-IP not found, quitting.");
            goto RegistryError;
        }

        bindP = (WCHAR *)tcpBindingsP;
        bindT = (WCHAR *)(tcpBindingsP->Data);
    }
    else
    {
        bindT = bindP;
    }

    for (; *bindT != UNICODE_NULL; bindT += (macName.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR))
    {
        RtlInitUnicodeString(&macName, bindT);
        NPF_CreateDevice(DriverObject, &macName);
    }


    Status = NdisFRegisterFilterDriver(DriverObject,
        (NDIS_HANDLE) FilterDriverObject,
        &FChars,
        &FilterDriverHandle);
    if (Status != NDIS_STATUS_SUCCESS)
    {
        TRACE_MESSAGE(PACKET_DEBUG_INIT, "Failed to register filter with NDIS.");
        TRACE_EXIT();
        return Status;
    }


    TRACE_EXIT();
    return STATUS_SUCCESS;

    RegistryError : NdisFDeregisterFilterDriver(FilterDriverHandle);

    Status = STATUS_UNSUCCESSFUL;
    TRACE_EXIT();
    return(Status);
}
Was it helpful?

Solution

Why your FilterAttach routine isn't called — I don't know. I can't think of any significant differences between Windows 7 and Windows 8. (On the other hand, Windows 8.1 Preview does have some substantial binding changes.)

Check if the filter is bound in usermode. Use Get-NetAdapterBinding from powershell to ensure that there is a binding from the NIC to your filter, and that the binding is enabled.

Verify the miniports are started normally. Use !ndiskd.miniport to see if the miniports are otherwise bound normally. Check if your filter is listed on the miniport's list of filter bindings.

A couple unrelated notes:

  • I don't think the RegistryError label should call NdisFDeregisterFilterDriver, since the filter wouldn't have been registered with NDIS yet.
  • Code like getAdaptersList and getTcpBindings sounds scary, but I suppose that's probably pre-existing code from the old driver. Note that we don't support rummaging around in the registry, and would rather you use INetCfg in usermode to discover adapters. For a LWF, we prefer that your filter is always bound to all capable adapters. If perf is a concern, the LWF can optionally insert/remove itself dynamically into the datapath as needed, using NdisFRestartFilter and NdisSetOptionalHandlers.

OTHER TIPS

Thanks for the additional diagnostic information that you gave in the comments. Your description of the problem has allowed me to isolate this as a bug in Windows.

The problem occurs when first installing a filter. NDIS may, in some cases, ignore the notification that the bindings on a miniport have changed, if the filter driver hasn't started in kernelmode yet.

There are several workarounds - as you've noted, one workaround is to change the StartType of the filter. Another workaround would be to use INetCfg to disable and re-enable the filter bindings to each miniport after installation. You can also disable and re-enable the miniports, or reboot the computer, although those are rather disruptive workarounds.

✓ This bug does not affect Windows 7.
✗ This bug affects Windows 8 and Windows Server 2012.
✓ This bug does not affect Windows 8.1 and Windows Server 2012 R2. (I unknowingly fixed this bug while cleaning up some code in NDIS.)

If you cannot wait for the free Windows 8.1 update to roll out to all Windows 8 machines, you can contact Microsoft WDK support. Please reference WindowsSE:452306 so that they can find my notes on the issue, or have them contact me internally.

//
// DisableEnableBindings
//
// Purpose:  This code can be used to quickly disable/enable all bindings to a particular
// NDIS protocol or filter.
//
// Usage:  Run this and provide the name of a NetCfg component.  For example, "ms_pacer".

#include <Windows.h>
#include <netcfgx.h>

#include <atlbase.h>
#include <atlcom.h>

#include <stdio.h>
#include <vector>

#define MY_APP_NAME L"DisableEnableBindings test app"

bool RestartAllBindings(INetCfg *netcfg, PCWSTR name)
{
    HRESULT hr;
    CComPtr<INetCfgComponent> comp;
    CComPtr<INetCfgComponentBindings> bindings;

    hr = netcfg->FindComponent(name, &comp);
    if (FAILED(hr))
    {
        wprintf(L"INetCfg::FindComponent 0x%08x\n", hr);
        return false;
    }

    hr = comp.QueryInterface(&bindings);
    if (FAILED(hr))
    {
        wprintf(L"QueryInterface(INetCfgComponentBindings) 0x%08x\n", hr);
        return false;
    }

    CComPtr<IEnumNetCfgBindingPath> enumerator;
    hr = bindings->EnumBindingPaths(EBP_BELOW, &enumerator);
    if (FAILED(hr))
    {
        wprintf(L"INetCfgComponentBindings::EnumBindingPaths 0x%08x\n", hr);
        return false;
    }

    // Loop over all bindings that involve this component
    while (true)
    {
        CComPtr<INetCfgBindingPath> path;
        hr = enumerator->Next(1, &path, nullptr);
        if (hr == S_FALSE)
        {
            // Reached end of list; quit.
            break;
        }
        if (FAILED(hr))
        {
            wprintf(L"IEnumNetCfgBindingPath::Next 0x%08x\n", hr);
            return false;
        }

        PWSTR token = nullptr;
        hr = path->GetPathToken(&token);
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::GetPathToken 0x%08x\n", hr);
            continue;
        }

        wprintf(L"Found binding %s\n", token);
        CoTaskMemFree(token);

        hr = path->IsEnabled();
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::IsEnabled 0x%08x\n", hr);
            continue;
        }

        if (S_FALSE == hr)
        {
            wprintf(L"\tPath is already disabled.  Skipping over it.\n");
            continue;
        }

        // Diable

        hr = path->Enable(FALSE);
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::Enable(FALSE) 0x%8x\n", hr);
            continue;
        }

        hr = netcfg->Apply();
        if (FAILED(hr))
        {
            wprintf(L"INetCfg::Apply 0x%08x\n", hr);
            return false;
        }

        wprintf(L"\tPath disabled\n");

        // Enable

        hr = path->Enable(TRUE);
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::Enable(TRUE) 0x%8x\n", hr);
            return false;
        }

        hr = netcfg->Apply();
        if (FAILED(hr))
        {
            wprintf(L"INetCfg::Apply 0x%08x\n", hr);
            return false;
        }

        wprintf(L"\tPath enabled\n");
    }



    return true;
}

bool ConnectToNetCfg(PCWSTR name)
{
    HRESULT hr;
    CComPtr<INetCfg> netcfg;
    CComPtr<INetCfgLock> lock;

    // Before we can get started, we need to do some initialization work.

    hr = netcfg.CoCreateInstance(CLSID_CNetCfg);
    if (FAILED(hr))
    {
        wprintf(L"CoCreateInstance(CLSID_CNetCfg 0x%08x\n", hr);
        return false;
    }

    hr = netcfg.QueryInterface(&lock);
    if (FAILED(hr))
    {
        wprintf(L"QueryInterface(INetCfgLock) 0x%08x\n", hr);
        return false;
    }

    // Note that this call can block.
    hr = lock->AcquireWriteLock(INFINITE, MY_APP_NAME, nullptr);
    if (FAILED(hr))
    {
        wprintf(L"INetCfgLock::AcquireWriteLock 0x%08x\n", hr);
        return false;
    }

    hr = netcfg->Initialize(nullptr);
    if (FAILED(hr))
    {
        wprintf(L"INetCfg::Initialize 0x%08x\n", hr);
        return false;
    }

    bool ok = RestartAllBindings(netcfg.p, name);

    hr = netcfg->Uninitialize();
    if (FAILED(hr))
    {
        wprintf(L"INetCfg::Uninitialize 0x%08x\n", hr);
    }

    hr = lock->ReleaseWriteLock();
    if (FAILED(hr))
    {
        wprintf(L"INetCfgLock::ReleaseWriteLock 0x%08x\n", hr);
    }

    return ok;
}

int wmain(int argc, wchar_t **argv)
{
    if (argc != 2)
    {
        wprintf(L"Usage: DisableEnableBindings <componentId>\n");
        return 2;
    }

    PCWSTR name = argv[1];

    CComPtr<INetCfg> netcfg;
    CComPtr<INetCfgLock> lock;

    HRESULT hr;

    hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
    {
        wprintf(L"CoInitializeEx 0x%08x\n", hr);
        return 1;
    }

    bool ok = ConnectToNetCfg(name);

    CoUninitialize();

    wprintf(ok ? L"Succeeded.\n" : L"Failed.\n");

    return ok ? 0 : 1;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top