NDIS LWF: Interaction between Windows TCP/IP layer and LWF indicated frames marked as loopback.

Hi, everyone:
I’ve written an NDIS LWF.
It modifies a subset of the packets passing through it.
For debugging purposes, I wanted to optionally see in Wireshark the two versions of each packet: the original one passed to the filter driver and the altered version generated by it.
I didn’t wanted other layers, for example Winsock (with the exception of, perhaps, RAW sockets), to process the original packets, just the altered ones.
I devised a plan to achieve it by using especially crafted loopback packets.
For the sending path, it seems to be working:

  • I create a first clone of the NET_BUFFER_LIST, alter it, and pass it to NdisFSendNetBufferLists. NDIS loops back this NET_BUFFER_LIST based on standard procedures.
  • I create a second clone of the NET_BUFFER_LIST passing NDIS_CLONE_FLAGS_USE_ORIGINAL_MDLS to NdisAllocateCloneNetBufferList, then mark the NET_BUFFER_LIST with NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET, and finally pass it to NdisFIndicateReceiveNetBufferLists (i.e., I manually loop it back in the filter driver).
  • Both NET_BUFFER_LIST clones carry my LWF handle in SourceHandle and they are reference counted in ChildRefCount of the original NET_BUFFER_LIST in order to complete the latter only when both clones have been completed/returned.
  • The altered NET_BUFFER_LIST is sent and two loopbacks are seen in Wireshark. No other side effect seem to occur. Good.

Unfortunately, the receiving path shows an unexpected side effect. The algorithm is similar:

  • I create a first clone of the NET_BUFFER_LIST, alter it, and pass it to NdisFIndicateReceiveNetBufferLists. This seems to be alright.
  • I create a second clone of the NET_BUFFER_LIST passing NDIS_CLONE_FLAGS_USE_ORIGINAL_MDLS to NdisAllocateCloneNetBufferList, then mark the NET_BUFFER_LIST with NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET, and finally pass it to NdisFIndicateReceiveNetBufferLists (i.e., I manually loop it back in the filter driver).
  • Both NET_BUFFER_LIST clones carry my LWF handle in SourceHandle and they are reference counted in ChildRefCount of the original NET_BUFFER_LIST in order to return the latter only when both clones have been returned.
  • I see both clones in Wireshark as expected.
  • And now the unexpected result: Windows TCP/IP layer processes both clones. In my case, the loopback packet (a copy of the original one) is UDP while the altered one isn’t, and nobody is listening through a UDP socket. Windows generates an ICMP destination port unreachable error indication for the former. I thought Windows TCP/IP would refrain from processing it, just because it was marked as loopback.

Any ideas to help me achieve this?
Why do sending path loopbacks work so well? Are they discarded by Windows TCP/IP just because the destination MAC/IP addresses do not match those assigned to the receiving network interface regardless of their being loopbacks?
Another question just to check the requirements for the manual looping back: only for the sending path, a NET_BUFFER_LIST can contain several NET_BUFFERs. My current understanding is that I have to extract and clone the NET_BUFFERs one by one (creating a new NET_BUFFER_LIST for each original NET_BUFFER, instead of one for the whole group) in order to satisfy “indicating frames” restrictions (a received Ethernet NET_BUFFER_LIST must carry exactly one NET_BUFFER). Is my understanding correct or can a loopback NET_BUFFER_LIST contain several NET_BUFFERs, simplifying the cloning process?

Thank you very much.
Best regards.

We don’t really support LWFs doing manual loopback of packets. Layer-2 loopback is rarely used – basically it’s there just for Wireshark – so it’s not something that we’ve spent a lot of time adding features for.

TCPIP itself doesn’t filter out loopback NBLs. Instead, NDIS typically does this by checking the protocol’s packet filter (OID_GEN_CURRENT_PACKET_FILTER). The specific rules are somewhat complex, but there are cases where, if the TCPIP open is in promiscuous mode, then NDIS indicates all NBLs to it, including loopbacks. When this happens, TCPIP will discard the NBLs because of the destination address, not just because the loopback flag is set on them. (Arguably, TCPIP should check the loopback flag and discard such NBLs, but that would introduce a small perf hit to everyone for a scenario that is rarely used and which typically takes care of itself thanks to the destination address.)

If you’re just trying to see packets in Wireshark, you could hide them from TCPIP by, for example, inserting a fake VLAN header. TCPIP won’t parse it then, but Wireshark will.