This is happening Mac M1 Monterey OS .Environment supports both IPv4 and IPV6.
When a http client calls gettaddrinfo() it is returning both IPv6,IPv4 IPs . first v6 IPs and then v4 IPs.
We need to have a way to sort gettaddrinfo() output to get v4 ip first and then v6.
We tried changing DNS order with scutil by putting v4 DNS first , but still getaddrInfo() listing v6 IPs first .
In linux there is a way to control gettaddrinfo() o/p with /etc/gai.conf https://man7.org/linux/man-pages/man5/gai.conf.5.html . In Mac I did not find any option like this , scutil changing order DNS is not effective . can you tell us what is way to do this in MAC OSx ?
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.
Post
Replies
Boosts
Views
Activity
This issue has cropped up many times here on DevForums. Someone recently opened a DTS tech support incident about it, and I used that as an opportunity to post a definitive response here.
If you have questions or comments about this, start a new thread and tag it with Network so that I see it.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
iOS Network Signal Strength
The iOS SDK has no general-purpose API that returns Wi-Fi or cellular signal strength in real time. Given that this has been the case for more than 10 years, it’s safe to assume that it’s not an accidental omission but a deliberate design choice.
For information about the Wi-Fi APIs that are available on iOS, see TN3111 iOS Wi-Fi API overview.
Network performance
Most folks who ask about this are trying to use the signal strength to estimate network performance. This is a technique that I specifically recommend against. That’s because it produces both false positives and false negatives:
The network signal might be weak and yet your app has excellent connectivity. For example, an iOS device on stage at WWDC might have terrible WWAN and Wi-Fi signal but that doesn’t matter because it’s connected to the Ethernet.
The network signal might be strong and yet your app has very poor connectivity. For example, if you’re on a train, Wi-Fi signal might be strong in each carriage but the overall connection to the Internet is poor because it’s provided by a single over-stretched WWAN.
The only good way to determine whether connectivity is good is to run a network request and see how it performs. If you’re issuing a lot of requests, use the performance of those requests to build a running estimate of how well the network is doing. Indeed, Apple practices what we preach here: This is exactly how HTTP Live Streaming works.
Keep in mind that network performance can change from moment to moment. The user’s train might enter or leave a tunnel, the user might walk into a lift, and so on. If you build code to estimate the network performance, make sure it reacts to such changes.
But what about this code I found on the ’net?
Over the years various folks have used various unsupported techniques to get around this limitation. If you find code on the ’net that, say, uses KVC to read undocumented properties, or grovels through system logs, or walks the view hierarchy of the status bar, don’t use it. Such techniques are unsupported and, assuming they haven’t broken yet, are likely to break in the future.
But what about Hotspot Helper?
Hotspot Helper does have an API to read Wi-Fi signal strength, namely, the signalStrength property. However, this is not a general-purpose API. Like the rest of Hotspot Helper, this is tied to the specific use case for which it was designed. This value only updates in real time for networks that your hotspot helper is managing, as indicated by the isChosenHelper property.
But what about MetricKit?
MetricKit is so cool. Amongst other things, it supports the MXCellularConditionMetric payload, which holds a summary of the cellular conditions while your app was running. However, this is not a real-time signal strength value.
But what if I’m working for a carrier?
This post is about APIs in the iOS SDK. If you’re working for a carrier, discuss your requirements with your carrier’s contact at Apple.
I maintain a cross-platform client side network library for persistent TCP connections targeting Win32, Darwin and FreeBSD platforms. I recently upgraded to a Mac Studio w/ M1 Max (Ventura 13.1) from a late 2015 Intel Macbook Pro (Monterey 12.6.2) and I've encountered a discrepancy between the two.
For secure TCP connections my lib uses WolfSSL across all platforms but also supports use of system provided Security libraries. On Darwin platforms this is SecureTransport. Yes I am aware SecureTransport is deprecated in favor of Network. I intend to attempt to integrate with Network later but for now my architecture dictates that I use similar C-style callbacks akin to WolfSSL, OpenSSL, MBedTLS etc.
On the first call to SSLHandshake the SecureTransport write callback generates 151 bytes for my TLS 1.2 connection to example.com:443 on both platforms. However, while on Intel MBP I am able to continue with the full handshake I immediately receive 0 bytes with EOF. In Wireshark on the Intel MBP the 151 bytes are observed as a TLS 1.2 client hello while on M1 it is observed as an SSL continuation message and that is the last message observed.
I regularly see folks struggle to debug their Network Extension providers. For an app, and indeed various app extensions, debugging is as simple as choosing Product > Run in Xcode. That’s not the case with a Network Extension provider, so I thought I’d collect together some hints and tips to help you get started.
If you have any comments or questions, create a new thread here on DevForums and tag it with Network Extension so that I see it.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Debugging a Network Extension Provider
Debugging a Network Extension provider presents some challenges; its not as simple as choosing Product > Run in Xcode. Rather, you have to run the extension first and then choose Debug > Attach to Process. Attaching is simple, it’s the running part that causes all the problems. When you first start out it can be a challenge to get your extension to run at all.
Add a First Light Log Point
The first step is to check whether the system is actually starting your extension. My advice is to add a first light log point, a log point on the first line of code that you control. The exact mechanics of this depend on your development, your deployment target, and your NE provider’s packaging. In all cases, however, I recommend that you log to the system log.
The system log has a bunch of cool features. If you’re curious, see Your Friend the System Log. The key advantage is that your log entries are mixed in with system log entries, which makes it easier to see what else is going on when your extension loads, or fails to load.
IMPORTANT Use a unique subsystem and category for your log entries. This makes it easier to find them in the system log.
For more information about Network Extension packaging options, see TN3134 Network Extension provider deployment.
Logging in Swift
If you’re using Swift, the best logging API depends on your deployment target. On modern systems — macOS 11 and later, iOS 14 and later, and aligned OS releases — it’s best to use the Logger API, which is shiny and new and super Swift friendly. For example:
let log = Logger(subsystem: "com.example.galactic-mega-builds", category: "earth")
let client = "The Mice"
let answer = 42
log.log(level: .debug, "run complete, client: \(client), answer: \(answer, privacy: .private)")
If you support older systems, use the older, more C-like API:
let log = OSLog(subsystem: "com.example.galactic-mega-builds", category: "earth")
let client = "The Mice"
let answer = 42
os_log(.debug, log: log, "run complete, client: %@, answer: %{private}d", client as NSString, answer)
Logging in C
If you prefer a C-based language, life is simpler because you only have one choice:
#import <os/log.h>
os_log_t log = os_log_create("com.example.galactic-mega-builds", "earth");
const char * client = "The Mice";
int answer = 42;
os_log_debug(log, "run complete, client: %s, answer: %{private}d", client, answer);
Add a First Light Log Point to Your App Extension
If your Network Extension provider is packaged as an app extension, the best place for your first light log point is an override of the provider’s initialiser. There are a variety of ways you could structure this but here’s one possibility:
import NetworkExtension
import os.log
class PacketTunnelProvider: NEPacketTunnelProvider {
static let log = Logger(subsystem: "com.example.myvpnapp", category: "packet-tunnel")
override init() {
self.log = Self.log
log.log(level: .debug, "first light")
super.init()
}
let log: Logger
… rest of your code here …
}
This uses a Swift static property to ensure that the log is constructed in a race-free manner, something that’s handy for all sorts of reasons.
It’s possible for your code to run before this initialiser — for example, if you have a C++ static constructor — but that’s something that’s best to avoid.
Add a First Light Log Point to Your System Extension
If your Network Extension provider is packaged as a system extension, add your first light log point to main.swift. Here’s one way you might structure that:
import NetworkExtension
func main() -> Never {
autoreleasepool {
let log = PacketTunnelProvider.log
log.log(level: .debug, "first light")
NEProvider.startSystemExtensionMode()
}
dispatchMain()
}
main()
See how the main function gets the log object from the static property on PacketTunnelProvider. I told you that’d come in handy (-:
Again, it’s possible for your code to run before this but, again, that’s something that’s best to avoid.
App Extension Hints
Both iOS and macOS allow you to package your Network Extension provider as an app extension. On iOS this is super reliable. I’ve never seen any weirdness there.
That’s not true on macOS. macOS lets the user put apps anywhere; they don’t have to be placed in the Applications directory. macOS maintains a database, the Launch Services database, of all the apps it knows about and their capabilities. The app extension infrastructure uses that database to find and load app extensions. It’s not uncommon for this database to get confused, which prevents Network Extension from loading your provider’s app extension. This is particularly common on developer machines, where you are building and rebuilding your app over and over again.
The best way to avoid problems is to have a single copy of your app extension’s container app on the system. So, while you’re developing your app extension, delete any other copies of your app that might be lying around.
If you run into problems you may be able to fix them using:
lsregister, to interrogate and manipulate the Launch Services database
pluginkit, to interrogate and manipulate the app extension state [1]
IMPORTANT Both of these tools are for debugging only; they are not considered API. Also, lsregister is not on the default path; find it at /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister.
For more details about pluginkit, see the pluginkit man page.
When debugging a Network Extension provider, add buttons to make it easy to save and remove your provider’s configuration. For example, if you’re working on a packet tunnel provider you might add:
A Save Config button that calls the saveToPreferences(completionHandler:) method to save the tunnel configuration you want to test with
A Remove Config button that calls the removeFromPreferences(completionHandler:) method to remove your tunnel configuration
These come in handy when you want to start again from scratch. Just click Remove Config and then Save Config and you’ve wiped the slate clean.
You don’t have to leave these buttons in your final product, but it’s good to have them during bring up.
[1] This tool is named after the PluginKit framework, a private framework used to load this type of app extension. It’s distinct from the ExtensionKit framework which is a new, public API for managing extensions.
System Extension Hints
macOS allows you to package your Network Extension provider as a system extension. For this to work the container app must be in the Applications directory [1]. Copying it across each time you rebuild your app is a chore. To avoid that, add a Build post-action script:
Select your app’s scheme and choose Product > Scheme > Edit Scheme.
On the left, select Build.
Click the chevron to disclose all the options.
Select Post-actions.
In the main area, click the add (+) button and select New Run Script Action.
In the “Provide build settings from” popup, select your app target.
In the script field, enter this script:
ditto "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "/Applications/${FULL_PRODUCT_NAME}"
Now, each time you build your app, this script will copy it to the Applications directory. Build your app now, both to confirm that this works and to enable the next step.
The next issue you’ll find is that choosing Product > Run runs the app from the build products directory rather than the Applications directory. To fix that:
Edit your app’s scheme again.
On the left, select Run.
In the main area, select the Info tab.
From the Executable popup, choose Other.
Select the copy of your app in the Applications directory.
Now, when you choose Product > Run, Xcode will run that copy rather than the one in the build products directory. Neat-o!
For your system extension to run your container app must activate it. As with the Save Config and Remote Config buttons described earlier, it’s good to add easy-to-access buttons to activate and deactivate your system extension.
With an app extension the system automatically terminates your extension process when you rebuild it. This is not the case with a system extension; you’ll have to deactivate and then reactivate it each time. Each activation must be approved in System Settings > Privacy & Security. To make that easier, leave System Settings running all the time.
This debug cycle leaves deactivated but not removed system extensions installed on your system. These go away when you restart, so do that from time to time. Once a day is just fine.
macOS includes a tool, systemextensionctl, to interrogate and manipulate system extension state. The workflow described above does not require that you use it, but it’s good to keep in mind. Its man page is largely content free so run the tool with no arguments to get help.
[1] Unless you disable System Integrity Protection, but who wants to do that?
You Can Attach with the Debugger
Once your extension is running, attach with the debugger using one of two commands:
To attach to an app extension, choose Debug > Attach to Process > YourAppExName.
To attach to a system extension, choose Debug > Attach to Process by PID or Name. Make sure to select Debug Process As root. System extensions run as root so the attach will fail if you select Debug Process As Me.
But Should You?
Debugging networking code with a debugger is less than ideal because it’s common for in-progress network requests to time out while you’re stopped in the debugger. Debugging Network Extension providers this way is especially tricky because of the extra steps you have to take to get your provider running. So, while you can attach with the debugger, and that’s a great option in some cases, it’s often better not to do that.
Rather, consider the following approach:
Write the core logic of your provider so that you can unit test each subsystem outside of the provider. This may require some scaffolding but the time you take to set that up will pay off once you encounter your first gnarly problem.
Add good logging to your provider to help debug problems that show up during integration testing.
I recommend that you treat your logging as a feature of your product. Carefully consider where to add log points and at what level to log. Check this logging code into your source code repository and ship it — or at least the bulk of it — as part of your final product. This logging will be super helpful when it comes to debugging problems that only show up in the field.
Remember that, when using the system log, log points that are present but don’t actually log anything are very cheap. In most cases it’s fine to leave these in your final product.
Now go back and read Your Friend the System Log because it’s full of useful hints and tips on how to use the system log to debug the really hard problems.
General Hints and Tips
Install the Network Diagnostics and VPN (Network Extension) profiles [1] on your test device. These enable more logging and, most critically, the recording of private data. For more info about that last point, see… you guessed it… Your Friend the System Log.
Get these profiles from our Bug Reporting > Profiles and Logs page.
When you’re bringing up a Network Extension provider, do your initial testing with a tiny test app. I regularly see folks start out by running Safari and that’s less than ideal. Safari is a huge app with lots of complexity, so if things go wrong it’s hard to tell where to look.
I usually create a small test app to use during bring up. The exact function of this test app varies by provider type. For example:
If I’m building a packet tunnel provider, I might have a test function that makes an outgoing TCP connection to an IP address. Once I get that working I add another function that makes an outgoing TCP connection to a DNS name. Then I start testing UDP. And so on.
Similarly for a content filter, but then it makes sense to add a test that runs a request using URLSession and another one to bring up a WKWebView.
If I’m building a DNS proxy provider, my test app might use CFHost to run a simple name-to-address query.
Also, consider doing your bring up on the Mac even if your final target is iOS. macOS has a bunch of handy tools for debugging networking issues, including:
dig for DNS queries
nc for TCP and UDP connections
netstat to display the state of the networking stack
tcpdump for recording a packet trace [2]
Read their respective man pages for all the details.
[1] The latter is not a profile on macOS, but just a set of instructions.
[2] You can use an RVI packet trace on iOS but it’s an extra setup step.
Revision History
2023-12-15 Fixed a particularly egregious typo (and spelling error in a section title, no less!).
2023-04-02 Fixed one of the steps in Sytem Extension Hints.
I'd like to learn how a Matter "Binding" can be created on iOS, e.g. via the "Matter" Framework.
I'm aware of the Matter Framework "Documentation" here.
But since it's missing any real description or documentation it's not helpful to me atm.
Any examples/ tutorials/ explanations are greatly appreciated.
Hello, I am getting crashes on iOS 16 devices only regarding CFNetwork. Below is the full crash report. I am not able to reproduce it on my end. I've attached the .txt crash log below and also posted it below.
CFNetworkCrashLog.txt
Any help is appreciated, thank you!
Crashed: com.apple.NSXPCConnection.m-user.com.apple.nsurlsessiond
EXC_BREAKPOINT 0x00000001cfbbecec
7 Foundation 0x42054 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212
8 Foundation 0x41f3c -[NSRunLoop(NSRunLoop) runUntilDate:] + 64
9 UIKitCore 0x4d66a4 -[UIEventFetcher threadMain] + 436
10 Foundation 0x5b518 __NSThread__start__ + 716
11 libsystem_pthread.dylib 0x16cc _pthread_start + 148
12 libsystem_pthread.dylib 0xba4 thread_start + 8
com.google.firebase.crashlytics.MachExceptionServer
0 App 0x387cfc FIRCLSProcessRecordAllThreads + 393 (FIRCLSProcess.c:393)
1 App 0x3880dc FIRCLSProcessRecordAllThreads + 424 (FIRCLSProcess.c:424)
2 App 0x395be0 FIRCLSHandler + 34 (FIRCLSHandler.m:34)
3 App 0x396400 FIRCLSMachExceptionServer + 521 (FIRCLSMachException.c:521)
4 libsystem_pthread.dylib 0x16cc _pthread_start + 148
5 libsystem_pthread.dylib 0xba4 thread_start + 8
GAIThread
0 libsystem_kernel.dylib 0xda8 mach_msg2_trap + 8
1 libsystem_kernel.dylib 0x13a1c mach_msg2_internal + 80
2 libsystem_kernel.dylib 0x13c5c mach_msg_overwrite + 388
3 libsystem_kernel.dylib 0x12ec mach_msg + 24
4 CoreFoundation 0x7aac4 __CFRunLoopServiceMachPort + 160
5 CoreFoundation 0x7bd08 __CFRunLoopRun + 1232
6 CoreFoundation 0x80eb0 CFRunLoopRunSpecific + 612
7 Foundation 0x42054 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212
8 Foundation 0x41ee8 -[NSRunLoop(NSRunLoop) run] + 64
9 App 0x563d00 +[GAI threadMain:] + 64
10 Foundation 0x5b518 __NSThread__start__ + 716
11 libsystem_pthread.dylib 0x16cc _pthread_start + 148
12 libsystem_pthread.dylib 0xba4 thread_start + 8
com.apple.NSURLConnectionLoader
0 libsystem_kernel.dylib 0xda8 mach_msg2_trap + 8
1 libsystem_kernel.dylib 0x13a1c mach_msg2_internal + 80
2 libsystem_kernel.dylib 0x13c5c mach_msg_overwrite + 388
3 libsystem_kernel.dylib 0x12ec mach_msg + 24
4 CoreFoundation 0x7aac4 __CFRunLoopServiceMachPort + 160
5 CoreFoundation 0x7bd08 __CFRunLoopRun + 1232
6 CoreFoundation 0x80eb0 CFRunLoopRunSpecific + 612
7 CFNetwork 0x257ff0 _CFURLStorageSessionDisableCache + 61088
8 Foundation 0x5b518 __NSThread__start__ + 716
9 libsystem_pthread.dylib 0x16cc _pthread_start + 148
10 libsystem_pthread.dylib 0xba4 thread_start + 8
CommandHandler
0 libsystem_kernel.dylib 0xda8 mach_msg2_trap + 8
1 libsystem_kernel.dylib 0x13a1c mach_msg2_internal + 80
2 libsystem_kernel.dylib 0x13c5c mach_msg_overwrite + 388
3 libsystem_kernel.dylib 0x12ec mach_msg + 24
4 CaptiveNetwork 0x9d78 ConnectionGetCommandInfo + 160
5 CaptiveNetwork 0x7c54 __add_signal_port_source_block_invoke_2 + 244
6 libdispatch.dylib 0x3f88 _dispatch_client_callout + 20
7 libdispatch.dylib 0x7418 _dispatch_continuation_pop + 504
8 libdispatch.dylib 0x1aa58 _dispatch_source_invoke + 1588
9 libdispatch.dylib 0xb518 _dispatch_lane_serial_drain + 376
10 libdispatch.dylib 0xc18c _dispatch_lane_invoke + 384
11 libdispatch.dylib 0x16e10 _dispatch_workloop_worker_thread + 652
12 libsystem_pthread.dylib 0xdf8 _pthread_wqthread + 288
13 libsystem_pthread.dylib 0xb98 start_wqthread + 8
Thread
0 libsystem_kernel.dylib 0x12b0 __workq_kernreturn + 8
1 libsystem_pthread.dylib 0xe44 _pthread_wqthread + 364
2 libsystem_pthread.dylib 0xb98 start_wqthread + 8
Thread
0 libsystem_pthread.dylib 0xb90 start_wqthread + 254
Crashed: com.apple.NSXPCConnection.m-user.com.apple.nsurlsessiond
0 libobjc.A.dylib 0x6cec objc_opt_respondsToSelector + 48
1 libsystem_trace.dylib 0x6480 _os_log_fmt_flatten_object + 248
2 libsystem_trace.dylib 0x41a0 _os_log_impl_flatten_and_send + 1864
3 libsystem_trace.dylib 0x21bc _os_log + 152
4 libsystem_trace.dylib 0x7840 _os_log_impl + 24
5 CFNetwork 0x10dc08 _CFURLConnectionCopyTimingData + 34880
6 Foundation 0x64b620 message_handler_error + 360
7 libxpc.dylib 0x1179c _xpc_connection_call_event_handler + 152
8 libxpc.dylib 0x11be8 _xpc_connection_mach_event + 1020
9 libdispatch.dylib 0x4048 _dispatch_client_callout4 + 20
10 libdispatch.dylib 0x24104 _dispatch_mach_cancel_invoke + 128
11 libdispatch.dylib 0x21720 _dispatch_mach_invoke + 916
12 libdispatch.dylib 0xb518 _dispatch_lane_serial_drain + 376
13 libdispatch.dylib 0xc1c0 _dispatch_lane_invoke + 436
14 libdispatch.dylib 0x16e10 _dispatch_workloop_worker_thread + 652
15 libsystem_pthread.dylib 0xdf8 _pthread_wqthread + 288
16 libsystem_pthread.dylib 0xb98 start_wqthread + 8
We have been observing an issue where when binding a UDP socket to an ephemeral port (i.e. port 0), the OS ends up allocating a port which is already bound and in-use. We have been seeing this issue across all macos versions we have access to (10.x through recent released 13.x).
Specifically, we (or some other process) create a udp4 socket bound to wildcard and ephemeral port. Then our program attempts a bind on a udp46 socket with ephemeral port. The OS binds this socket to an already in use port, for example you can see this netstat output when that happens:
netstat -anv -p udp | grep 51630
udp46 0 0 *.51630 *.* 786896 9216 89318 0 00000 00000000 00000000001546eb 00000000 00000800 1 0 000001
udp4 0 0 *.51630 *.* 786896 9216 89318 0 00000 00000000 0000000000153d9d 00000000 00000800 1 0 000001
51630 is the (OS allocated) port here, which as you can see has been allocated to 2 sockets. The process id in this case is the same (because we ran an explicit reproducer to reproduce this), but it isn't always the case.
We have a reproducer which consistenly shows this behaviour. Before filing a feedback assistant issue, I wanted to check if this indeed appears to be an issue or if we are missing something here, since this appears to be a very basic thing.
In code:
thread Queue: shared_tcpConnWorkQueue
libsp.dylib`spd_checkin_socket.cold.1:
0x242276464 <+0>: adrp x8, 137736
0x242276468 <+4>: adrp x9, 0
0x24227646c <+8>: add x9, x9, #0xa3f ; "Linked against modern SDK, VOIP socket will not wake. Use Local Push Connectivity instead"
0x242276470 <+12>: str x9, [x8, #0x390]
0x242276474 <+16>: brk #0x1 ->
EXC_BREAKPOINT (code=1, subcode=0x242276474)
DeviceLogs:
Application Specific Information:
Linked against modern SDK, VOIP socket will not wake. Use Local Push Connectivity instead
Thread 3 name: Dispatch queue: shared_tcpConnWorkQueue
Thread 3 Crashed:
0 libsp.dylib 0x216566474 spd_checkin_socket.cold.1 + 16
1 libsp.dylib 0x2165654c0 spd_checkin_socket + 896
2 CFNetwork 0x1b4230ef0 0x1b40f2000 + 1306352
3 CFNetwork 0x1b4232bcc 0x1b40f2000 + 1313740
4 CFNetwork 0x1b42351e0 0x1b40f2000 + 1323488
5 CFNetwork 0x1b42343a0 0x1b40f2000 + 1319840
6 libdispatch.dylib 0x1b9e61850 _dispatch_call_block_and_release + 24
7 libdispatch.dylib 0x1b9e627c8 _dispatch_client_callout + 16
8 libdispatch.dylib 0x1b9e3d854 _dispatch_lane_serial_drain$VARIANT$armv81 + 604
9 libdispatch.dylib 0x1b9e3e2e4 _dispatch_lane_invoke$VARIANT$armv81 + 380
10 libdispatch.dylib 0x1b9e48000 _dispatch_workloop_worker_thread + 612
11 libsystem_pthread.dylib 0x1fa8e2b50 _pthread_wqthread + 284
12 libsystem_pthread.dylib 0x1fa8e267c start_wqthread + 8
I think it is due to voip socket issue. But not getting the point exactly. As am new to ios Development so do not have much idea. Looking forward for guidance.
Thanks in advance!
In our PacketTunnelProvider we are seeing behavior for enforceRoutes which appears to contradict the documentation.
According to the developer documentation (my emphasis):
If this property is YES when the includeAllNetworks property is NO, the system scopes the included routes to the VPN and the excluded routes to the current primary network interface.
If we set these IPv4 settings:
IPv4Settings = {
configMethod = manual
addresses = (
172.16.1.1,
)
subnetMasks = (
255.255.255.255,
)
includedRoutes = (
{
destinationAddress = 0.0.0.0
destinationSubnetMask = 0.0.0.0
},
)
excludedRoutes = (
{
destinationAddress = 10.10.0.0
destinationSubnetMask = 255.255.255.0
},
)
overridePrimary = YES
}
Then if enforceRoutes is set to YES, then we do not see traffic for the excluded network, which is the expected behavior. If enforceRoutes is set to NO, then we do see traffic for the excluded network.
In both cases includeAllNetworks and excludeLocalNetworks are both NO.
The excluded network is not one of the local LANs.
Is this a known issue? Is there some documented interaction that I missed here?
Is there a workaround we can use to make this function as intended, with enforceRoutes set to YES?
I want to connect to Wi-Fi programmatically using swift in my iPad application and I want to create according to bellow flow.
Enter the network name programmatically
Set the programmatically "WAP2-Enterprise" for security.
Set username/password.
A certificate popup will appear, so tap "Trust".
"Turn on the following information." otherwise off.
Automatic connection
Restrict IP address tracking
Set the programmatically IPV4 address below.
Configure ID: Manual
IP address: 192.***.***.***
For tablets, ○○: 1 to 20
Subnet mask: 255.255.255.0
Router: 192.***.***.***
Configure DNS server(Set the programmatically)
Manual
Add server: 8.8.8.8
HTTP proxy(Set the programmatically)
Configure proxy: off
if anyone you can guide me to proper way much a appreciated!!!
Most apps perform ordinary network operations, like fetching an HTTP resource with URLSession and opening a TCP connection to a mail server with Network framework. These operations are not without their challenges, but they’re the well-trodden path.
Note If your app performs ordinary networking, see TN3151 Choosing the right networking API for recommendations as to where to start.
Some apps have extra-ordinary networking requirements. For example, apps that:
Help the user configure a Wi-Fi accessory
Require a connection to run over a specific interface
Listen for incoming connections
Building such an app is tricky because:
Networking is hard in general.
Apple devices support very dynamic networking, and your app has to work well in whatever environment it’s running in.
Documentation for the APIs you need is tucked away in man pages and doc comments.
In many cases you have to assemble these APIs in creative ways.
If you’re developing an app with extra-ordinary networking requirements, this post is for you.
Note If you have questions or comments about any of the topics discussed here, put them in a new thread here on DevForums. Make sure I see it by tagging it with… well… tags appropriate to the specific technology you’re using, like Foundation, CFNetwork, Network, or Network Extension.
Links, Links, and More Links
Each topic is covered in a separate post:
The iOS Wi-Fi Lifecycle describes how iOS joins and leaves Wi-Fi networks. Understanding this is especially important if you’re building an app that works with a Wi-Fi accessory.
Network Interface Concepts explains how Apple platforms manage network interfaces. If you’ve got this far, you definitely want to read this.
Network Interface Techniques offers a high-level overview of some of the more common techniques you need when working with network interfaces.
Network Interface APIs describes APIs and core techniques for working with network interfaces. It’s referenced by many other posts.
Running an HTTP Request over WWAN explains why most apps should not force an HTTP request to run over WWAN, what they should do instead, and what to do if you really need that behaviour.
If you’re building an iOS app with an embedded network server, see Showing Connection Information in an iOS Server for details on how to get the information to show to your user so they can connect to your server.
Many folks run into trouble when they try to find the device’s IP address, or other seemingly simple things, like the name of the Wi-Fi interface. Don’t Try to Get the Device’s IP Address explains why these problems are hard, and offers alternative approaches that function correctly in all network environments.
If you’re building an app that works with a Wi-Fi accessory, see Working with a Wi-Fi Accessory.
If you’re trying to gather network interface statistics, see Network Interface Statistics.
There are also some posts that are not part of this series but likely to be of interest if you’re working in this space:
Local Network Privacy FAQ discusses iOS’s local network privacy feature.
Calling BSD Sockets from Swift does what it says on the tin, that is, explain how to call BSD Sockets from Swift. When doing weird things with the network, you often find yourself having to use BSD Sockets, and that API is not easy to call from Swift. The code therein is primarily for the benefit of test projects, oh, and DevForums posts like this one.
TN3111 iOS Wi-Fi API overview is a critical resource if you’re doing Wi-Fi specific stuff on iOS.
TLS For Accessory Developers tackles the tricky topic of how to communicate securely with a network-based accessory.
Networking Resources has links to many other useful resources.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Revision History
2024-04-30 Added a link to Network Interface Statistics.
2023-09-14 Added a link to TLS For Accessory Developers.
2023-07-23 First posted.
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.
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
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.
Hi Team,
I'm trying to capture inbound traffic for DNS responses and have experimented with the following rules, but they did not work.
NENetworkRule *dnsInboundTraffic = [[NENetworkRule alloc] initWithRemoteNetwork:nil remotePrefix:0 localNetwork:[NWHostEndpoint endpointWithHostname:@"0.0.0.0" port:@"53"] localPrefix:0 protocol:NENetworkRuleProtocolUDP direction:NETrafficDirectionInbound];
settings.includedNetworkRules = @[dnsInboundTraffic];
Could you please correct me if I'm making any mistakes while setting the rules?
How to commission a device to our fabric after adding it using Matter.Support
We have the following configuration:
eero border router
our own thermostat device that we are developing
no home pod or home TV are used
We have followed this guide
https://developer.apple.com/documentation/mattersupport/adding-matter-support-to-your-ecosystem
and implemented Matter Extension, as Matter.Framework is no more allowed to pair device.
In Matter Extension the following callbacks are fired:
override func selectThreadNetwork(from threadScanResults: [MatterAddDeviceExtensionRequestHandler.ThreadScanResult]) async throws -> MatterAddDeviceExtensionRequestHandler.ThreadNetworkAssociation
override func commissionDevice(in home: MatterAddDeviceRequest.Home?, onboardingPayload: String, commissioningID: UUID) async throws
In our demo app the following function completes without errors
try await request.perform()
The thermostat device seems to be successfully commissioned.
In https://developer.apple.com/documentation/mattersupport/adding-matter-support-to-your-ecosystem there is a comment that says:
Use Matter.Framework APIs to pair the accessory to your application with the provided onboardingPayload.
How should this thing be done ?
We tried to start the matter controller in the old way:
func start() -> Bool
{
if (self.matterController == nil)
{
let storage = MatterStorage.shared
let factory = MTRDeviceControllerFactory.sharedInstance()
let factoryParams = MTRDeviceControllerFactoryParams(storage: storage)
do
{
try factory.start(factoryParams)
}
catch
{
return false
}
let keys = FabricKeys()
let params = MTRDeviceControllerStartupParams(signing: keys, fabricId: UInt64(fabricId), ipk: keys.ipk)
params.vendorID = NSNumber(value: self.vendorId)
self.matterController = try? factory.createController(onExistingFabric: params)
let controllerNid = self.matterController?.controllerNodeID
if (self.matterController == nil)
{
self.matterController = try? factory.createController(onNewFabric: params)
}
}
return (self.matterController != nil)
}
Certificate are generated locally as in Matter Darwin example.
We tried to call
let params = MTRCommissioningParameters()
matterController?.commissionNode(withID: NSNumber(value: self.deviceNid), commissioningParams: params)
in commissionDevice of Matter Extension but we get error:
Invalid object state.
I would like to use NWProtocolQUIC in Swift's Network.framework to prepare multiple QUIC Streams and send different data to the server for each.
class QuicConnection {
var acceptConnection: NWConnection?
var openConnection: NWConnection?
var acceptConnectionState: NWConnection.State?
var openConnectionState: NWConnection.State?
var receiveFromAcceptConnection: String?
static func makeNWParameters() -> NWParameters {
let options = NWProtocolQUIC.Options(alpn: ["echo"])
options.direction = .bidirectional
let securityProtocolOptions: sec_protocol_options_t = options.securityProtocolOptions
sec_protocol_options_set_verify_block(securityProtocolOptions,
{ (_: sec_protocol_metadata_t,
_: sec_trust_t,
complete: @escaping sec_protocol_verify_complete_t) in
complete(true)
}, DispatchQueue.main)
return NWParameters(quic: options)
}
let group: NWConnectionGroup
init() {
print("init")
let parameters = Self.makeNWParameters()
let descriptor = NWMultiplexGroup(to: .hostPort(host: "192.168.0.20", port: 4242))
group = NWConnectionGroup(with: descriptor, using: parameters)
//subscribe()
group.stateUpdateHandler = { (state: NWConnectionGroup.State) in
print("state: \(state)")
switch state {
case .ready:
print("quic connected!")
default:
break
}
}
group.newConnectionHandler = { [weak self] (connection: NWConnection) in
print("new connection: \(connection)")
self?.acceptConnection = connection
self?.acceptConnection?.stateUpdateHandler = { [weak self] (state: NWConnection.State) in
self?.acceptConnectionState = state
}
self?.subscribeAcceptConnection()
self?.acceptConnection?.start(queue: DispatchQueue.main)
}
group.start(queue: DispatchQueue.main)
}
func createStream() {
//guard let group else { return }
let options = NWProtocolQUIC.Options()
options.direction = .bidirectional
let securityProtocolOptions: sec_protocol_options_t = options.securityProtocolOptions
sec_protocol_options_set_verify_block(securityProtocolOptions,
{ (_: sec_protocol_metadata_t,
_: sec_trust_t,
complete: @escaping sec_protocol_verify_complete_t) in
complete(true) // Insecure !!!
}, DispatchQueue.main)
openConnection = NWConnection(from: group)
openConnectionState = openConnection?.state
openConnection?.stateUpdateHandler = { [weak self] (state: NWConnection.State) in
self?.openConnectionState = state
print("state: \(state)")
switch state {
case .ready:
print("stream connected!")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self?.send(message: "marker1")
}
default:
break
}
}
openConnection?.start(queue: DispatchQueue.main)
}
func send(message: String) {
print("send start")
let completion: NWConnection.SendCompletion = .contentProcessed { (error: Error?) in
if let error = error {
print("send error: \(error)")
} else {
print("send successful")
}
}
openConnection?.send(content: message.data(using: .utf8)!,
contentContext: .defaultMessage,
isComplete: true,
completion: completion)
print("message: \(message)")
}
}
When the app starts, it calls the init function of this QuicConnection class to create an instance and build the QUIC tunnel." quic connected" log appears, and when a specific action is performed in the app, the createStream function is called to put up a stream." stream connected" log is displayed, but when I then try to send data using the send function, the "send successful" message is displayed, but there is no output on the server side indicating that the message was received from the client. However, when I try to send data using the send function after that, I get a "send successful" message.
I don't think there is a problem on the server side, because I can communicate well when only NWConnection is used without NQConnectionGroup. The server is using quic-go.
I would like to borrow knowledge from those who have handled QUIC streams in Swift.
Below are Apple's announcement and official documents that I referred to. I wrote the code referring to these, but even though I can connect the QUIC tunnels, I can not send data by setting up individual streams.
https://developer.apple.com/videos/play/wwdc2021/10094/
https://developer.apple.com/documentation/network/nwprotocolquic
Note The PF side of this is now covered by TN3165 Packet Filter is not API.
Network Extension (NE) providers let you create products for VPN, content filtering, transparent proxying, and so on. Various Apple platforms support various different provider types. See TN3134 Network Extension provider deployment for the details.
On iOS NE providers are the only game in town. It’s not possible to implement products like this in any other way. On macOS, however, there are a variety of other ad hoc techniques you might use. These include:
Packet Filter (PF) aka pfctl (see its man page)
A utun interface (see <net/if_utun.h>)
Network kernel extensions (NKE), aka KEXTs
People use these techniques for a variety of reasons. For example, you might have a product that predates the NE provider architecture, or you might want to reuse code that you wrote for another platform.
Regardless of the reason, be aware that DTS doesn’t support these ad hoc techniques. If you’re building a product like this for macOS, create an NE provider.
We’ve adopted this policy because, in our experience, these ad hoc techniques tend to be very brittle, and thus are not supportable in the long term. A great example of this is PF. There’s no documented arbitration scheme for PF rules so, as a third-party developer, the rules you install might be incompatible with the rules set up by various macOS features, other third-party developers, the user, or the site admin.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Revision History
2028-02-09 Added a link to TN3165.
2023-11-23 First posted.
We use the OSS release of mDNSresponder fairly extensively and have patched it to fix some issues.
I was curious if Apple accepted such contributions upstream, or provided a way to communicate the changes for potential inclusion in future patches, as maintaining a fork isn't ideal for both parties.
It seems that PRs on the GH repo are not always reviewed, but there is some evidence that they are looked at. We realise that POSIX implementation for mDNSResponder might not be a priority for Apple, but curious if there's a process for contributions from industry?
Hello everyone,
I'm currently developing a firewall using a network extension that employs two methods: NEFilterDataProvider, which specifically filters UDP and TCP connections, and NEFilterPacketProvider, designed to filter all packets. However, I've noticed that utilizing NEFilterPacketProvider results in a 50% reduction in bandwidth compared to when it's not in use.
Within my packetHandler closure, I'm only returning 'allow.' I suspect this slowdown might be due to the packet data cache being passed to my extension, with only one active thread available to handle it.
I'm wondering if there's a way to adjust the size of the packet buffer, increase the number of threads dedicated to processing packets, or configure specific rules to mitigate this issue.
Thank you.
Hi there, I have watched the keynote of WWDC23 and like to have technical information on how to implement Filling forms for iPad OS using PDFKIT into our App.
I have reviewed PDFKit API documentation, but there are no any documentation about how to use that technology and no any changes to API.
Thanks,
Eduard
We have a PakcetTunnelProvider in SystemExtension with split tunnelling. We are setting up a private range of IP address (240.0.0.1/10) as include routes and a few match domains using NEPacketTunnelNetworkSettings. Everything works fine. We are able to setup tunnel settings and receive DNS as well as data traffic as per our rules.
However, when we execute the netstat -rn -f inet command in Terminal, it shows the following output:
240.0.0/10 link#8 UCS utun0
240.0.0.1 10.211.55.1 UGHS en0
240.0.0.2 10.211.55.1 UGHS en0
240.0.0.3 link#8 UHWIi utun0
After stopping the tunnel, some stale entries remain in the route table, as evidenced by the output of netstat -rn -f inet:
240.0.0/10 link#8 UCS utun0
240.0.0.3 link#8 UHWIi utun0
The expected behavior is that included routes should automatically clear once the tunnel stops.
** It's noteworthy that we've only observed this behaviour on Monterey OS; **
it works as expected on Ventura and Sonoma (where routes are automatically removed upon tunnel cessation)
We have tried to set the tunnel settings to nil explicitly, but no luck.
setTunnelNetworkSettings(nil) { _ in}
We're unsure why the routes aren't clearing properly on Monterey OS.
Thanks -
Happy questioning