Network Extension Pause-Resume Flow Not Working

Hello.

We are facing a problem regarding the pausing and resuming of a network flow based on the application layer data.

Context

We are implementing an HTTP/HTTPS filtering feature. The main goals are:

  1. Analyze both HTTP and HTTPS protocols and allow or deny a flow based on the the exchanged data.
  2. Analyze not only Web-kit based applications but every HTTP/HTTPS traffic.

Implemented methods

In order to satisfy these requirements, and based on the SimpleFirewall sample, we have implemented these methods:

  • handleNewFlow
  • handleInboundData
  • handleOutboundData
  • handleInboundDataComplete and handleOutboundDataComplete (for cleaning purposes)

Application data processing

Firstly, we return “NEFilterNewFlowVerdict.filterDataVerdict(withFilterInbound: true, peekInboundBytes: Int.max, filterOutbound: true, peekOutboundBytes: Int.max)” to handleNewFlow in order to access the application layer data.

Afterwards handleInboundData and handleOutboundData parse the received HTTP/HTTPS data and returns one of these possible choices:

  1. “NEFilterDataVerdict.init(passBytes: passBytesCount, peekBytes: Int.max)” when we don’t have the required application layer data to perform the analysis.
  2. “NEFilterDataVerdict.pause()” whenever the information has been fulfilled and we can proceed with the analysis.

Finally, we perform a flowResume operation with the action resulting from the analysis, either "NEFilterNewFlowVerdict.allow()" or "NEFilterNewFlowVerdict.drop()".

Problem

The trouble is the flow is not correctly resumed. The paused data is not reinjected to the network and the connection does not progress.

Considerations

  • Traffic interception is based on the SimpleFirewall Swift code, but application layer data parsing is written in C. We are using the bridging header and every data seems to be correct in both Swift and C sections.
  • The analysis is performed in a separated thread inside the C code section. So we:
    • Indicate Pause to the main thread that came from the Swift section, which in the end will return “NEFilterDataVerdict.pause()” to the system.
    • Call Swift section from the analysis thread to perform a flowResume operation.
  • As flowResume needs the NEFilterFlow object previously provided by handleNewFlow, we store a reference to it in an indexed flow cache inside the Swift section. That way the analysis thread will be able to obtain it.
  • When the analysis thread calls Swift code we are not in the FilterDataProvider class context. This class has a method that performs the flowResume action. To call it we instantiate the class and call that method from the analysis thread context.

We would really appreciate it if you could bring some light to this issue. Thanks a lot, in advance.

Regards

Thank you very much for the detailed writeup, it is appreciated. My first reaction is that there is an inconsistency somewhere in either our logic or your logic that is not resuming in the correct spot. If you are not able to make any progress on this, open a TSI with a focused sample so I can take a deeper look at what is happening here in your project.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Hello Matt.

Thanks a lot for your quick reply and interest in the issue. We are going to perform some other tests and if we don't manage to solve it we'll follow your recommendation and open a TSI.

Regards

Hello Matt.

During the last days we have been working on this issue and fortunately we came up with a valid solution. Here we detail the new processing flow and some related questions.

Solution for the Pause-Resume issue

We moved the asynchronous analysis starting point from C to Swift, as follows:

  1. Process the Pause action in a method following the next steps:
  • Enqueue a task in an asynchronous global dispatch queue, passing a closure as a responseHandler that will be called at the end of the task execution.
  • Implement the closure that will manage the final action. This closure captures the local NEFilterFlow, which is required to perform the resumeFlow (similar to the one implemented in the SimpleFirewall example).
  1. The dispatch queue processes the task, which:
  • Calls the C code section to perform the analysis.
  • When the analysis is finished the responseHandler closure is executed.
  1. Finally, from the closure we call resumeFlow with the required verdict.

Do you have any comment about this processing? Nevertheless, the problem from the original question remains unsolved and this new processing flow rise some new questions and other problems.

Pause-Resume Related questions

  1. Can we request more data after resuming a paused flow? That is, not resuming with a final allow or drop action but asking for more data to analyze (“NEFilterDataVerdict.init(passBytes: passBytesCount, peekBytes: Int.max)”): Pause ---> Resume(MoreDataRequired) ---> Allow/Drop

  2. If so, can we make several pause-resume cycles for the same flow?

HandleDataComplete Callback Issue

This is very likely related to our management of the pause-resume cycle, but in both handleInboundDataComplete and handleOubtoundDataComplete methods, most of the times the NEFilterFlow parameter comes with a nil value.

Moreover, we have tested the same pause-resume mechanism implemented in the SimpleFirewall sample but moved to the handleInboundData and handleOutboundData callbacks, and the nil parameter issue does not occur. Any idea why is this happening?

Thanks a lot for your help. Regards,

Rodrigo Ortega

Do you have any comment about this processing?

No, the solution that you have outlined does seem similar to the one used in SimpleFirewall.

Regarding:

Can we request more data after resuming a paused flow? That is, not resuming with a final allow or drop action but asking for more data to analyze (“NEFilterDataVerdict.init(passBytes: passBytesCount, peekBytes: Int.max)”): Pause ---> Resume(MoreDataRequired) ---> Allow/Drop

If you are pausing then you will need to call resume on the flow to bring it out of the paused state. If you need to analyze more data on the flow I would go straight to:

return [NEFilterNewFlowVerdict filterDataVerdictWithFilterInbound:YES peekInboundBytes:PEEKSIZE filterOutbound:YES peekOutboundBytes:PEEKSIZE];

Regarding:

Moreover, we have tested the same pause-resume mechanism implemented in the SimpleFirewall sample but moved to the handleInboundData and handleOutboundData callbacks, and the nil parameter issue does not occur. Any idea why is this happening?

The NEFilterFlow values should be non-nil in the handleInboundData and handleOutboundData methods because the connections have been setup and the data is flowing back and forth on the connection.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

If you are pausing then you will need to call resume on the flow to bring it out of the paused state. If you need to analyze more data on the flow I would go straight to: return [NEFilterNewFlowVerdict filterDataVerdictWithFilterInbound:YES peekInboundBytes:PEEKSIZE filterOutbound:YES peekOutboundBytes:PEEKSIZE];

Our question was related to multiple analysis task in the same flow, each processing as follows:

  1. Get and parse some data.
  2. Asynchronously analyze accumulated data up to a point, and pause the flow in the meantime.
  3. When the analysis is finished, resume the flow.

When the flow is resumed, is it mandatory to set an ALLOW/DENY action, or can we request more data and continue analyzing the flow?

With respect to this:

The NEFilterFlow values should be non-nil in the handleInboundData and handleOutboundData methods because the connections have been setup and the data is flowing back and forth on the connection.

We have no idea what is causing this issue, but we guess it is something related to our asynchronous analysis flow. We have tested different options but none works. Moreover, it is weird that in spite of it, the network connections are working.

We will continue studying it, and maybe open a TSI for deeper investigation.

Best regards,

Rodrigo Ortega

Network Extension Pause-Resume Flow Not Working
 
 
Q