Issue with Network Flow Detection using NEFilterDataProvider in iOS Network Extension.

PLATFORM AND VERSION

iOS Development environment: Xcode 16.0, macOS 15.0.1 Run-time configuration: iOS 17.5.1

DESCRIPTION OF PROBLEM

We are working on an iOS application that utilizes the NEFilterDataProvider class from the Network Extension framework to control network flows. However, we are encountering an issue where network flows are not being detected as expected. Here are the details of our setup:

  1. We are using the NEFilterDataProvider class to filter network traffic in our app.
  2. The filtering setup works well for certain flows/apps, but we cannot detect Facebook network flows as intended.
  3. The app is correctly configured with the necessary entitlements, and we have set up the required App Groups and Network Extension capabilities.

We would like to request guidance on how to troubleshoot or resolve this issue. Could you provide insights on:

  1. Whether there are any known limitations or conditions under which network flows may not be detected by NEFilterDataProvider.
  2. Recommendations for additional debugging techniques to better understand why some flows might not be captured.
  3. Recommendations for additional code to be added to detect some flows that might not be captured.
  4. Any specific scenarios or configurations that might be causing this issue in iOS.

STEPS TO CHECK

  1. Replace below code in FilterDataProvider.
  2. Try running the app and set debugger in FilterDataProvider.
  3. Launch Facebook app.
  4. You will observe that no NEFilterFlow is detected in handleNewFlow for actions such as posts, reels, etc.
import NetworkExtension

class FilterDataProvider: NEFilterDataProvider {
    
    let blockedDomains = [
        "facebook.com"
    ]
    
    override func startFilter(completionHandler: @escaping (Error?) -> Void) {
        // Perform any necessary setup here.
        DNSLogger.shared.log(message: "Filter started")
        
        completionHandler(nil)
    }
    
    override func stopFilter(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        // Perform any necessary cleanup here.
        DNSLogger.shared.log(message: "Filter stopped with reason: \(reason)")
        completionHandler()
    }
    
    override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {
        
        var url: URL?
        
        if let urlFlow = flow as? NEFilterBrowserFlow {
            url = urlFlow.url
        }
        else {
            let urlFlow = flow as? NEFilterSocketFlow
            url = urlFlow?.url
        }
        
        guard let hostName = url?.host else { return .allow() }
        
        DNSLogger.shared.log(message: "Domain reveived: \(hostName)")
        
        return .allow()
    }
    
    // Handle inbound data (data received from the network)
    override func handleInboundData(from flow: NEFilterFlow, readBytesStartOffset offset: Int, readBytes: Data) -> NEFilterDataVerdict {
        DNSLogger.shared.log(message: "Inbound data: \(readBytes)")
        return .needRules()
    }
    
    // Handle outbound data (data sent to the network)
    override func handleOutboundData(from flow: NEFilterFlow, readBytesStartOffset offset: Int, readBytes: Data) -> NEFilterDataVerdict {
        // Inspect or modify outbound data if needed
        // For example, you could log the data or modify it before sending
        DNSLogger.shared.log(message: "Outbound data: \(readBytes)")
        return .needRules()
    }
    
    override func handleRemediation(for flow: NEFilterFlow) -> NEFilterRemediationVerdict {
        return .needRules()
    }
    
    override func handleRulesChanged() {
        // Handle any changes to the rules
    }
}
Answered by DTS Engineer in 811526022
Whether there are any known limitations or conditions under which network flows may not be detected by NEFilterDataProvider.

Not that I’m aware of.

The most common cause of problems like this is that folks are misidentifying the flows. For example:

  • A flow might come through as a socket flow (NEFilterSocketFlow) when they’re expecting a browser flow (NEFilterBrowserFlow).

  • A flow might come through with no DNS name and they’re filtering on that.

  • A flow might come through as UDP when they’re expecting TCP. The most obvious example of this is HTTP/3, which uses QUIC, which is based on UDP.

To debug this my recommendation is that you catch all flows and log all the info about them. Then compare the results to an RVI packet trace. If you see flows in the packet trace that don’t show up in the filter, that’d be weird, and something that I’d be happy to help you look into.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Whether there are any known limitations or conditions under which network flows may not be detected by NEFilterDataProvider.

Not that I’m aware of.

The most common cause of problems like this is that folks are misidentifying the flows. For example:

  • A flow might come through as a socket flow (NEFilterSocketFlow) when they’re expecting a browser flow (NEFilterBrowserFlow).

  • A flow might come through with no DNS name and they’re filtering on that.

  • A flow might come through as UDP when they’re expecting TCP. The most obvious example of this is HTTP/3, which uses QUIC, which is based on UDP.

To debug this my recommendation is that you catch all flows and log all the info about them. Then compare the results to an RVI packet trace. If you see flows in the packet trace that don’t show up in the filter, that’d be weird, and something that I’d be happy to help you look into.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Issue with Network Flow Detection using NEFilterDataProvider in iOS Network Extension.
 
 
Q