NDIS modifying cloned buffer

Hello. On the TX path I want to modify IP address, TCP port, and TCP data. Initially I was using NdisGetDataBuffer() but sometimes the data is fragmented and creates a new buffer, hence modifying that data does not affect the original MDL to be sent downwards.

At the moment I manually walk the cloned MDLs chain to find the data. I had a look through the NDIS macros but couldn’t find anything relevant. What is the correct way to modify the data and send it down? I know I need to fix the TCP checksum of data if modified. But for now I just changing TCP destinationPort and even that doesn’t succeed. I checked IP checksum and it is NULL, from what I read online, the NIC card calculates checksum for Tx packets.

What I do doesn’t quite work - i.e. if I omit the modification the clone works fine against webserver I test against. However if I change the TCP.destinationPort port my webserver still gets a request to original IP:port. However I am sure I am know editing the correct MDL, and in the FilterSendNetBufferListsComplete I also inspect my clonedNbl to be freed, and I see the modified port (that is proven since I target port 8000 (0x401f) and it only appears once in the frame data, and is at offset 0n34 = (ETH len)0n14 + (IP4 len)0n20) = 0n34 (TCP)

Pseudocode of what I do.

FilterSendNetBufferLists:
while (currentNbl)
…while (pNetBuffer)
…if (search) {…}
…cloneNbl = NdisAllocateCloneNetBufferList(currentNbl, NULL, NULL, 0);
…NdisCopySendNetBufferListInfo(cloneNbl, currentNbl); // copy flags
…for (mdl link loop) {…} // search and replace data
…cloneNbl->ParentNetBufferList = currentNbl; //save orig nbl, for restoration in completion
NdisFSendNetBufferLists(NBL list of cloneNbl) // send down to NIC

FilterSendNetBufferListsComplete:
while (currentNbl) …
//check NBL belongs to me
…parentNbl = currentNbl->ParentNetBufferList; // add to nbl list
…InsertTailQueue(&PacketCompleteQueue, parentNbl);
NdisFSendNetBufferListsComplete(PacketCompleteQueue…) // send original NBL I cloned back up to protocol flt

Meh this markup and my code example is kapoot…

Try using a packet capture tool to see what actually goes out on the wire. (Wireshark uses an old-fashioned way to hook into the stack, so it doesn’t interop very well with LWFs on the local machine. If you use Wireshark, do it from another machine that is on the same network.)

Note that the TCP checksum does cover parts of the TCP header, including the destination port. So you must think about the checksum if you tamper with the destination port. (In the presence of hardware offload of TCP checksum, you can choose to skip this step for performance.)

Also, note that if you tamper with the port number, you should generally recalculate (or at least clear out) out the RSS information too. This isn’t super important on the transmit path, but some drivers might use the RSS hash even on Tx or Tx-complete. And presumably you’re going to start doing this on the Rx path – there it’ll be more important to ensure the RSS has is not incorrect.

WFP is a platform for modifying the network stack at the protocol layer. Depending on what the larger problem you’re solving is, WFP might be a more appropriate place to redirect port numbers.

@“Jeffrey_Tippet_[MSFT]” said:
Try using a packet capture tool to see what actually goes out on the wire. (Wireshark uses an old-fashioned way to hook into the stack, so it doesn’t interop very well with LWFs on the local machine. If you use Wireshark, do it from another machine that is on the same network.)

Note that the TCP checksum does cover parts of the TCP header, including the destination port. So you must think about the checksum if you tamper with the destination port. (In the presence of hardware offload of TCP checksum, you can choose to skip this step for performance.)

Also, note that if you tamper with the port number, you should generally recalculate (or at least clear out) out the RSS information too. This isn’t super important on the transmit path, but some drivers might use the RSS hash even on Tx or Tx-complete. And presumably you’re going to start doing this on the Rx path – there it’ll be more important to ensure the RSS has is not incorrect.

WFP is a platform for modifying the network stack at the protocol layer. Depending on what the larger problem you’re solving is, WFP might be a more appropriate place to redirect port numbers.

I’m trying to get used to just packet modification (with just TCP for now), hence not limiting to 1 protocol yet, so better to understand lwf/miniport design.

On the other side I do indeed see the modified packet. Yes I didn’t know the checksum included header data itself, but from what I read about one’s complement, I can get away with swapping 0x1f40 (8000) with 0x401f (16415) to keep the checksum still valid, which wireshark also agrees with. Also checked IP4 checksum, which is verified.

It fails after 3rd packet, with TCP re transmission spam then eventual RST. 192.168.235.1 = server, 192.168.235.129 = guest with filter.

Modifying only destination port gives this sequence on server:
.139 → .1 SYN (gets callback FilterSendNetBufferLists)
.1 → .139 SYN ACK
.139 → .1 SYN (gets callback FilterSendNetBufferLists, wireshark err TCP retransmission)
.139 → .1 SYN (wireshark err TCP retransmission)
.1 → .139 SYN ACK (wireshark err TCP retransmission)
.1 → .139 SYN ACK (wireshark err TCP retransmission)
.1 → .139 RST

A normal good operation, where I use my cloned netbuffers without modfiying anything:
.139 → .1 SYN
.1 → .139 SYN ACK
.139 → .1 SYN (wireshark err TCP retransmission)
.1 → .139 SYN ACK (wireshark err TCP retransmission)
139 → .1 ACK
139 → .1 ACK (wireshark err TCP retransmission)
139 → .1 HTTP GET!

If you don’t mind looking at the following stream attached. Packets 0 to 7 are the failure cause. 8 to 19 are the success case.

Do you think I need to modify the actual TCP:HTTP Header i.e. host field as well?

Hmm do I need to capture and modify the responding Rx path data (after I modified the Tx data) and hide the fact that I changed the port, to indicate to upper drivers (protocol etc)?

Hmm do I need to capture and modify the responding Rx path data (after I modified the Tx data) and hide the fact that I changed the port, to indicate to upper drivers (protocol etc)?

Yup. That’s why I said “presumably” in my previous mail :wink:

@“Jeffrey_Tippet_[MSFT]” said:

Hmm do I need to capture and modify the responding Rx path data (after I modified the Tx data) and hide the fact that I changed the port, to indicate to upper drivers (protocol etc)?

Yup. That’s why I said “presumably” in my previous mail :wink:

Yep that was exactly it! In the RX path fixing the port back to original, allowed the stream to work successfully.

Turns out I didn’t need to modify checksum for modifying a port. Maybe it is being offloaded?

An issue I did have was only doing a single network byte swap, forgot to swap it once I made the change ha.
pFrameEncaps->tcp.sourcePort = RtlUshortByteSwap(RtlUshortByteSwap(pFrameEncaps->tcp.sourcePort) - 1);