Please consider this trivial C code which deals with BSD sockets. This will illustrate an issue with sendto() which seems to be impacted by the recent "Local Network" restrictions on 15.3.1 macos.
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "sys/socket.h"
#include <string.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <net/if.h>
// prints out the sockaddr_in6
void print_addr(const char *msg_prefix, struct sockaddr_in6 sa6) {
char addr_text[INET6_ADDRSTRLEN] = {0};
printf("%s%s:%d, addr family=%u\n",
msg_prefix,
inet_ntop(AF_INET6, &sa6.sin6_addr, (char *) &addr_text, INET6_ADDRSTRLEN),
sa6.sin6_port,
sa6.sin6_family);
}
// creates a datagram socket
int create_dgram_socket() {
const int fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd < 0) {
perror("Socket creation failed");
return -1;
}
return fd;
}
// returns a string representing the current local time
char *current_time() {
time_t seconds_since_epoch;
time(&seconds_since_epoch);
char *res = ctime(&seconds_since_epoch);
const size_t len = strlen(res);
// strip off the newline character that's at the end of the ctime() output
res[len - 1] = '\0';
return res;
}
// Creates a datagram socket and then sends a messages (through sendto()) to a valid
// multicast address. This it does two times, to the exact same destination address from
// the exact same socket.
//
// Between the first and the second attempt to sendto(), there is
// a sleep of 1 second.
//
// The first time, the sendto() succeeds and claims to have sent the expected number of bytes.
// However system logs (generated through "log collect") seem to indicate that the message isn't
// actually sent (there's a "cfil_service_inject_queue:4466 CFIL: sosend() failed 65" in the logs).
//
// The second time the sendto() returns a EHOSTUNREACH ("No route to host") error.
//
// If the sleep between these two sendto() attempts is removed then both the attempts "succeed".
// However, the system logs still suggest that the message isn't actually sent.
int main() {
printf("current process id:%ld parent process id: %ld\n", (long) getpid(), (long) getppid());
// valid multicast address as specified in
// https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
const char *ip6_addr_str = "ff01::1";
struct in6_addr ip6_addr;
int rv = inet_pton(AF_INET6, ip6_addr_str, &ip6_addr);
if (rv != 1) {
fprintf(stderr, "failed to parse ipv6 addr %s\n", ip6_addr_str);
exit(EXIT_FAILURE);
}
// create a AF_INET6 SOCK_DGRAM socket
const int sock_fd = create_dgram_socket();
if (sock_fd < 0) {
exit(EXIT_FAILURE);
}
printf("created a socket, descriptor=%d\n", sock_fd);
const int dest_port = 12345; // arbitrary port
struct sockaddr_in6 dest_sock_addr;
memset((char *) &dest_sock_addr, 0, sizeof(struct sockaddr_in6));
dest_sock_addr.sin6_addr = ip6_addr; // the target multicast address
dest_sock_addr.sin6_port = htons(dest_port);
dest_sock_addr.sin6_family = AF_INET6;
print_addr("test will attempt to sendto() to destination host:port -> ", dest_sock_addr);
const char *msg = "hello";
const size_t msg_len = strlen(msg) + 1;
for (int i = 1; i <= 2; i++) {
if (i != 1) {
// if not the first attempt, then sleep a while before attempting to sendto() again
int num_sleep_seconds = 1;
printf("sleeping for %d second(s) before calling sendto()\n", num_sleep_seconds);
sleep(num_sleep_seconds);
}
printf("%s attempt %d to sendto() %lu bytes\n", current_time(), i, msg_len);
const size_t num_sent = sendto(sock_fd, msg, msg_len, 0, (struct sockaddr *) &dest_sock_addr,
sizeof(dest_sock_addr));
if (num_sent == -1) {
fprintf(stderr, "%s ", current_time());
perror("sendto() failed");
close(sock_fd);
exit(EXIT_FAILURE);
}
printf("%s attempt %d of sendto() succeeded, sent %lu bytes\n", current_time(), i, num_sent);
}
return 0;
}
What this program does is, it uses the sendto() system call to send a message over a datagram socket to a (valid) multicast address. It does this twice, from the same socket to the same target address. There is a sleep() of 1 second between these two sendto() attempts.
Copy that code into noroutetohost.c and compile:
clang noroutetohost.c
Then run:
./a.out
This generates the following output:
current process id:58597 parent process id: 21614
created a socket, descriptor=3
test will attempt to sendto() to destination host:port ->ff01::1:14640, addr family=30
Fri Mar 14 20:34:09 2025 attempt 1 to sendto() 6 bytes
Fri Mar 14 20:34:09 2025 attempt 1 of sendto() succeeded, sent 6 bytes
sleeping for 1 second(s) before calling sendto()
Fri Mar 14 20:34:10 2025 attempt 2 to sendto() 6 bytes
Fri Mar 14 20:34:10 2025 sendto() failed: No route to host
Notice how the first call to sendto() "succeeds", even the return value (that represents the number of bytes sent) matches the number of bytes that were supposed to be sent. Then notice how the second attempt fails with a EHOSTUNREACH ("No route to host") error. Looking through the system logs, it appears that the first attempt itself has failed:
2025-03-14 20:34:09.474797 default kernel cfil_hash_entry_log:6082 <CFIL: Error: sosend_reinject() failed>: [58597 a.out] <UDP(17) out so 891be95f3a70c605 22558774573152560 22558774573152560 age 0> lport 0 fport 12345 laddr :: faddr ff01::1 hash 1003930
2025-03-14 20:34:09.474806 default kernel cfil_service_inject_queue:4466 CFIL: sosend() failed 65
(notice the time on that log messages, they match the first attempt from the program's output log)
So even though the first attempt failed, it never got reported back to the application. Then after sleeping for (an arbitrary amount of) 1 second, the second call fails with the EHOSTUNREACH. The system logs don't show any error (at least not the one similar to that previous one) for the second call.
If I remove that sleep() between those two attempts, then both the sendto() calls "succeed" (and return the expected value for the number of bytes sent). However, the system logs show that the first call (and very likely even the second) has failed with the exact same log message from the kernel like before.
If I'm not wrong then this appears to be some kind of a bug in the "local network" restrictions. Should this be reported? I can share the captured logs but I would prefer to do it privately for this one.
Another interesting thing in all this is that there's absolutely no notification to the end user (I ran this program from the Terminal) about any of the "Local Network" restrictions.
Networking
RSS for tagExplore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm trying to use ThreadNetwork API to manage TheradNetworks on device (following this documentation: https://developer.apple.com/documentation/threadnetwork/), but while some functions on THClient work (such as getPreferedNetwork), most don't (storeCredentials, retrieveAllCredentials). When calling these functions I get the following warning/error:
Client: -[THClient getConnectionEntitlementValidity]_block_invoke - Error:
-[THClient storeCredentialsForBorderAgent:activeOperationalDataSet:completion:]_block_invoke:701: - Error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process." UserInfo={NSDebugDescription=The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process.}
Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process." UserInfo={NSDebugDescription=The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process.}
Failed to store Thread credentials: Couldn’t communicate with a helper application.
STEPS TO REPRODUCE
Create new project
Add Thread Network capability via Xcode UI (com.apple.developer.networking.manage-thread-network-credentials)
Trigger storeCredentials
let extendedMacData = "9483C451DC3E".hexadecimal
let tlvHex = "0e080000000000010000000300001035060004001fffe002083c66f0dc9ef53f1c0708fdb360c72874da9905104094dce45388fd3d3426e992cbf0697b030d474c2d5332302d6e65773030310102250b04106c9f919a4da9b213764fc83f849381080c0402a0f7f8".hexadecimal
// Initialize the THClient
let thClient = THClient()
// Store the credentials
await thClient.storeCredentials(forBorderAgent: extendedMacData!, activeOperationalDataSet: tlvHex!) { error in
if let error = error {
print(error)
print("Failed to store Thread credentials: \(error.localizedDescription)")
} else {
print("Successfully stored Thread credentials")
}
}
NOTES:
I tried with first calling getPreferedNetwork to initiate network permission dialog
Tried adding meshcop to bojur services
Tried with different release and debug build configurations
For important background information, read Extra-ordinary Networking before reading this.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Working with a Wi-Fi Accessory
Building an app that works with a Wi-Fi accessory presents specific challenges. This post discusses those challenges and some recommendations for how to address them.
Note While my focus here is iOS, much of the info in this post applies to all Apple platforms.
IMPORTANT iOS 18 introduced AccessorySetupKit, a framework to simplify the discovery and configuration of an accessory. I’m not fully up to speed on that framework myself, but I encourage you to watch WWDC 2024 Session 10203 Meet AccessorySetupKit and read the framework documentation.
IMPORTANT iOS 26 introduced WiFiAware, a framework for setting up communication with Wi-Fi Aware accessories. Wi-Fi Aware is an industry standard to securely discover, pair, and communicate with nearby devices. This is especially useful for stand-alone accessories (defined below). For more on this framework, watch WWDC 2025 Session 228 Supercharge device connectivity with Wi-Fi Aware and read the framework documentation. For information on how to create a Wi-Fi Aware accessory that works with iPhone, go to Developer > Accessories, download Accessory Design Guidelines for Apple Devices, and review the Wi-Fi Aware chapter.
Accessory Categories
I classify Wi-Fi accessories into three different categories.
A bound accessory is ultimately intended to join the user’s Wi-Fi network. It may publish its own Wi-Fi network during the setup process, but the goal of that process is to get the accessory on to the existing network. Once that’s done, your app interacts with the accessory using ordinary networking APIs.
An example of a bound accessory is a Wi-Fi capable printer.
A stand-alone accessory publishes a Wi-Fi network at all times. An iOS device joins that network so that your app can interact with it. The accessory never provides access to the wider Internet.
An example of a stand-alone accessory is a video camera that users take with them into the field. You might want to write an app that joins the camera’s network and downloads footage from it.
A gateway accessory is one that publishes a Wi-Fi network that provides access to the wider Internet. Your app might need to interact with the accessory during the setup process, but after that it’s useful as is.
An example of this is a Wi-Fi to WWAN gateway.
Not all accessories fall neatly into these categories. Indeed, some accessories might fit into multiple categories, or transition between categories. Still, I’ve found these categories to be helpful when discussing various accessory integration challenges.
Do You Control the Firmware?
The key question here is Do you control the accessory’s firmware? If so, you have a bunch of extra options that will make your life easier. If not, you have to adapt to whatever the accessory’s current firmware does.
Simple Improvements
If you do control the firmware, I strongly encourage you to:
Support IPv6
Implement Bonjour [1]
These two things are quite easy to do — most embedded platforms support them directly, so it’s just a question of turning them on — and they will make your life significantly easier:
Link-local addresses are intrinsic to IPv6, and IPv6 is intrinsic to Apple platforms. If your accessory supports IPv6, you’ll always be able to communicate with it, regardless of how messed up the IPv4 configuration gets.
Similarly, if you support Bonjour, you’ll always be able to find your accessory on the network.
[1] Bonjour is an Apple term for three Internet standards:
RFC 3927 Dynamic Configuration of IPv4 Link-Local Addresses
RFC 6762 Multicast DNS
RFC 6763 DNS-Based Service Discovery
WAC
For a bound accessory, support Wireless Accessory Configuration (WAC). This is a relatively big ask — supporting WAC requires you to join the MFi Program — but it has some huge benefits:
You don’t need to write an app to configure your accessory. The user will be able to do it directly from Settings.
If you do write an app, you can use the EAWiFiUnconfiguredAccessoryBrowser class to simplify your configuration process.
HomeKit
For a bound accessory that works in the user’s home, consider supporting HomeKit. This yields the same onboarding benefits as WAC, and many other benefits as well. Also, you can get started with the HomeKit Open Source Accessory Development Kit (ADK).
Bluetooth LE
If your accessory supports Bluetooth LE, think about how you can use that to improve your app’s user experience. For an example of that, see SSID Scanning, below.
Claiming the Default Route, Or Not?
If your accessory publishes a Wi-Fi network, a key design decision is whether to stand up enough infrastructure for an iOS device to make it the default route.
IMPORTANT To learn more about how iOS makes the decision to switch the default route, see The iOS Wi-Fi Lifecycle and Network Interface Concepts.
This decision has significant implications. If the accessory’s network becomes the default route, most network connections from iOS will be routed to your accessory. If it doesn’t provide a path to the wider Internet, those connections will fail. That includes connections made by your own app.
Note It’s possible to get around this by forcing your network connections to run over WWAN. See Binding to an Interface in Network Interface Techniques and Running an HTTP Request over WWAN. Of course, this only works if the user has WWAN. It won’t help most iPad users, for example.
OTOH, if your accessory’s network doesn’t become the default route, you’ll see other issues. iOS will not auto-join such a network so, if the user locks their device, they’ll have to manually join the network again.
In my experience a lot of accessories choose to become the default route in situations where they shouldn’t. For example, a bound accessory is never going to be able to provide a path to the wider Internet so it probably shouldn’t become the default route. However, there are cases where it absolutely makes sense, the most obvious being that of a gateway accessory.
Acting as a Captive Network, or Not?
If your accessory becomes the default route you must then decide whether to act like a captive network or not.
IMPORTANT To learn more about how iOS determines whether a network is captive, see The iOS Wi-Fi Lifecycle.
For bound and stand-alone accessories, becoming a captive network is generally a bad idea. When the user joins your network, the captive network UI comes up and they have to successfully complete it to stay on the network. If they cancel out, iOS will leave the network. That makes it hard for the user to run your app while their iOS device is on your accessory’s network.
In contrast, it’s more reasonable for a gateway accessory to act as a captive network.
SSID Scanning
Many developers think that TN3111 iOS Wi-Fi API overview is lying when it says:
iOS does not have a general-purpose API for Wi-Fi scanning
It is not.
Many developers think that the Hotspot Helper API is a panacea that will fix all their Wi-Fi accessory integration issues, if only they could get the entitlement to use it.
It will not.
Note this comment in the official docs:
NEHotspotHelper is only useful for hotspot integration. There are both technical and business restrictions that prevent it from being used for other tasks, such as accessory integration or Wi-Fi based location.
Even if you had the entitlement you would run into these technical restrictions. The API was specifically designed to support hotspot navigation — in this context hotspots are “Wi-Fi networks where the user must interact with the network to gain access to the wider Internet” — and it does not give you access to on-demand real-time Wi-Fi scan results.
Many developers look at another developer’s app, see that it’s displaying real-time Wi-Fi scan results, and think there’s some special deal with Apple that’ll make that work.
There is not.
In reality, Wi-Fi accessory developers have come up with a variety of creative approaches for this, including:
If you have a bound accessory, you might add WAC support, which makes this whole issue go away.
In many cases, you can avoid the need for Wi-Fi scan results by adopting AccessorySetupKit.
You might build your accessory with a barcode containing the info required to join its network, and scan that from your app. This is the premise behind the Configuring a Wi-Fi Accessory to Join the User’s Network sample code.
You might configure all your accessories to have a common SSID prefix, and then take advantage of the prefix support in NEHotspotConfigurationManager. See Programmatically Joining a Network, below.
You might have your app talk to your accessory via some other means, like Bluetooth LE, and have the accessory scan for Wi-Fi networks and return the results.
Programmatically Joining a Network
Network Extension framework has an API, NEHotspotConfigurationManager, to programmatically join a network, either temporarily or as a known network that supports auto-join. For the details, see Wi-Fi Configuration.
One feature that’s particularly useful is it’s prefix support, allowing you to create a configuration that’ll join any network with a specific prefix. See the init(ssidPrefix:) initialiser for the details.
For examples of how to use this API, see:
Configuring a Wi-Fi Accessory to Join the User’s Network — It shows all the steps for one approach for getting a non-WAC bound accessory on to the user’s network.
NEHotspotConfiguration Sample — Use this to explore the API in general.
Secure Communication
Users expect all network communication to be done securely. For some ideas on how to set up a secure connection to an accessory, see TLS For Accessory Developers.
Revision History
2025-11-05 Added a link to the Accessory Design Guidelines for Apple Devices.
2025-06-19 Added a preliminary discussion of Wi-Fi Aware.
2024-09-12 Improved the discussion of AccessorySetupKit.
2024-07-16 Added a preliminary discussion of AccessorySetupKit.
2023-10-11 Added the HomeKit section. Fixed the link in Secure Communication to point to TLS For Accessory Developers.
2023-07-23 First posted.
I have been using networking multicast permissions in my Xamarin application for UDP device discovery, and it has been working reliably for years. However, I am now encountering an issue specific to iPadOS 18.1 (potentially also iPadOS 18, though I haven’t tested this).
The issue is that my app no longer requests the required network permission on devices running iPadOS 18.1. On the other hand, the app works perfectly on iPhone and iPadOS 17.7 without any problems.
Has there been any change in the networking or permissions framework in iPadOS 18.1 that could cause this behavior? I would appreciate any guidance or insights to resolve this issue.
Thank you for your assistance!
Topic:
App & System Services
SubTopic:
Networking
Hi,
I’m urgently seeking assistance with an issue in my app development.
The app allows users to control which domains are routed through my relay servers (six server URLs).
However, I’ve encountered a problem:
When a single relay configuration (for a single server URL) contains more than 70 domains, only one configuration can be active at a time. If I manually activate another relay configuration (for another server URL), the previously activated one automatically deactivates.
Is there a way to overcome this limitation?
Also, is there a maximum amount of domains that can exist across the per-app relays?
I’m referencing the Apple documentation here:
https://developer.apple.com/documentation/networkextension/relays
Any guidance or insights into resolving this issue would be greatly appreciated!
Thank you in advance :)
Hi there, I'm trying to build a MacOS VPN application from scratch. My VPN application is slightly from normal ones,
It will include an authentication token and underlying process information (pid, application path etc.) in each connection made to the VPN gateway. Consider it a poor man's zerotrust implementation.
NetworkExtension and PacketTunnel is a must, thus to retrieve process information via audit tokens.
However, I'm unable to find any working examples that can be built on MacOS 15.X. I tried to open an TSI case but didn't receive anything useful.
Anyone?
During development, before things eventually go live, if the associated server for a message filter extension has a self signed SSL then if/how can test iPhones be configured such that the OS will connect to the server when they are performing a message filter query request?
I’m working with the NEHotspotHelper API in my iOS app, and I noticed the following log message in Console:
"(BUNDLE ID ) is using NEHotspotHelper API and it's unresponsive to API's evaluate command. The API gives 45 seconds to 3rd party apps to respond, and then it launches WebSheet to allow user to interact with the portal."
I have two different apps that both register a NEHotspotHelper handler:
App A checks for .evaluate and calls createResponse(.unsupportedNetwork) if we don’t manage that particular network.
App B registers for hotspot events but does not handle .evaluate at all.
In App A, whenever I see that “unresponsive” or “45 seconds” log, the system eventually launches the standard captive portal WebSheet. In App B, I never see those logs.
I have a few questions:
Are these “unresponsive” logs indeed triggered by the .evaluate command specifically?
In other words, do we only see that 45-second timeout and the subsequent WebSheet message if our app is registered to handle Evaluate but doesn’t respond quickly (or responds with .unsupportedNetwork)?
Is it best practice (or required) to always respond to .evaluate—for example, sending .unsupportedNetwork if we don’t plan on managing the user’s login or captive portal? Does ignoring .evaluate lead to other unexpected behavior or logs?
Should we still explicitly respond to Evaluate with .unsupportedNetwork? Or is it okay to skip Evaluate handling entirely on every app or invocation?
I’d love to confirm whether .evaluate handling is the direct cause of these logs, and how best to avoid the “unresponsive”/“45 seconds” fallback if our app isn’t intended to manage the portal.
Thanks in advance for any insights!
Topic:
App & System Services
SubTopic:
Networking
With the new macOS 15, Apple introduced the new Local Network Privacy feature.
This is causing issues for our customers as - even though they granted the required permission for our software - connections to a server in their local network are being blocked. The situation is not fixed by recent macOS updates.
As far as I know, this issue exists for machines running on Apple Silicon. Systems running macOS versions (e.g. Sonoma) are not affected.
Currently, the workaround is to re-enable the permission under Settings > Privacy & Security > Local Network. The list shows our application with an enabled checkbox. Users now have to de-select the box and then re-select it again for the application to work. They have to do this after each and every reboot of their system, which is slightly annoying (so at the moment we recommend to not upgrade macOS to Sequoia, if possible)
I did some research and saw that other products are also affected by this bug. Is there a solution to this issue or any plans to fix it?
We’ve been dealing with local network permission issues on macOS 15. Although 15.1 brought some improvement, users are now reporting similar problems again on 15.2.
Our setup:
A “launcher” app (installed from a web package, not sandboxed) uses NSTask to launch our main macOS app.
This macOS app connects to an iOS app via the local network.
We expect a local network permission prompt to appear when the new app launches, but for many users, it never does.
In cases where it worked on an earlier macOS version, there’s no entry in System Settings > Privacy & Security > Local Network, so they can’t toggle anything.
Oddly, if we run the macOS app directly in 15.2, local network access works, yet the privacy entry is still sometimes missing.
We haven’t found a clear way to troubleshoot this within the current API. Has anyone experienced a similar issue, or have suggestions on how to debug and resolve this? Thanks in advance!
I was trying to call getsockopt(fd, SOL_LOCAL, LOCAL_PEERCRED, ...), and by mistake passed a wrong value for the second parameter where it should be SOL_LOCAL. But the call still succeeded. Then I did more experiments and passed more random values for the second parameter, all succeeded. It seems there is a lack of parameter check in the implementation of getsockopt() , where it should return errors if people pass invalid parameters instead of succeeding silently. Hope the Apple engineers can help to validate and fix it.
We are trying to connect an accessory to the home's Wi-Fi network and we want to pass that name from the app to the accessory. Passing via Bluetooth.
Is there and API available on iOS to list the networks that the phone can see?
We've been directed here by Quinn in DTS.
We use multicast/broadcast messages extensively in our physical products for discovery purposes. If, for whatever reason, our customers cannot get this to work on their home Wi-Fi network, we advise that they connect to an iPhone hotspot to confirm behaviour and perform firmware updates as needed.
As of iOS 18, we're seeing odd behaviour when using Personal Hotspot. Interestingly, we're also seeing that client devices connected to the Hotspot network are not showing a Subnet Mask in the Wi-Fi Details screen in the iOS Settings app - I don't know if that's related. We're also seeing that screen show an IP address of 192.0.0.2 for all client iPhones connected to an iPhone Hotspot.
Getting more specific, we're seeing that multicast messages are no longer being received by clients when connected to an iPhone Hotspot where the iPhone running the hotspot is running iOS 18.0 or newer. By "multicast", I mean we're using a BSD socket to send data to 255.255.255.255
I've confirmed that our app has the multicast entitlement, the user has granted Local Network permission, and we've created a small sample app that demonstrates this behaviour perfectly - when connected to any other test network, multicast messages are received correctly by clients.
We've also confirmed that this behaviour doesn't happen when the iPhone running the hotspot is running older iOS versions. We've tried a number of iOS 17.x releases and a number of iOS 16.x releases specifically with our sample app, but have been using this exact code since our app's original iOS 9 deployment target and have had no issues until now.
Topic:
App & System Services
SubTopic:
Networking
We're seeing some new and odd behavior where our NEPacketTunnelProvider instance is receiving a stopTunnelWithReason:completionHandler: call with reason NEProviderStopReasonInternalError.
Can anyone shed some light into how to diagnose this situation?
Here are some basic details:
Our PacketTunnel has been in use for years and we only started seeing this issue recently.
We're able to reproduce this behavior with some light browsing.
The documentation provides no insight on why/when this might occur. Can anyone shed some light into how to diagnose this situation?
Things we’ve tried so far:
We grabbed a sysdiagnose and looked through the logs:
a. Right before the stopTunnel, we see log items referring to a "nesessionmanager" (PID 2038) getting killed. Presumably, this is due to hitting a highwater threshold. (See sysdiagnose items listing below)
b. Thinking these were due to memory pressure, we added logging of available/used memory.
c. We confirmed that the PacketTunnel was only using 11,808.73 KB.
d. Since, there is plenty of memory available the PacketTunnel was not killed for using too much memeory.
We wondered if this could be due to our UI's usage of objects like: NETunnelProviderManager and NETunnelProviderSession
a. We ran an experiment where we swiped closed the UI to ensure these manager/session objects are not used.
b. Without the UI, we still saw the random stopTunnel with NEProviderStopReasonInternalError.
We wondered if our routes were the problem, but they seem correct.
a. See the NEPacketTunnelNetworkSettings listing below
LISTING: From the system_logs.logarchive, the nesessionmanager log items:
2025-01-23 15:07:59.176146 -0800 0x278 memorystatus com.apple.xnu memorystatus: killing process 2038 [nesessionmanager] in high band ? (140) - memorystatus_available_pages: 18932 default kernel
2025-01-23 15:07:59.179641 -0800 0x278 memorystatus com.apple.xnu memorystatus: killing_highwater_process pid 2038 [nesessionmanager] (highwater 140) 7056KB - memorystatus_available_pages: 19161 compressor_size:69593 default kernel
2025-01-23 15:07:59.179888 -0800 0x278 memorystatus com.apple.xnu memorystatus: failed to kill a process and no memory was reclaimed default kernel
2025-01-23 15:07:59.185695 -0800 1 0x45e0c user/501/com.apple.nesessionmanager [2038] exited with exit reason (namespace: 1 code: 0x2) - JETSAM_REASON_MEMORY_HIGHWATER, ran for 266329ms default launchd
2025-01-23 15:07:59.231188 -0800 31 0x45bf2 com.apple.networkextension nesessionmanager(2038) exited default UserEventAgent
2025-01-23 15:07:59.253371 -0800 31 0x45bf2 com.apple.networkextension nesessionmanager exited with active sessions, re-launching nesessionmanager to clear agent status default UserEventAgent
LISTING: From the system_logs.logarchive, the stopTunnel from PID 2046
2025-01-23 15:07:59.201581 -0800 SamplePacketTunnel [Extension com.REDACTED.PacketTunnel]: Calling stopTunnelWithReason because: None
2025-01-23 15:08:20.783112 -0800 SamplePacketTunnel 2025-01-23 15:08:20,786 2046 ERROR REDACTED (285805) - Exiting after waiting for stopTunnelWithReason
LISTING: routes from NEPacketTunnelNetworkSettings
{
tunnelRemoteAddress = fd12:3456:789a:1::1
DNSSettings = {
protocol = cleartext
server = (
2606:4700:4700::1234,
2606:4700:4700::2345,
)
matchDomains = (
,
)
matchDomainsNoSearch = NO
}
IPv6Settings = {
configMethod = manual
addresses = (
fd12:3456:789a:1::1,
)
networkPrefixLengths = (
64,
)
includedRoutes = (
{
destinationAddress = 2606:4700:4700::2345
destinationNetworkPrefixLength = 128
},
{
destinationAddress = 2606:4700:4700::1234
destinationNetworkPrefixLength = 128
},
)
excludedRoutes = (
{
destinationAddress = REDACTED
destinationNetworkPrefixLength = 128
},
{
destinationAddress = REDACTED
destinationNetworkPrefixLength = 128
},
)
}
MTU = 3072
}
Thanks for taking a look, any help or suggestions would be greatly appreciated
I am writing an app using Microsoft's MAUI platform. I am posting this here because that team wants me to make an xcode project to help determine an issue I am having.
My MAUI app sends a broadcast packet on a UDP socket using address 255.255.255.255. This worked fine in iOS version 17.x. After upgrading my phone to iOS 18.x it stopped working.
The error I get is "no route to host".
The exact same code works fine on MacOS. It does not work on iPadOs 18.
My question here is 3 fold:
Did something specific change between iOS 17 and 18 that would cause a 'no route to host' error when sending a UDP broadcast packet?
Can someone provide sample code to show me how to do this type of broadcast using Swift in Xcode for iOS?
I read an article that said my app would need the com.apple.developer.networking.multicast entitlement in order to use boradcast functionality. This was introduced in iOS 14. Why did my app work fine in iOS 17 then? Is this what changed? Did this requirement use to be optional and is now required? I did get this entitlement from Apple and applied it to my provisioning profile and my app gave the same "no route to host" error. Why?
Hi,
I would like to confirm whether the matchDomains property in NERelayManager operates strictly at the Application Layer. Specifically, it seems that adding IPv4 addresses or IPv4 CIDR blocks to the matchDomains list does not work, as the relay manager appears unable to match them.
For example, I tried adding the following IPv4 patterns to the matchDomains list:
11.22.33.44
11.22..
11.22.*
However, these IPv4 addresses or patterns are not routed through my Relay server.
Additionally, I have observed that when using only the excludedDomains property, the desired IPv4 traffic is correctly routed to the relay server as expected.
My question is: Can IPv4 addresses or IPv4 CIDR ranges work with matchDomains? If not, is there an alternative approach to enable IPv4 matching while matchDomains is active?
Topic:
App & System Services
SubTopic:
Networking
Tags:
Extensions
Network Extension
Network
System Configuration
Hi,
I would like to confirm if the matchDomains property in NERelayManager operates exclusively at the application layer. Specifically, it seems that adding IPv4 addresses or IPv4 CIDR blocks to the matchDomains list does not work, as the relay manager appears unable to match them.
Relay Configuration
For example, I tried adding the following IPv4 patterns to the matchDomains list:
11.22.33.44
11.22..
11.22.*
In this configuration, I expected traffic to be routed to the relay server as defined by the matchDomains entries. However, the relay manager did not handle these IPv4 patterns as anticipated.
On the other hand, when using only the excludedDomains property, the desired IPv4 traffic is successfully routed to the relay server as expected.
Purpose of Forwarding IPv4 to the Relay Server
The primary reason for forwarding IPv4 traffic to the relay server is to address cases where certain applications—such as those developed with Flutter or React Native—use their own custom network stack. These custom network stacks often do not respect the relay configuration. As a result, even when these applications use domains that are matched by the relay manager’s matchDomains, their TCP connections to DNS-resolved IPv4 addresses bypass the relay server and connect directly to the IPv4 server.
This behavior makes it critical to enable IPv4 matching to ensure all traffic, regardless of the application’s network stack implementation, is routed through the relay server.
Questions
Can IPv4 addresses or IPv4 CIDR blocks be used with matchDomains?
If not, is there an alternative method to enable IPv4 matching while keeping matchDomains enabled?
Thank you for your assistance.
Topic:
App & System Services
SubTopic:
Networking
Tags:
Extensions
Network Extension
Network
System Configuration
I've implemented a custom system extension VPN for macOS using Packet Tunnel Provider.
The VPN is configured with on-demand, and a rule to always connect whenever there's traffic:
onDemandRules = [NEOnDemandRuleConnect()]
As for the tunnel's settings (at the Packet Tunnel Provider), I've configured a split tunnel, so some routes are excluded from the tunnel.
Now I have the following scenario:
The VPN is connected
The Mac enters sleep
The sleep() function is called (at my Packet Tunnel Provider)
The Mac briefly awakes to check emails/push notifications/etc. This traffic is excluded from the tunnel.
What is the expected behavior here? Should the wake function be called because of the on-demand rule? Or should the VPN remain asleep because this traffic is excluded from the tunnel?
Context: We are using NWConnection for UDP and TCP Connections, and wanted to know the best way to keep the number of pending send completions in control to limit resource usage
Questions:
Is there a way to control the send rate, such that too many 'send pending completion' does not get queued. Say if I do a ‘extremely dense flurry of 10 million NWConnection.send’ will all go asynchronous without any complications? Or I would be informed once it reaches some threshold.
Or no? And is it the responsibility of the application using NWConnection.send to limit the outstanding completion , as if they were beyond a certain limit, it would have an impact on outstanding and subsequent requests?
If so – how would one know ‘what is supposed to be the limit’ at runtime? Is this a process level or system level limit.
Will errors like EAGAIN and ETIMEOUT ever will be reported. In the test I simulated, where the TCP Server was made to not do receive, causing the 'socket send buffer' to become full on the sender side. On the sender side my send stopped getting complete, and became pending. Millions of sends were pending for long duration, hence wanted to know if we will ever get EAGAIN or ETIMEOUT.
Hello, we have noticed a crash in BigSur 11.7.10, 20G1427 libdispatch:
Crashed Thread: 1 Dispatch queue: com.apple.network.connections
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Illegal instruction: 4
Termination Reason: Namespace SIGNAL, Code 0x4
Terminating Process: exc handler [94088]
Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: Release of a suspended object
Thread 0:
0 libsystem_kernel.dylib 0x00007fff20488aea __sigsuspend_nocancel + 10
1 libdispatch.dylib 0x00007fff2031f4e1 _dispatch_sigsuspend + 36
2 libdispatch.dylib 0x00007fff2031f4bd _dispatch_sig_thread + 53
Thread 1 Crashed:: Dispatch queue: com.apple.network.connections
0 libdispatch.dylib 0x00007fff2033cc35 _dispatch_queue_xref_dispose.cold.1 + 24
1 libdispatch.dylib 0x00007fff20313808 _dispatch_queue_xref_dispose + 50
2 libdispatch.dylib 0x00007fff2030e2eb -[OS_dispatch_source _xref_dispose] + 17
3 libnetwork.dylib 0x00007fff24255999 __nw_queue_context_create_source_block_invoke + 41
4 libdispatch.dylib 0x00007fff2030d623 _dispatch_call_block_and_release + 12
5 libdispatch.dylib 0x00007fff2030e806 _dispatch_client_callout + 8
6 libdispatch.dylib 0x00007fff203111b0 _dispatch_continuation_pop + 423
7 libdispatch.dylib 0x00007fff203211f4 _dispatch_source_invoke + 1181
8 libdispatch.dylib 0x00007fff20316318 _dispatch_workloop_invoke + 1784
9 libdispatch.dylib 0x00007fff2031ec0d _dispatch_workloop_worker_thread + 811
10 libsystem_pthread.dylib 0x00007fff204b545d _pthread_wqthread + 314
11 libsystem_pthread.dylib 0x00007fff204b442f start_wqthread + 15
I have seen similar crashes in the forum, but none from com.apple.network.connections queue.
Should we raise a ticket or is this something that was fixed in newer OS versions?
Thanks!
Jakub