Using Custom Queues for

NET_BUFFER_LIST Management

Introduction

Often the task to be performed in a NDIS 6 FilterSendNetBufferLists callback involves sorting the singly-linked list of NBLs based on packet inspection. One issue in performing this task is how to keep track of the NBLs as you sort them in an efficient and simple way.

This topic expands on some structures and MACROs that are included in some of the Microsoft WDK NDIS 6 samples that may be helpful in managing NBLs as you sort them. The originals are found in the WDK "filter" and "e100bex" samples (now deprecated), with some "glorification" added by PCAUSA.

NBL_QUEUE_HEADER Structure

A simple header structure is used for custom queuing of NBLs:

typedef struct _NBL_QUEUE_HEADER
{
    PNET_BUFFER_LIST Head;
    PNET_BUFFER_LIST Tail;
} NBL_QUEUE_HEADER, PNBL_QUEUE_HEADER;

In reality this is just the head of a singly-linked list with an auxiliary field that tracks the NBL at the tail of the list.

 

Macros for NBL_QUEUE Management

A few MACROs are provided to simplify the queuing process. They include:

bulletINIT_NBL_QUEUE_HEADER - Initialize the queue header before use.
bulletINSERT_TAIL_NBL_QUEUE - Inserts a NBL at the tail of the queue.
bulletNBL_GET_QUEUE_HEAD - Fetches the first NBL in the queue.

There are a few others. These MACROs are defined on the NBL_QUEUE page.

 

Example of NBL_QUEUE Use

In a simple case there may be two operations to be performed:

bulletSend the NBL
bulletDrop the NBL

In this case simply define two queues in your FilterSendNetBufferLists callback:

NDF_NBL_QUEUE_HEADER NblSendQueue; // NBLs to passthrough to lower-level miniport

NDF_NBL_QUEUE_HEADER NblDropQueue; // NBLs to return to higher-level protocol by send completion

And initialize them before using them:

NDF_INIT_NBL_QUEUE_HEADER( &NblSendQueue );

NDF_INIT_NBL_QUEUE_HEADER( &NblDropQueue );

Early in the do{} loop when processing individual NBLs you need to fetch the next NBL (because you'll be yanking it out of the original list) and also zap the NBL next field (so queuing this one won't drag along those chained behind it...). Something like this:

// Locate the Next NetBufferList

pNextNetBufferList = NET_BUFFER_LIST_NEXT_NBL (pCurrentNetBufferList);

 

// Unlink NBLs Following The Original NBL

NET_BUFFER_LIST_NEXT_NBL(pCurrentNetBufferList) = NULL;

Now as you examine the current NBL (that's out of scope of this document...) you would decide somewhere whether to pass or block the packet:

 

if( BlockNbl )

{

    // Block This NetBufferList

    NDF_INSERT_TAIL_NBL_QUEUE( &NblDropQueue, pCurrentNetBufferList );

    goto MoveToNextNbl;

}

else

{

    // Passthrough Send This NetBufferList

    NDF_INSERT_TAIL_NBL_QUEUE( &NblSendQueue, pCurrentNetBufferList );

    goto MoveToNextNbl;

}

Where MoveToNextNbl is a label at the tail of your per-NBL do{} loop:

MoveToNextNbl:

    // Move to the Next NetBufferList

    pCurrentNetBufferList = pNextNetBufferList;

}

After you exit your per-NBL do{} loop you would process the two queues:

// Send Passthrough NetBufferLists

if( NDF_GET_NBL_QUEUE_HEAD( &NblSendQueue ) )

{

    NdisFSendNetBufferLists(

        pBindingContext->FilterHandle,

        NDF_GET_NBL_QUEUE_HEAD( &NblSendQueue ),

        PortNumber,

        SendFlags

        );

}

// Complete Blocked NetBufferLists

if( NDF_GET_NBL_QUEUE_HEAD( &NblDropQueue ) )

{

    NdisFSendNetBufferListsComplete(

        pBindingContext->FilterHandle,

        NDF_GET_NBL_QUEUE_HEAD( &NblDropQueue ),

        DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0

        );

}

Some PCAUSA drivers have many more queues. For example, an impairment generator might have:

NDF_NBL_QUEUE_HEADER NblSendQueue; // NBLs to passthrough to lower-level miniport

NDF_NBL_QUEUE_HEADER NblDropQueue; // NBLs to return to higher-level protocol by send completion

NDF_NBL_QUEUE_HEADER NblDelayQueue; // NBLs to delay before sending to lower-level miniport

NDF_NBL_QUEUE_HEADER NblMangleQueue; // NBLs to mangle before sending to lower-level miniport

I hope that this is helpful to some of you.

 

Topic Status

August 9, 2011 Information posted.
 

PCAUSA Home · Privacy Statement · Products · Ordering · Support · Utilities · Resources
Mailing Lists  · PCAUSA Newsletter · PCAUSA Discussion List
Rawether for Windows, Rawether .NET, WinDis 32 and NDIS Press are trademarks of Printing Communications Assoc., Inc. (PCAUSA)
Microsoft, MS, Windows, Windows Vista, Windows 95, Windows 98, Windows Millennium, Windows 2000, and Win32 are registered trademarks and Visual C++ and Windows NT are trademarks of the Microsoft Corporation.
Copyright © 1996-2012 Printing Communications Assoc., Inc. (PCAUSA)
Last modified: January 01, 2012