Is there a way to read the NET_BUFFER at once?

248 views Asked by At

I made NDIS 6 network filter driver and am reading the packet. When I use Intel I350 NIC, 'MmGetMdlByteCount' returns '9014'bytes. This value is the same as the MTU size, so I can read the data at once. However, when using the x540 NIC, 'MmGetMdlByteCount' is returned to '2048'bytes. So I have to read the MDL over and over again. Why is this happening? Is there a way to read data at once on the X540 NIC? I want to reduce repetition because I think the consumption time will be longer if I bring the data several times.

1

There are 1 answers

1
Jeffrey Tippet On

What you're seeing is a result of how the NIC hardware physically works. Different hardware will use different buffer layout strategies. NDIS does not attempt to force every NIC to use the same strategy, since that would reduce performance on some NICs. Unfortunately for you, that means the complexity of dealing with different buffers gets pushed upwards into NDIS filter & protocol drivers.

You can use NdisGetDataBuffer to do some of this work for you. Internally, NdisGetDataBuffer works like this:

if MmGetSystemAddressForMdl fails:
    return NULL;
else if the payload is already contiguous in memory:
    return a pointer to that directly;
else if you provided your own buffer:
    copy the payload into your buffer
    return a pointer to your buffer;
else:
    return NULL;

So you can use NdisGetDataBuffer to obtain a contiguous view of the payload. The simplest way to use it is this:

UCHAR ScratchBuffer[MAX_MTU_SIZE];
UCHAR *Payload = NdisGetDataBuffer(NetBuffer, NetBuffer->DataLength, ScratchBuffer, 1, 0);
if (!Payload) {
    return NDIS_STATUS_RESOURCES; // very unlikely: MmGetSystemAddressForMdl failed
}

memcpy(baImage, Payload, NetBuffer->DataLength);

But this can have a double-copy in some cases. (Exercise to test your understanding: when would there be a double-copy?) For slightly better performance, you can avoid the double-copy with this trick:

UCHAR *Payload = NdisGetDataBuffer(NetBuffer, NetBuffer->DataLength, baImage, 1, 0);
if (!Payload) {
    return NDIS_STATUS_RESOURCES; // very unlikely: MmGetSystemAddressForMdl failed
}

// Did NdisGetDataBuffer already copy the payload into my flat buffer?
if (Payload != baImage) {
    // If not, copy from the MDL to my flat buffer now.
    memcpy(baImage, Payload, NetBuffer->DataLength);
}

You haven't included a complete code sample, but I suspect there may be some bugs in your code. I don't see any attempt to handle NetBuffer->CurrentMdlOffset. While this is usually zero, it's not always zero, so your code would not always be correct.

Similarly, it looks like the copy isn't correctly constrained by ulDataLength. You would need a ulDataLength -= ulBytesToCopy in there somewhere to fix this.

I'm very sympathetic to how tricky it is to navigate NBLs, NBs, and MDLs -- my first NIC driver included a nasty bug in calculating MDL offsets. I have some MDL-handling code internally -- I will try to clean it up a bit and publish it at https://github.com/microsoft/ndis-driver-library/ in the next few days. I'll update this post if I get it published. I think there's clearly a need for some nice, reusable and well-documented sample code to just copy (subsets of) an MDL chain into a flat buffer, or vice-versa.

Update: Refer to MdlCopyMdlChainAtOffsetToFlatBuffer in mdl.h