Networking

RSS for tag

Explore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.

Networking Documentation

Posts under Networking subtopic

Post

Replies

Boosts

Views

Activity

iOS 18.4 key usage requirements fails TLS connections
iOS 18.4 introduced some requirements on the Key Usage of 802.1x server certificates, as described here. https://support.apple.com/en-us/121158 When using TLS_ECDHE_RSA or TLS_DHE_RSA cipher suites, 802.1X server certificates containing a Key Usage extension must have Digital Signature key usage set. When using the TLS_RSA cipher suite, 802.1X server certificates containing a Key Usage extension must have Key Encipherment key usage set. It reads like the change is supposed to affect 802.1x only. However, we have found out that the new restrictions are actually imposed on all TLS connections using the Network framework, including in Safari. Unlike other certificate errors which can be either ignored by users (as in Safari) or by code (via sec_protocol_options_set_verify_block), these new ones can't. Even if passing completion(true) in the TLS verification block, the connection still ends up in waiting state with error -9830: illegal parameter. I understand that these requirements are valid ones but as a generic TLS library I also expect that Network framework could at least allow overriding the behavior. The current treatment is not consistent with those on other certificate errors. Since I can't upload certificates, here is how to reproduce a certificate that fails. Create a OpenSSL config file test.cnf [ req ] default_bits = 2048 distinguished_name = dn x509_extensions = v3_ca prompt = no [ dn ] CN = example.com [ v3_ca ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = CA:TRUE keyUsage = critical, keyCertSign, cRLSign Generate certificate and private key openssl req -x509 -new -nodes -keyout key.pem -out cert.pem -days 365 -config test.cnf And here is the client code to test. // Target server and port let host = NWEndpoint.Host("example.com") let port = NWEndpoint.Port("443")! // Configure insecure TLS options let tlsOptions = NWProtocolTLS.Options() sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { _, _, completion in // Always trust completion(true) }, DispatchQueue.global()) let params = NWParameters(tls: tlsOptions) let connection = NWConnection(host: .init(host), port: .init(rawValue: port)!, using: params) connection.stateUpdateHandler = { newState in switch newState { case .ready: print("TLS connection established") case .failed(let error): print("Connection failed: \(error)") case .cancelled: print("Connection canceled") case .preparing: print("Connection preparing") case .waiting(let error): print("Connection waiting: \(error)") case .setup: print("Connection setup") default: break } } connection.start(queue: .global()) Output Connection preparing Connection waiting: -9830: illegal parameter Previously reported as FB17099740
5
0
273
Apr ’25
Disable Local Network Access permission check
I'm using a Mac Studio in a homelab context and use Homebrew to manage the installed services. The services include things that access the local network, for example Prometheus which monitors some other servers, a reverse proxy which fronts other web services on the network, and a DNS server which can use another as upstream. Local Network Access permissions make it impossible to reliably perform unattended updates of services because an updated binary requires a GUI login to grant local network permissions (again). I use brew services to manage the services as launchd agents, i.e. they run in a non-root GUI context. I know that I can also use sudo brew services which instead installs the services as launchd daemons, but running services as root has negative security implication and generally doesn't look like a good idea to me. If only there was a way to disable local network access checks altogether…
5
0
210
Jun ’25
Crash in connection loader from CFNetwork with stack traces referring to internal Apple SDKs
We found there is a significant crash reports (most of them are from iOS 17, the rest are iOS 16 and 15) comes from network loader from CFNetwork. Apparently it seems there are two types of crashes if we checked from the stack trace, the one we found from both Xcode organizer and 3rd party crash reporter is referring to URLConnectionLoader::loadWithWhatToDo and the other one from our 3rd party crash reporter (didn’t found the report from Xcode organizer) referring to _CFURLResponseCreateFromArchiveList (this one only happened on iOS 17.5 and later devices). It seems that they are both kinda similar which might point to the same root cause. From what I’ve seen, we never touch the lower level API directly, we usually use the URLSession to manage our API request. The crashed stack trace also didn’t give any indication about which of our app code that triggered the crash, it only shows calls to Apple’s internal SDKs so we are unsure how to approach this issue meanwhile the crash event already reached 800+ in the last 30 days. Unfortunately, we cannot reproduce the issue as the stack trace itself seems unclear to us. I have submitted a report through feedback assistant with number: FB14679252. Would appreciate if anyone can give any advice on what we can do to avoid this in the future and probably any hint on why it could happened. Hereby I attached the crash reports that we found each from Xcode crash report and our 3rd party crash reporter (the report said it crashed on com.apple.CFNetwork.LoaderQ) so you could get a glimpse of the similarity. Xcode crash report xcode crash report.crash 3rd party crash report 3rd party crash report.txt
5
1
1.6k
Mar ’25
connect() iOS 18.5 Developer Beta (22EF5042g)
Hello! 👋 I am noticing new failures in the iOS 18.5 Developer Beta build (22EF5042g) when calling the system call connect() (from C++ source, in network extension). When using cell/mobile data (Mint & T-Mobile) this returns with EINTR (interrupted system call) right away. When I switch over to wifi, everything works fine. Note: I have not tested on other mobile carriers; which could make a difference since T-Mobile/Mint are IPv6 networks. FWIW, this is working in the previous developer beta (18.4). Anyone have any ideas?
5
0
333
Apr ’25
VPN profile corruption
We've often observed connectivity issues from our VPN app that can only be remedied by removing the VPN profile. It happens to a small but significant amount of our users, this often happens more when the app is updated, but the VPN profile corruption can happen without that too. The behavior we're observing is that any socket opened by the packet tunnel process just fails to send any data whatsoever. Stopping and restarting the packet tunnel does not help. The only solution is to remove the profile and create a new one. We believe our app is not the only one suffering from this issue as other VPN apps have added a specific button to refresh their VPN profile, which seemingly deletes and re-created the VPN configuration profile. Previously, we've caught glimpses of this in a sysdiagnose, but that was a while ago and we found nothing of interest. Alas, the sysdiagnose was not captured on a device with the network extension diagnostic profile (it was not a developer device). I would love to get technical support with this, as our bug reports have gone unanswered for long enough, yet we are still struggling with this issue. But of course, there is no minimum viable xcodeproject that reproduces this. Is there anything we can feasibly do to help with this issue? Is it even an acknowledged issue?
5
0
146
2h
URLSessionDownloadTaskDelegate functions not called when using URLSession.download(for:), but works when using URLSession.downloadTask(with:)
I'm struggling to understand why the async-await version of URLSession download task APIs do not call the delegate functions, whereas the old non-async version that returns a reference to the download task works just fine. Here is my sample code: class DownloadDelegate: NSObject, URLSessionDownloadDelegate { func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { // This only prints the percentage of the download progress. let calculatedProgress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite) let formatter = NumberFormatter() formatter.numberStyle = .percent print(formatter.string(from: NSNumber(value: calculatedProgress))!) } } // Here's the VC. final class DownloadsViewController: UIViewController { private let url = URL(string: "https://pixabay.com/get/g0b9fa2936ff6a5078ea607398665e8151fc0c10df7db5c093e543314b883755ecd43eda2b7b5178a7e613a35541be6486885fb4a55d0777ba949aedccc807d8c_1280.jpg")! private let delegate = DownloadDelegate() private lazy var session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil) // for the async-await version private var task: Task<Void, Never>? // for the old version private var downloadTask: URLSessionDownloadTask? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) task?.cancel() task = nil task = Task { let (_, _) = try! await session.download(for: URLRequest(url: url)) self.task = nil } // If I uncomment this, the progress listener delegate function above is called. // downloadTask?.cancel() // downloadTask = nil // downloadTask = session.downloadTask(with: URLRequest(url: url)) // downloadTask?.resume() } } What am I missing here?
5
1
2.1k
May ’25
[networkextesion] dnsproxy
hello I am testing the use of network extension. When we use dnsproxy to proxy DNS requests, we will send you a message that the udp pcbcount of your system continues to increase. For example for ((i=1; i<=99999; i++));do echo "Attempt $i:" dig google.com done when the dig command is used continuously, the dig command will show the following errors when pcbcount reaches a certain number. isc_socket_bind: address not available Can you help us determine what the problem might be? thank you
5
0
298
Feb ’25
NetworkConnection throws EINVAL when receiving ping/pong control frames
Summary NetworkConnection<WebSocket> in iOS 26 Network framework throws POSIXErrorCode(rawValue: 22): Invalid argument when receiving WebSocket ping (opcode 9) or pong (opcode 10) control frames. This prevents proper WebSocket keep-alive functionality. Environment iOS 26.0 (Simulator) macOS 26.1 Xcode 26.0 Note: This issue was initially discovered on iOS 26 Simulator. The same behavior was confirmed on macOS 26, suggesting a shared bug in the Network framework. The attached sample code is for macOS for easier reproduction. Description When using the new NetworkConnection<WebSocket> API introduced in iOS 26 or macOS 26, the receive() method throws EINVAL error whenever a ping or pong control frame is received from the server. This is a critical issue because: WebSocket servers commonly send ping frames to keep connections alive Clients send ping frames to verify connection health The receive callback never receives the ping/pong frame - the error occurs before the frame reaches user code Steps to Reproduce Create a WebSocket connection to any server that supports ping/pong (e.g., wss://echo.websocket.org): import Foundation import Network // MARK: - WebSocket Ping/Pong EINVAL Bug Reproduction // This sample demonstrates that NetworkConnection<WebSocket> throws EINVAL // when receiving ping or pong control frames. @main struct WebSocketPingPongBug { static func main() async { print("=== WebSocket Ping/Pong EINVAL Bug Reproduction ===\n") do { try await testPingPong() } catch { print("Test failed with error: \(error)") } } static func testPingPong() async throws { let host = "echo.websocket.org" let port: UInt16 = 443 print("Connecting to wss://\(host)...") let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host(host), port: NWEndpoint.Port(rawValue: port)! ) try await withNetworkConnection(to: endpoint, using: { WebSocket { TLS { TCP() } } }) { connection in print("Connected!\n") // Start receive loop in background let receiveTask = Task { var messageCount = 0 while !Task.isCancelled { do { let (data, metadata) = try await connection.receive() messageCount += 1 print("[\(messageCount)] Received frame - opcode: \(metadata.opcode)") if let text = String(data: data, encoding: .utf8) { print("[\(messageCount)] Content: \(text)") } else { print("[\(messageCount)] Binary data: \(data.count) bytes") } } catch let error as NWError { if case .posix(let code) = error, code == .EINVAL { print("❌ EINVAL error occurred! (POSIXErrorCode 22: Invalid argument)") print(" This is the bug - ping/pong frame caused EINVAL") // Continue to demonstrate workaround continue } print("Receive error: \(error)") break } catch { print("Receive error: \(error)") break } } } // Wait for initial message from server try await Task.sleep(for: .seconds(2)) // Test 1: Send text message (should work) print("\n--- Test 1: Sending text message ---") try await connection.send("Hello, WebSocket!") print("✅ Text message sent") try await Task.sleep(for: .seconds(1)) // Test 2: Send ping (pong response will cause EINVAL) print("\n--- Test 2: Sending ping frame ---") print("Expecting EINVAL when pong is received...") let pingMetadata = NWProtocolWebSocket.Metadata(opcode: .ping) try await connection.ping(Data()) { pingMetadata } print("✅ Ping sent, waiting for pong...") // Wait for pong response try await Task.sleep(for: .seconds(2)) // Cleanup receiveTask.cancel() print("\n=== Test Complete ===") print("If you saw 'EINVAL error occurred!' above, the bug is reproduced.") } } } The receive() call fails with error when pong arrives: ❌ EINVAL error occurred! (POSIXErrorCode 22: Invalid argument) Test Results Scenario Result Send/receive text (opcode 1) ✅ OK Client sends ping, receives pong ❌ EINVAL on pong receive Expected Behavior The receive() method should successfully return ping and pong frames, or at minimum, handle them internally without throwing an error. The autoReplyPing option should allow automatic pong responses without disrupting the receive loop. Actual Behavior When a ping or pong control frame is received: The receive() method throws NWError.posix(.EINVAL) The frame never reaches user code (no opcode check is possible) The connection remains valid, but the receive loop is interrupted Workaround Catch the EINVAL error and restart the receive loop: while !Task.isCancelled { do { let received = try await connection.receive() // Process message } catch let error as NWError { if case .posix(let code) = error, code == .EINVAL { // Control frame caused EINVAL, continue receiving continue } throw error } } This workaround allows continued operation but: Cannot distinguish between ping-related EINVAL and other EINVAL errors Cannot access the ping/pong frame content Cannot implement custom ping/pong handling Impact WebSocket connections to servers that send periodic pings will experience repeated EINVAL errors Applications must implement workarounds that may mask other legitimate errors Additional Information Packet capture confirms ping/pong frames are correctly transmitted at the network level The error occurs in the Network framework's internal processing, before reaching user code
5
0
216
Dec ’25
Network Extension – Delayed Startup Time
I've implemented a custom VPN system extension for macOS, utilizing Packet Tunnel Provider. One of the users reported a problem: he was connected to the VPN, and then his Mac entered sleep mode. Upon waking, the VPN is supposed to connect automatically (because of the on-demand rules). The VPN's status changed to 'connecting', but it remained stuck in this status. From my extension logs, I can see that the 'startTunnelWithOption()' function was called 2 minutes after the user clicked the 'connect' button. From the system logs, I noticed some 'suspicious' logs, but I can't be sure if they are related to the problem. Some of them are: kernel: (Sandbox) Sandbox: nesessionmanager(562) deny(1) system-fsctl (_IO "h" 47) entitlement com.apple.developer.endpoint-security.client not present or not true (I don't need this entitlement at the extension) nesessionmanager: [com.apple.networkextension:] NESMVPNSession[Primary Tunnel:XXXXXX(null)]: Skip a start command from YYYYY:session in state connecting NetworkExtension.com.***: RunningBoard doesn't recognize submitted process - treating as a anonymous process sysextd: activateDecision found existing entry of same version: state activated_enabled, ID FAE... Are any of the logs related to the above problem? How can I debug such issues? What info should I get from the user?
5
0
242
Oct ’25
NWConnection cancel: Do we need to wait for pending receive callbacks to be cancelled?
Hi, I’m using Network Framework to implement a UDP client via NWConnection, and I’m looking for clarification about the correct and fully safe shutdown procedure, especially regarding resource release. I have initiated some pending receive calls on the NWConnection (using receive). After calling connection.cancel(), do we need to wait for the cancellation of these pending receives? As mentioned in this thread, NWConnection retains references to the receive closures and releases them once they are called. If a receive closure holds a reference to the NWConnection itself, do we need to wait for these closures to be called to avoid memory leaks? Or, if there are no such retained references, we don't need to wait for the cancellation of the pending I/O and cancelled state for NWConnection?
5
0
168
Dec ’25
Will an iPhone reconnect automatically to a wifi network we connected to previously with joinOnce = false?
PLATFORM AND VERSION: iOS Development environment: Xcode 15.3, macOS 14.7.1 (23H222) Run-time configuration: iOS 18.3.1 DESCRIPTION OF PROBLEM: Our app uses NEHotspotConfigurationManager with joinOnce set to false to connect to an IoT device's Wi-Fi hotspot. Later, we programmatically disconnect from this hotspot. We are wondering if, after this programmatic disconnection, there is a possibility that the iPhone will automatically reconnect to the hotspot (even when our app is not running). Does it matter if the hotspot's SSID is hidden or not? This concern arises because the iPhone is already familiar with the hotspot's network configuration. Our testing indicates that this does not happen, but we want to be certain. This is a behavior we do NOT want to occur. We set joinOnce to false because we experience connectivity issues with the IoT device when joinOnce is true (there are several discussions in forums regarding issues with setting it to true). Thank you. Thanks.
5
0
386
Mar ’25
XPC connection consistently invalidated on app upgrade
Hi, Our project is a MacOS SwiftUI GUI application that bundles a System Network Extension, signed with a Developer ID certificate for distribution outside of the app store. The system network extension is used to write a packet tunnel provider. The signing of the app & network extension is handled by XCode (v16.0.0), we do not run codesign ourselves. We have no issues with XPC or the system network extension during normal usage, nor when the application is installed on a user's device for the first time. The problem only arises when the user upgrades the application. I have experienced this issue myself, as have our users. It's been reported on Apple Silicon macbooks running at least macOS 15.3.2. Much like the SimpleFirewall example (which we used as a reference), we use XPC for basic communication of state between the app and NE. These XPC connections stop working when the user installs a new version of the app, with OS logs from the process indicating that the connection is immediately invalidated. Subsequent connection attempts are also immediately invalidated. Toggling the VPN in system settings (or via the app) does not resolve the problem, nor does restarting the app, nor does deleting and reinstalling the app, nor does restarting the device. The only reliable workaround is to delete the system extension in Login Items & Extensions, under Network Extensions. No device restart is necessary to garbage collect the old extension - once the extension is reapproved by the user, the XPC issue resolves itself. This would be an acceptable workaround were it possible to automate the deleting of the system extension, but that appears deliberately not possible, and requiring our users to do this each time they update is unreasonable. When the upgraded app is opened for the first time, the OSSystemExtensionRequest request is sent, and the outcome is that the previously installed system network extension is replaced, as both the CFBundleVersion and CFBundleShortVersionString differ. When this issue is encountered, the output of systemextensionsctl list shows the later version is installed and activated. I've been able to reproduce this bug on my personal laptop, with SIP on and systemextensionsctl developer off, but on my work laptop with SIP off and systemextensionsctl developer on (where the network extension is replaced on each activation request, instead of only when the version strings differ), I do not encounter this issue, which leads me to believe it has something to do with the notarization process. We notarize the pkg using xcrun notarytool, and then staple to the pkg. This is actually the same issue described in: https://developer.apple.com/forums/thread/711713 https://developer.apple.com/forums/thread/667597 https://developer.apple.com/forums/thread/742992 https://developer.apple.com/forums/thread/728063 but it's been a while since any of these threads were updated, and we've made attempts to address it off the suggestions in the threads to no avail. Those suggestions are: Switching to a .pkg installer from a .dmg As part of the .pkg preinstall, doing all of the following: Stopping the VPN (scutil --nc stop), shutting down the app (using osascript 'quit app id'), and deleting the app (which claims to delete the network extension, but not the approval in Login Items & Extensions remains??), by running rm -rf on the bundle in /Applications As part of the .pkg postinstall: Forcing macOS to ingest the App bundle's notarization ticket using spctl --assess. Ensuring NSXPCListener.resume() is called after autoreleasepool { NEProvider.startSystemExtensionMode() } (mentioned in a forum thread above as a fix, did not help.) One thing I'm particularly interested in is the outcome of this feedback assistant ticket, as I can't view it: FB11086599. It was shared on this forum in the first thread above, and supposedly describes the same issue. I almost find it hard to believe that this issue has been around for this many years without a workaround (there's system network extension apps out there that appear to work fine when updating, are they not using XPC?), so I wonder if there's a fix described in that FB ticket. Since I can't view that above feedback ticket, I've created my own: FB17032197
5
0
383
Jun ’25
Network Push Provider Wifi Selection Behavior
In our App, we have a network extension with a NEAppPushProvider subclass running. We run the following steps Setup a dual-band wireless router per the following: Broadcasting 2.4 GHz and 5 GHz channels Same SSID names for both channels Connected to the production network to the router DHCP assigning addresses in the 10.1.x.x network Connect the mobile device to the 5 GHz network (if needed, turn off the 2.4 GHz network temporarily; once the device connects to the 5 GHz network, the 2.4 GHz network can be turned back on). Create a NEAppPushManager in the App, using the SSID from the above mentioned network and set it to the matchSSIDs property. Call saveToPreferences() on the push manager to save. A. We have UI that shows the extension has been started and it has connected to the server successfully. Walk out of the range of the 5 GHz channel of the router, but stay within range of the 2.4ghz channel. Wait for the mobile device to connect to the 2.4 GHz channel. Expected: The extension would reconnect to the 2.4ghz network. Observed: The extension does not reconnect. Checking the logs for the extension we see that the following was called in the push provider subclass. stop(with:completionHandler:) > PID: 808 | 🗒️🛑 Stopped with reason 3: "noNetworkAvailable" The expectation is that start() on the NEAppPushProvider subclass would be called. Is this an incorrect expectation? How does the NEAppPushProvider handle same network SSID roaming among various band frequencies? I looked at the documentation and did not find any settings targeting 2.4 or 5 ghz networks. Please advise on what to do.
5
1
136
Apr ’25
Network System Extension cannot use network interface of another VPN
Hi, Our project is a MacOS SwiftUI GUI application that bundles a (Sandboxed) System Network Extension, signed with a Developer ID certificate for distribution outside of the app store. The system network extension is used to write a packet tunnel provider (NEPacketTunnelProvider), as our project requires the creation of a TUN device. In order for our System VPN to function, it must reach out to a (self-hosted) server (i.e. to discover a list of peers). Being self-hosted, this server is typically not accessible via the public web, and may only be accessible from within a VPN (such as those also implemented using NEPacketTunnelProvider, e.g. Tailscale, Cloudflare WARP). What we've discovered is that the networking code of the System Network Extension process does not attempt to use the other VPN network interfaces (utunX) on the system. In practice, this means requests to IPs and hostnames that should be routed to those interfaces time out. Identical requests made outside of the Network System Extension process use those interfaces and succeed. The simplest example is where we create a URLSession.downloadTask for a resource on the server. A more complicated example is where we execute a Go .dylib that continues to communicate with that server. Both types of requests time out. Two noteworthy logs appear when packets fail to send, both from the kernel 'process': cfil_hash_entry_log:6088 <CFIL: Error: sosend_reinject() failed>: [30685 com.coder.Coder-Desktop.VPN] <UDP(17) out so b795d11aca7c26bf 57728068503033955 57728068503033955 age 0> lport 3001 fport 3001 laddr 100.108.7.40 faddr 100.112.177.88 hash 58B15863 cfil_service_inject_queue:4472 CFIL: sosend() failed 49 I also wrote some test code that probes using a UDP NWConnection and NWPath availableInterfaces. When run from the GUI App, multiple interfaces are returned, including the one that routes the address, utun5. When ran from within the sysex, only en0 is returned. I understand routing a VPN through another is unconventional, but we unfortunately do need this functionality one way or another. Is there any way to modify which interfaces are exposed to the sysex? Additionally, are these limitations of networking within a Network System Extension documented anywhere? Do you have any ideas why this specific limitation might exist?
5
2
343
Jul ’25
adhoc ipa, installed on iOS 18 devices, udp and tcp cannot access the local network, such as 17.25.11.128
I have read all the information and forum posts about local network, such as TN3179, etc., and have added NSLocalNetworkUsageDescription, but it does not solve my problem. The problem I encountered is described as follows: Device: iOS18.1.1 Signing method: automatic Xcode debug directly runs, and the app can access 17.25.11.128 normally. However, relase run or packaged into adhoc installation, this IP cannot be accessed. There is a phenomenon that the app package of the App Store can also be used. Our test team has few iOS18+ devices, and internal testing is not possible. Please contact us as soon as possible, thank you. ======= 我已经了解了所有关于local network 相关的资料和论坛帖子,比如TN3179 等等, 已经添加了 NSLocalNetworkUsageDescription, 但是不解决我的问题。 我遇到的问题描述如下: 设备:iOS18.1.1 签名方式:自动 xcode debug 直接运行,app是可以正常访问17.25.11.128的。 但是 relase run 或者 打包成 adhoc 安装,就无法访问这个IP了。 有一个现象, App Store 的app包 也是可以的。 我们的测试团队,iOS18+的设备就没几个,还不能内部测试了。请尽快联系我们,谢谢。
5
0
258
May ’25
Replacing Packet Filter (pf) with Content Filter for VPN Firewall Use Case
Hi, We're in the process of following Apple’s guidance on transitioning away from Packet Filter (pf) and migrating to a Network Extension-based solution that functions as a firewall. During this transition, we've encountered several limitations with the current Content Filter API and wanted to share our findings. Our VPN client relies on firewall functionality to enforce strict adherence to split tunneling rules defined via the routing table. This ensures that no traffic leaks outside the VPN tunnel, which is critical for our users for a variety of reasons. To enforce this, our product currently uses interface-scoped rules to block all non-VPN traffic outside the tunnel. Replicating this behavior with the Content Filter API (NEFilterDataProvider) appears to be infeasible today. The key limitation we've encountered is that the current Content Filter API does not expose information about the network interface associated with a flow. As a workaround, we considered using the flow’s local endpoint IP to infer the interface, but this data is not available until after returning a verdict to peek into the flow’s data—at which point the connection has already been established. This can result in connection metadata leaking outside the tunnel, which may contain sensitive information depending on the connection. What is the recommended approach for this use case? NEFilterPacketProvider? This may work, but it has a negative impact on network performance. Using a Packet Tunnel Provider and purely relying on enforceRoutes? Would this indeed ensure that no traffic can leak by targeting a specific interface or by using a second VPN extension? And more broadly—especially if no such approach is currently feasible with the existing APIs—we're interpreting TN3165 as a signal that pf should be considered deprecated and may not be available in the next major macOS release. Is that a reasonable interpretation?
5
0
209
May ’25
XPC listener initialized in System Extesnion invalidates incoming connection under certain conditions
I found a problem where a process tries to connect to System Extension and connection is invalidated. XPC listener has to be disposed and initialized again. This happens when System Extension executes tasks in following order: NSXPCListener initialized NSXPCListener.resume() NSProvider.startSystemExtensionMode() Result: Connection is invalidated and not only that the client has to retry connection, nut also System Extension must reinitialize listener (execute step 1 and 2). However if I call NSProvider.startSystemExtensionMode() NSXPCListener initialized NSXPCListener.resume() It works as expected and even if the connection is invalidated/interrupted, client process can always reconnect and no other action is necessary in System Extension (no need to reinitialize XPC listener), In Apple docs about NSProvider.startSystemExtensionMode() it says that this method starts handling request, but in another online article written by Scott Knight I found that startSystemExtensionMode() also starts listener server. Is that right? PLease could you add this info into the docs if it is so? https://knight.sc/reverse%20engineering/2019/08/24/system-extension-internals.html I would like to use following logic: Call NSProvider.startSystemExtensionMode() only under certain circumstances - I have received some configuration that I need to process and do some setup. If I don't receive it, there is no reason to call startSystemExtensionMode() yet, I don't need to handle handleNewFlow() yet. Connect XPC client to System Extension under certain conditions. Ideally communicate with client even though System Extension is not handling network requests yet, that is without receiving handleNewFlow(). Basically I consider XPC and System Extension handling network requests as separate things. Is that correct, are they separate and independent? Does XPC communication really depend on calling startSystemExtensionMode()? Another potential issue: Is it possible that XPC listener fails to validate connection when client tries to connect before System Extension manages to complete init and park the main thread in CFRunLoop? Note: These querstions arose mostly from handling upgrades of System Extension (extension is already running, network filter is created and is connected and new version of the app upgrades System Exension). Thanks.
5
0
1.3k
Apr ’25
Socket Becomes Unresponsive in Local Connectivity Extension After Lock Screen
I’m developing an app designed for hospital environments, where public internet access may not be available. The app includes two components: the main app and a Local Connectivity Extension. Both rely on persistent TCP socket connections to communicate with a local server. We’re observing a recurring issue where the extension’s socket becomes unresponsive every 1–3 hours, but only when the device is on the lock screen, even if the main app remains in the foreground. When the screen is not locked, the connection is stable and no disconnections occur. ❗ Issue Details: • What’s going on: The extension sends a keep-alive ping packet every second, and the server replies with a pong and a system time packet. • The bug: The server stops receiving keep alive packets from the extension.  • On the server, we detect about 30 second gap on the server, a gap that shows no packets were received by the extension. This was confirmed via server logs and Wireshark).  • On the extension, from our logs there was no gap in sending packets. From it’s perspective, all packets were sent with no error.  • Because no packet are being received by the server, no packets will be sent to the extension. Eventually the server closes the connection due to keep-alive timeout.  • FYI we log when the NEAppPushProvider subclass sleeps and it did NOT go to sleep while we were debugging. 🧾 Example Logs: Extension log: 2025-03-24 18:34:48.808 sendKeepAliveRequest() 2025-03-24 18:34:49.717 sendKeepAliveRequest() 2025-03-24 18:34:50.692 sendKeepAliveRequest() ... // continuous sending of the ping packet to the server, no problems here 2025-03-24 18:35:55.063 sendKeepAliveRequest() 2025-03-24 18:35:55.063 keepAliveTimer IS TIME OUT... in CoreService. // this is triggered because we did not receive any packets from the server Server log: 2025-03-24 18:34:16.298 No keep-alive received for 16 seconds... connection ID=95b3... // this shows that there has been no packets being received by the extension ... 2025-03-24 18:34:30.298 Connection timed out on keep-alive. connection ID=95b3... // eventually closes due to no packets being received 2025-03-24 18:34:30.298 Remote Subsystem Disconnected {name=iPhone|Replica-Ext|...} ✅ Observations: • The extension process continues running and logging keep-alive attempts. • However, network traffic stops reaching the server, and no inbound packets are received by the extension. • It looks like the socket becomes silently suspended or frozen, without being properly closed or throwing an error. ❓Questions: • Do you know why this might happen within a Local Connectivity Extension, especially under foreground conditions and locked ? • Is there any known system behavior that might cause the socket to be suspended or blocked in this way after running for a few hours? Any insights or recommendations would be greatly appreciated. Thank you!
5
0
151
Apr ’25
Running headless app as root for handling VPN and launching microservices
Hello to all I have coded in swift a headless app, that launches 3 go microservices and itself. The app listens via unix domain sockets for commands from the microservices and executes different VPN related operations, using the NEVPNManager extension. Because there are certificates and VPN operations, the headless app and two Go microservices must run as root. The app and microservices run perfectly when I run in Xcode launching the swift app as root. However, I have been trying for some weeks already to modify the application so at startup it requests the password and runs as root or something similar, so all forked apps also run as root. I have not succeeded. I have tried many things, the last one was using SMApp but as the swift app is a headless app and not a CLI command app it can not be embedded. And CLI apps can not get the VPN entitlements. Can anybody please give me some pointers how can I launch the app so it requests the password and runs as root in background or what is the ideal framework here? thank you again.
5
0
237
Dec ’25