Problems with NdisFSendNetBufferLists

Hello,

I write a Filter Driver for our GigE Vision Cameras with NDIS Version 6.30. The FilterClass is set to custom:
HKR, Ndi,FilterClass,custom
because we have Bandwidth above 5 Gbit/s and want to exclude the Firewall/Antivirus-Filter-Driver from the Data.

Receiving works till now like charme, but if the background Image is changing I got packet loses.

My Problem is the sending side.

I send the low count of Pakets over iocontrol (a complete Networkframe including Ethernet Frame is send by the user Application).
It works at the beginning, but after a unspecified time it stops working.
I debugged that I call the NdisFSendNetBufferLists but the Callback Func FilterSendNetBufferListsComplete is never called. Only a restart of the computer fix this problem.
Uninstalling ends in hang up, because of waiting for the missing Packets.

Code:

BOOLEAN SendRawPacket(PMS_FILTER aFilter, PVOID aPacketPtr, LONG aPacketByteSize,PLONG Status)
{
InterlockedIncrement(&SendActive); // a global Variable for checking if there an Sendprocess active

BOOLEAN res = FALSE;
PMDL mdl = NULL;
PSENDBUF_MEM iSendBuf = NULL;

do
{
    if (SendTerminate)
    {
        *Status = 11000;
        InterlockedIncrement(&ErrorCounter.SendTerminated);
        break;
    }

    iSendBuf = SendBufMem_get(); // a local cache -> the memory is allocated by ExAllocatePoolWithTag(NonPagedPool, sizeof(SENDBUF_MEM), MEMID_SENDBUFFER);
    if (iSendBuf == NULL)
    {
        *Status = 13000;
        break;
    }

    if ((aFilter == NULL) || (aPacketPtr == NULL) || (aPacketByteSize < 1) || (aPacketByteSize > ARRAYSIZE(iSendBuf->buffer)))
    {
        *Status = 12000;
        InterlockedIncrement(&ErrorCounter.SendInvalidParams);
        break;
    }

    NdisMoveMemory(iSendBuf->buffer, aPacketPtr, aPacketByteSize);

    mdl = NdisAllocateMdl(FilterDriverHandle, iSendBuf->buffer, aPacketByteSize);
    if (mdl == NULL)
    {
        *Status = 14000;
        InterlockedIncrement(&ErrorCounter.SendAllocMdl);
        break;
    }
    mdl->Next = NULL;

    NDIS_HANDLE sndbufpool = InterlockedCompareExchangePointer(&SendBufferPool, NULL, NULL); 

// SendBufferpool is initialized at Init
// NET_BUFFER_LIST_POOL_PARAMETERS params;
// NdisZeroMemory(&params, sizeof(params));
// params.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
// params.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
// params.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
// params.PoolTag = MEMID_SENDLISTS;
// params.fAllocateNetBuffer = TRUE;
// SendBufferPool = NdisAllocateNetBufferListPool(aHandle, &params);

    if (sndbufpool == NULL)
    {
        *Status = 15000;
        InterlockedIncrement(&ErrorCounter.SendBufferPool);
        break;
    }

    PNET_BUFFER_LIST BufList = NdisAllocateNetBufferAndNetBufferList(sndbufpool, sizeof(SEND_CONTEXT), 0, mdl, 0, aPacketByteSize);
    if (BufList == NULL)
    {
        *Status = 16000;
        InterlockedIncrement(&ErrorCounter.SendAllocBufList);
        break;
    }

    BufList->SourceHandle = FilterDriverHandle; // Times before I used the Handle from Attach - this is a try with the globa Filterhandle from NdisFRegisterFilterDriver

    SENDCONTEXT(BufList)->Signature = MEMID_SENDBUFFER;
    SENDCONTEXT(BufList)->Filter = aFilter;
    SENDCONTEXT(BufList)->Buffer = iSendBuf;

// this block is also a Try
BOOLEAN isDispatch = KeGetCurrentIrql() >= DISPATCH_LEVEL;
KIRQL oldIrql = PASSIVE_LEVEL;
if (!isDispatch)
{
NDIS_RAISE_IRQL_TO_DISPATCH(&oldIrql);
}
// Try Block End

    NdisFSendNetBufferLists(aFilter->FilterHandle, BufList, NDIS_DEFAULT_PORT_NUMBER, NDIS_SEND_FLAGS_DISPATCH_LEVEL);

// the aFitler->Filterhandle comes from FilterAttach

// this block is also a Try
if (!isDispatch)
{
NDIS_LOWER_IRQL(oldIrql, DISPATCH_LEVEL);
}
// Try Block End

    InterlockedIncrement(&InfoCounter.SendCounter);

    mdl = NULL;
    iSendBuf = NULL;
} while (FALSE);

if (iSendBuf != NULL)
{
    *Status += 100;
    SendBufMem_free(iSendBuf);
}

if (mdl != NULL)
{
    *Status += 10;
    NdisFreeMdl(mdl);
}

InterlockedDecrement(&SendActive);

return res;

}

