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:
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!