NdisFWhat?  Your secret decoder ring to NDIS functions

The first time you come across NDIS, you might find yourself lost in the enormous number of NDIS APIs, OIDs, status codes, and data structures.  What’s the difference between NdisMIndicateStatus and NdisFIndicateStatus?  Fortunately, NDIS has naming conventions that make it a little easier to organize the APIs.  Let’s take a look.

NDIS.SYS exports approximately 500 APIs on Windows 8.1.  (Yes, five hundred!)  Of those, all but three start with the name “Ndis”.  So that’s our first convention: NDIS APIs are almost always named NdisSomething.  That might seem obvious, but there’s a very subtle piece of information encoded in that name: NDIS’s internal routines are prefixed with “ndis”.  See the difference?  Upper-case N means it’s an exported API, and lower-case n means it’s an internal API.

Why should you care about NDIS’s internal routines?  Well most of the time, you don’t.  But when you start debugging your driver, you’ll inevitably be looking at some callstacks with NDIS routines (both exported and internal).  If you don’t know what the callstack is doing, you might get some clues by looking at related MSDN documentation.  Try looking at this callstack and see if you can guess which API is most likely to be documented on MSDN:

00 tcpip!FlReceiveNetBufferListChain
01 ndis!ndisMIndicateNetBufferListsToOpen
02 ndis!ndisIndicateSortedNetBufferLists
03 ndis!ndisMDispatchReceiveNetBufferListsInternal
04 ndis!ndisMTopReceiveNetBufferLists
05 ndis!ndisCallReceiveHandler
06 ndis!ndisIterativeDPInvokeHandlerOnTracker
07 ndis!ndisInvokeNextReceiveHandler
08 ndis!ndisMIndicateReceiveNetBufferListsInternal
09 ndis!NdisMIndicateReceiveNetBufferLists
0a netvsc!ReceivePacketMessage

That’s not all; it takes more than an N to spell Naming convention.  Following the “Ndis” prefix there is often a code indicating which type of driver is associated with the API.  Here’s a reference table of the codes:

Code Purpose
M Miniport drivers
F Filter drivers
IM Intermediate drivers
If Network interface providers
Co CoNDIS drivers
Cl CoNDIS call clients
Cm CoNDIS call managers
MCo CoNDIS miniport drivers
MCm CoNDIS integrated call managers

Functions that don’t have a code are meant to be called from multiple types of drivers, or from protocol drivers.  (For some reason, protocol drivers never earned their own code.  I’m not sure why, but who am I to argue with two decades of tradition?)

So now without even consulting the documentation, you can guess that NdisMSetTimer is for miniport drivers, while NdisIMNotifyPnPEvent is for intermediate drivers.  And I bet you can answer the question at the start of this article: what is the difference between NdisMIndicateStatus and NdisFIndicateStatus?

Enough trivia games; let’s return to practical applications of this knowledge.  Suppose you are debugging a callstack that looks like this

00 DriverY+0x22af
01 ndis!ndisMSendNBLToMiniport+0xb1
02 ndis!NdisFSendNetBufferLists+0x64
03 DriverX+0x1b6d
04 ndis!NdisFSendNetBufferLists+0x64
05 pacer!PcFilterSendNetBufferLists+0x9d
06 ndis!ndisSendNBLToFilter+0x69
07 ndis!NdisSendNetBufferLists+0x85
08 tcpip!FlpSendPacketsHelper+0x675

Although you might not be familiar with DriverX or DriverY, you can use NDIS naming conventions to guess their roles.  At frame 03, DriverX calls an NdisFXxx function in frame 02.  Therefore, DriverX is a filter driver.  You can also see an ndisMXxx internal NDIS function at frame 01, so you know that NDIS has finished calling all the filters, and has moved on to the miniport before calling DriverY in frame 00.  So it’s reasonable to conclude that DriverY is a miniport driver.

Another very practical application: knowing the codes will help you narrow down the APIs that you need to worry about when you’re writing code.  If you’re writing a lightweight filter driver, you can safely ignore the approximately 130 APIs that are exclusively for miniports (NdisMSomething).  That makes it a bit easier to find the API you’re looking for.

Now you know the ABC’s of NDIS!