Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible Confict with others WFP Solutions. #41

Closed
herossa opened this issue Mar 27, 2015 · 10 comments
Closed

Possible Confict with others WFP Solutions. #41

herossa opened this issue Mar 27, 2015 · 10 comments

Comments

@herossa
Copy link

herossa commented Mar 27, 2015

Test Environment: Windows 7 X86

Result:

  • When I stop the passthru keeping WFPSampler active I'm able to send emails normally, otherwise, keeping both running I'm not able to send emails anymore.

Note:

  • Instead of using the steps 3 and 4 you can install the Avast antivírus and the problem will occurs too.
@basil00
Copy link
Owner

basil00 commented Mar 30, 2015

Thanks for the report. I was not aware of any problem so I will need to investigate.

@basil00
Copy link
Owner

basil00 commented Apr 3, 2015

I could not build WFPSampler, but I tried with a combination of WinDivert driver variants and got a similar result. It seems that combining WinDivert/passthru with another WFP callout driver (that injects cloned packets) will cause packets to go into an infinite loop.

I will need to look more into this.

@basil00
Copy link
Owner

basil00 commented Apr 4, 2015

Assuming the above hypothesis is correct, it is not so clear that the problem can even be fixed.

To elaborate: the problem can be demonstrated with two network layer WFP callout drivers that do other than drop-and-clone the original packet (such as the WFPSampler and WinDivert's passthru). Each driver inserts itself in a different WFP sublayer.

What happens is this:

  1. A network packet (Packet_A) arrives, and is intercepted by Driver_1. Driver_1 clones Packet_A (to create Packet_B), injects Packet_B and drops Packet_A.
  2. WFP treats Packet_B as "new", and sends it back to Driver_1. Driver_1 detects that Packet_B was injected by itself (via FwpsQueryPacketInjectionState0 returning FWPS_PACKET_INJECTED_BY_SELF or FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF). Driver_1 "permits" Packet_B.
  3. Packet_B is sent to Driver_2. Driver_2 clones Packet_B (to create Packet_C), injects Packet_C and drops Packet_B.
  4. WFP treats Packet_C and "new", and sends it to Driver_1. Driver_1 detects that Packet_C was not injected by itself (it was injected by Driver_2), and therefore treats it as a brand new packet. We are therefore back to step 1. substituting Packet_C for Packet_A, and will loop forever.

If true, the same problem will arise with any WFP callout driver that clones and injects packets, not just WinDivert specifically. I have not verified this so I could be wrong.

This problem is not fixable since there is no practical way to tell if a packet is a clone of a previously seen packet.

It would be nice if injected packets are only sent to callout drivers with a lower priority/weight rather than all drivers. This immediately prevents the possibility of loops forming. This is also the traditional "divert sockets" semantics (of which WinDivert emulates).

@filipevserra
Copy link

Hi.
I did some changes on windivert.c to avoid this problem.
Before call FwpsCalloutRegister0 I've added the line:
filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT;
And in the windivert_classify_callout I've changed the if where has the INJECTED_BY_SELF check to:
if (packet_state == FWPS_PACKET_INJECTED_BY_SELF ||
packet_state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
{
priority = (UINT32)packet_context;
if (priority >= context->priority)
{
if ((filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) != 0) {
result->rights &= ~FWPS_RIGHT_ACTION_WRITE;
}
result->actionType = FWP_ACTION_PERMIT;
return;
}
}
I don't know if this changes will cause any other trouble, but in my case the infinite loop has ceased and everything works normaly.

@basil00
Copy link
Owner

basil00 commented Apr 7, 2015

The relevant documentation is here. It's been a while since I've looked at it.

I am not sure if "PERMIT" is correct. WinDivert never "permits" traffic per se, rather the state of non-matching traffic should be as if WinDivert were never there. Also maybe PERMIT may cause the traffic to skip other WinDivert filters, I am not sure.

@filipevserra
Copy link

Hi. Thanks for your answer.
I agree with you. I don't think that PERMIT is correct either. We can PERMIT the package sometimes, but in this case PERMIT is not the expected.
In our case it prevents the infinite loop. This loop can't occur on our users machine and "block" all network communication. So, for the moment we took this changes as our best option. By the way it didn't prevents that the package could be blocked by another WFP.

@psprowls
Copy link

I am running into what I a believe is the same issue as above when we have another WinDivert based solution on the system. CPU usage is pegged split between our service and their service and all network traffic fails (although we will still block sites appropriated). I am in the process of trying the suggested fix above, but was hoping since over a year has passed someone might have more information.

@basil00
Copy link
Owner

basil00 commented Oct 23, 2016

Unfortunately I do not know of a fix. Recently another user has opened a paid ticket with Microsoft regarding the issue, so hopefully a solution will be found, but I have not heard any news yet.

Unless I am misunderstanding something (which is quite possible), this seems to be a design flaw in WFP itself. Basically, WFP has a mechanism for preventing a driver (such as WinDivert) from (re)considering packets injected by itself, causing an infinite loop. This is fine, but it appears that the design of WFP did not consider that the mutual infinite loops are possible between two or more drivers using the "clone / block / inject method", as explained in detail above. The reason why I think it is a design flaw is that Windows own sample driver (WFPSampler) exhibits the same problem when combined with other WFP drivers such as WinDivert.

Another problem is even if a solution is found I currently cannot make any new release of WinDivert until #53 is fixed.

@basil00
Copy link
Owner

basil00 commented Oct 9, 2017

I finally found an authoritative acknowledgement and response to this problem. Post is old (2013) but was somehow missed. It seems that our analysis above is basically correct, i.e. two or more drivers can enter an infinite loop using the clone-block-reinject method.

Two suggested solutions from Dusty Harper:

As you state though, if both are injecting new NBLs each time, then it will get to be INJECTED_BY_OTHER ... in this case, you could implement a counter mechanism for how many times you've seen the indication, so you can bail instead of infinitely looping.

I am not sure how this can be implemented, since each time we see the indication it looks like a brand new packet.

But perhaps he means the packet's TTL counter? This is a crude way of preventing infinite loops, but may be better than nothing. It is also not the semantics we want: we want to allow the looping packet, not drop it.

Additionally, if you have the original, you should copy the nbl info to the new packet using NdisCopySendNetBufferListInfo or NdisCopyReceiveNetBufferListInfo.

This idea does not really work with WinDivert since the driver has no idea if the injected packet is copy or not. It might be possible to thread this information via the user space application (e.g. via the WinDivert address structure), however we then rely on the application to behave correctly in order to avoid infinite loops.

Another idea may be to just ignore any packet INJECTED_BY_OTHER. Since WinDivert inserts itself early in the pipeline, it is likely other injected packets come "after" WinDivert, so can be "allowed". That said, it is possible for another driver it insert itself before WinDivert I think.

basil00 added a commit that referenced this issue Oct 18, 2017
- A partial fix for #41
- Decrements the TTL for reinjected packets.
- If (TTL==0), WinDivertRecv() will fail with:
  ERROR_HOST_UNREACHABLE = 1232
  which is better than looping.
@basil00
Copy link
Owner

basil00 commented Oct 21, 2017

As mentioned, the repository version of WinDivert will decrement the TTL counter for "suspicious" packets that were injected by other callout drivers. This hardens WinDivert against infinite loops caused by mutual recursion, since the looping packet will eventually be dropped. When this occurs, the WinDivertRecv function will fail with ERROR_HOST_UNREACHABLE. An application can take some appropriate action, such as informing the user that there is incompatible software installed on the system. This is not a great solution but it is better than nothing.

@basil00 basil00 closed this as completed Oct 21, 2017
basil00 added a commit that referenced this issue Nov 13, 2017
WinDivert will now mark any packet injected by
another driver as an "impostor", meaning that it
did not originate from the network.  Changes are:
- User programs may filter impostor packets.
- WinDivertSend() automatically decrements the TTL
  for imposter packets, see #41.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants