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);
}
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:
RegistryError
label should callNdisFDeregisterFilterDriver
, since the filter wouldn't have been registered with NDIS yet.getAdaptersList
andgetTcpBindings
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, usingNdisFRestartFilter
andNdisSetOptionalHandlers
.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.