basil00 / WinDivert

WinDivert: Windows Packet Divert
https://reqrypt.org/windivert.html
Other
2.56k stars 513 forks source link

WinDivert changes UdpClient behaviour when DontFragment flag is set #278

Closed trudyhood closed 3 years ago

trudyhood commented 3 years ago

I am wondering why the behavior of C# UdpClient changes while WinDivert is running. I test it with passthru. When I send a UDP more than MTU with DontFragment, I receive FragmentationNeeded error, but after running passthru, I don't receive this error, of course the packets never reach to destination too. It causes problem for some apps to find a proper MTU. It this normal?

basil00 commented 3 years ago

Thanks for the report. Normally the ICMP Fragmentation Needed message should "pass through" the passthru program without change, allowing for normal pMTU discovery. What happens if you run passthru without diverting ICMP? E.g.:

passthru.exe "not icmp"
trudyhood commented 3 years ago

Applications usually send "DONT Fragment" to find MTU. By the way, with [passthru.exe "not icmp"] the result is still the same and unexpected. Without [passthru.exe "not icmp"], I receive the packet is too big error, but after running it, no error is return by windows, UDP packets just get lost without indicating any error. I think it causes unexpected behavior in some applications.

The more strange part is that Windows cache results from MTU error for each destination about a few minutes. When I choose a new destination, the first big packet gets lost, but the next call immediately receives the error indicating that packet is too big. But it looks like Windows bypass its cache result immediately as soon as I run passthru.

basil00 commented 3 years ago

with [passthru.exe "not icmp"] the result is still the same and unexpected.

This means the problem is not because passthru.exe is failing to reinject the ICMP packets.

I think maybe the problem is the original large packet. Perhaps, if the packet is too big, the underlying miniport/ethernet driver would normally return an error (e.g., NDIS_STATUS_INVALID_LENGTH) or it is handled by NDIS itself, rather than using ICMP internally. But, by using passthru.exe, the original packet is effectively discarded and a copy takes the original's place, meaning that the length error no longer flows back to the original application. This is just speculation though.

trudyhood commented 3 years ago

You are right, the test packet was too big and dropped by WinDivert before sending to the destination. Perhaps windows handle the error or maybe NDIS generate the ICMP reply itself. I wish WinDivert replied the "ICMP fragment needed" after dropping it.

basil00 commented 3 years ago

The packet is not dropped by WinDivert itself, but is likely dropped by the underlying Miniport (ethernet) driver.

To fix this issue, WinDivert ought to translate internal errors into corresponding ICMP packets. I think this would be a useful feature to add, but would need to investigate how to implement it & the complexity.

basil00 commented 3 years ago

I tried but could not reproduce this issue. Can you please post a minimal example to exercise this issue?

trudyhood commented 3 years ago

I tried but could not reproduce this issue. Can you please post a minimal example to exercise this issue?

Try this before and after "passthru true" ping 142.250.217.143 -l 1700 -f

Sometimes I change IP to prevent windows cache the result. before running passthru you should see this:

Pinging 142.250.217.143 with 1700 bytes of data:
Packet needs to be fragmented but DF set.

after that you just get time out.

Pinging 142.250.217.143 with 1700 bytes of data:
Request timed out.
basil00 commented 3 years ago

I think maybe the problem is the original large packet. Perhaps, if the packet is too big, the underlying miniport/ethernet driver would normally return an error (e.g., NDIS_STATUS_INVALID_LENGTH) or it is handled by NDIS itself, rather than using ICMP internally.

After some experimentation, it seems this theory is correct. I think it will be possible for the WinDivert driver to automatically translate the error code into an ICMP message, and maybe this will fix the PMTU problem.

basil00 commented 3 years ago

One problem is that the ICMP message should report the next hop MTU. However, the WinDivert driver does not have this information. It may be possible to map the interface index to the next hop MTU somehow.

An alternative would be for WinDivert to try and guess the MTU using the current packet size and common MTU values. If the guess is too low, then the connection will still work, albeit with a sub-optimal MTU. Else if the guess is too high, then the next send will fail, causing a new ICMP message to be generated with an even lower MTU value. Eventually, a working MTU value should be found.

trudyhood commented 3 years ago

Sorry, but If the underlying miniport/ethernet driver notify a high-level app like C# that packet is too big, then how WinDivert can interfere with this notification! If it is something in Windows API socket API. then how WinDivert intercepts that error!

basil00 commented 3 years ago

Because the error flows to WinDivertSend rather than the WSASend from the original application. In fact, WinDivertSend even ignores errors, since the "correct" error handling turned out to be a major bottleneck.

Now I think about it, generating an ICMP message may not help, since the original WSASend will successfully complete before the ICMP message arrives.

trudyhood commented 3 years ago

I think WSASend returns immediately but windows cache the ICMP result for few minutes as far as it arrives, then throws an error message in the subsequent calls.

By the way, I guess due to the unreliability of ICMP over the internet, many applications use a timeout in addition to ICMP's packet too big message.

basil00 commented 3 years ago

Injecting an ICMP message fixes the ping example at least. I will likely add this change to WinDivert, but it requires a mini-refactoring, so needs more testing first.

schultetwin1 commented 3 years ago

@basil00 - Do you plan to release a new version of WinDivert with this fix in it anytime soon?

basil00 commented 3 years ago

Version 2.3.0 will be released next year hopefully. There are a few other fixes that still need to be implemented, as well as a lot of testing.

schultetwin1 commented 3 years ago

Thanks!