NDIS: Generating multi MDL frames in a filter driver

Hi,

I alter Ethernet frames in a tunneling filter driver as they pass through FilterSendNetBufferLists and FilterReceiveNetBufferLists. The changes include, for the sending path, adding new headers between the Ethernet header and the IP header, changing addresses in the original IP header and then recalculating IP and TCP checksums (they fall into the application payload after adding the new headers), etc. For the receiving path I have to make the opposite changes.

There are fewer constraints in the sending path about splitting a packet into several MDLs, so I use NdisAdvanceNetBufferDataStart and NdisAdvanceNetBufferListDataStart to skip the parts I want to change, then NdisAllocateCloneNetBufferList to clone the NET_BUFFER_LIST (reusing the memory pages I won’t change) and finally NdisRetreatNetBufferListDataStart to create the buffer for the altered and new parts.

But the receiving path must observe stricter rules.

Supporting Header-Data Split in Protocol Drivers and Filter Drivers says “NDIS 6.0 and later protocol drivers and filter drivers must support receive indications with the header and data in non-contiguous buffers.

Header-data split was introduced in NDIS 6.1, what means that it should be possible to split frames in non header-data split supporting drivers, which is confirmed by Cases Where Header-Data Split Is Not Used when it says “There are cases where a received frame can be split outside of the header-data split provider requirements. That is, the header-data split requirements only apply to header-data split providers. In these cases, never split Ethernet frames in the middle of the IP header, IPv4 options, IPsec headers, IPv6 extension headers, or upper-layer-protocol headers, unless the first MDL contains at least as many bytes as NDIS specified for lookahead size.

But OID_GEN_CURRENT_LOOKAHEAD defines “lookahead” as excluding “the header” when it says “As a query, the OID_GEN_CURRENT_LOOKAHEAD OID returns the number of bytes of received packet data that will be indicated to the protocol driver. This specification does not include the header.” So the “unless” part in Cases Where Header-Data Split Is Not Used wouldn’t make sense unless the header OID_GEN_CURRENT_LOOKAHEAD refers to is just Ethernet’s.

My questions are:

  • Can I split receive indications in multiple MDLs even if I don’t fulfill header-data split provider requirements, just complying with the lookahead size rule? Can you please clarify the meaning of “lookahead size” in the presence of layer 2, 3 and 4 headers, and how to get it in a filter driver?
  • What about WinPcap over my filter driver? As it’s NDIS 5.x, does its presence prohibit Ethernet frame splitting altogether? Or will NDIS cope with this in the NET_BUFFER to NDIS_PACKET translation, so that the NDIS 6.x filter doesn’t have to worry about it? In case WinPcap banned my filter splitting the frames, how could I find out at run time? Should I intercept OID_GEN_HD_SPLIT_PARAMETERS to test for NDIS_HD_SPLIT_COMBINE_ALL_HEADERS? I think that solution would be fragile as “NDIS will send this OID to the miniport driver only if header-data split was enabled with the NDIS_HD_SPLIT_ENABLE_HEADER_DATA_SPLIT flag in the NDIS_HD_SPLIT_ATTRIBUTES structure during miniport initialization.” How to find out if frame splitting is prohibited by the presence of an NDIS 5.x protocol when the miniport does not enable header-data split?

Thank you very much.
Regards.

See also this doc: https://docs.microsoft.com/en-us/windows-hardware/drivers/network/indicating-received-ethernet-frames

Can I split receive indications in multiple MDLs even if I don’t fulfill header-data split provider requirements, just complying with the lookahead size rule?

Yes.

Can you please clarify the meaning of “lookahead size” in the presence of layer 2, 3 and 4 headers

“Lookahead size” means that protocols will tell you how many bytes to include in the first MDL, not including layer2. It’s not defined whether the lookahead includes layer 3 or 4 or whatever. For example, the vSwitch might not care about layer3 or 4, so it could just give you a lookahead of 0. But TCPIP might care about both 3 and 4, so it could give you sizeof(IPv4_HEADER) + MAX_IPv4_OPTIONS_SIZE + sizeof(TCP_HEADER). (In practice, the built-in TCPIP implementation currently asks for partial layer3 but none of layer4 size.)

[…] how to get it in a filter driver?

Your FilterRestartHandler will get this value in NDIS_RESTART_GENERAL_ATTRIBUTES::LookaheadSize.

You don’t have to worry about NDIS 5.x drivers; NDIS handles the translation for you. (NDIS 5.x actually does support NDIS_PACKETs that are split across multiple NDIS_BUFFERs, so the translation might be a no-op.)