// this Function is registed by the NdisFRegisterFilterDriver call
Use_decl_annotations
VOID
FilterSendNetBufferListsComplete(
NDIS_HANDLE FilterModuleContext,
PNET_BUFFER_LIST NetBufferLists,
ULONG SendCompleteFlags
)
{
PMS_FILTER aFilter = (PMS_FILTER)FilterModuleContext;

PNET_BUFFER_LIST BufList = NetBufferLists;
PNET_BUFFER_LIST BufListOld = NULL;

while (BufList)
{
    PNET_BUFFER_LIST pNextNetBufList = NET_BUFFER_LIST_NEXT_NBL(BufList);

    if (BufList->SourceHandle == FilterDriverHandle) 
    {
        if (SENDCONTEXT(BufList)->Signature != MEMID_SENDBUFFER)
        {

#if DBG
DbgBreakPoint();
#endif
}

        if (BufListOld == NULL)
        {
            NetBufferLists = pNextNetBufList;
        }
        else
        {
            BufListOld->Next = pNextNetBufList;
        }

        BufList->Next = NULL;

        PNET_BUFFER Currbuff = NET_BUFFER_LIST_FIRST_NB(BufList);
        while (Currbuff)
        {
            while (TRUE)
            {
                PMDL pmdl = Currbuff->MdlChain;
                if (pmdl == NULL) break;
                Currbuff->MdlChain = pmdl->Next;
                pmdl->Next = NULL;
                PSENDBUF_MEM buf = CONTAINING_RECORD(pmdl->MappedSystemVa, SENDBUF_MEM, buffer);
                SendBufMem_free(buf);
                NdisFreeMdl(pmdl); //Free MDL
                InterlockedIncrement(&InfoCounter.SendCompleteCounter);
            }
            Currbuff->CurrentMdl = NULL;
            Currbuff->CurrentMdlOffset = 0;
            Currbuff->DataLength = 0;
            Currbuff->DataOffset = 0;
            Currbuff = NET_BUFFER_NEXT_NB(Currbuff);
        }
        NdisFreeNetBufferList(BufList);

        InterlockedIncrement(&InfoCounter.SendCompleteCounter);
    }
    else
    {
        BufListOld = BufList;
    }
    BufList = pNextNetBufList;
}

if (NetBufferLists)
{
    NdisFSendNetBufferListsComplete(aFilter->FilterHandle, NetBufferLists, SendCompleteFlags);
}

}

A funny fact is, that the receiving Packets are indeed filtered and not visible in Wireshark. But the sending Packets are visible in Wireshark.
Why are the overling Filter drivers involved?
Another thing is that this send routine needs about 4ms instead of 1ms with the old winpcap implementation.

thanks in advance
derMischka

This post is likely to annoy you, because I have questions that are totally unrelated to your problem.

If you’re doing 5Gbps, then clearly you are not GigE. Is this a 10GigE product? And why do you need bypass antivirus processing? Surely that can’t be healthy for the infrastructure. What’s the plumbing here? The camera sends network packets, and your app receives them through the net filter, without going through the rest of the stack? Are there other products doing this?

Hello,

yes the product use a 10GigE SFP interface. The receive path is so far ok except the packet loss at desktop background image change (can be disabled).
In the past we had a lot of problems using Kaspersky Antivir Software with a huge amount of Bandwidth. So for me it is a really benefit if the special GigE Vision Packets are filtered out near the hardware and not processed through the complete Filter Stack.

But the big problem is in the send path which need no much bandwidth (at least 1 to 10 packets per second). Last afternoon I made a test where I could send over about 5000 packets without any problems and than the failure was present again. The SendComplete callback is not calling. The packets does also not leave the network adapter because the camera stops sending the image data because of the missing heartbeat/alive packet from the sendpath.

derMischka