Hi,
I have been reviewing some previous discussions around networking in macOS, and I’d like to clarify my understanding of the differences between the kernel-space network stack and user-space network stack and validate this understanding based on the information shared in earlier threads.
I’m also curious about how these differences impact macOS applications, particularly those requiring maintaining many simultaneous network connections.
Understanding Kernel-Space vs User-Space Network Stack
Kernel-Space Network Stack (BSD Sockets): The kernel-space networking stack refers to the traditional networking layer that runs in the kernel and handles network communication via BSD sockets. This stack is lower-level and interacts directly with the operating system's networking drivers and hardware. All network connections managed through this stack require a socket (essentially a file descriptor) to be opened, which places limits on the number of file descriptors that can be used (for example, the default 64K limit for sockets). The kernel network stack is traditionally used on macOS (and other UNIX-based systems) for networking tasks, such as when you use system APIs like BSD sockets.
User-Space Network Stack (Network Framework): The user-space network stack in macOS (via the Network framework) allows applications to handle networking tasks without directly using the kernel. This provides more flexibility and performance benefits for certain types of network operations, as the networking stack is managed in user space rather than kernel space. This approach reduces overhead and allows more control over networking configurations. In theory, with user-space networking, the application wouldn't be bound by kernel-level socket limits, and it could handle many more simultaneous connections efficiently.
In previous posts on that thread, Quinn mentioned that the Network framework in macOS can rely on the user-space stack (by default) for network operations, but there are still cases where macOS falls back to using the kernel stack (i.e., BSD sockets) under certain conditions. One key example is when the built-in firewall is enabled. This prevents user-space networking from functioning as expected, and the system defaults to using the kernel's BSD sockets for network communication.
In the same discussion, it was also highlighted that NECP (Network Extension Control Plane) could place further limitations on user-space networking, and eventually, systems may run into issues like ENOSPC errors due to excessive simultaneous network flows. This suggests that while user-space networking can offer more flexibility, it's not immune to limits imposed by other system resources or configurations.
Given the information above, I wanted to confirm:
-
Is the above understanding correct and does the macOS Network framework still use the user-space networking stack in macOS 14 and beyond?
-
Under what conditions would the system fall back to using the kernel stack (BSD sockets) instead of the user-space stack? For example, does enabling the firewall still disable user-space networking?
-
What is the practical impact of this fallback on applications that require many simultaneous network connections? Specifically, are there any limitations like the 64K socket limit that developers should be aware of when the system uses the user space stack, and what are the best practices to manage large numbers of connections?
Let’s start with some specific points:
I believe that changed in macOS 15.
That’s not quite right. That NECP limit applies to the process as a whole, regardless of which networking stack ends up in use.
Speaking of the NECP flow limit, I last looked into that in the macOS 14 timeframe (for example, this thread). I believe that we changed the specific error from ENOSPC
to ENOMEM
in macOS 15 (r. 126628989). I’m not aware of any other recent changes.
Coming back to your questions:
Essentially, yes.
That is an implementation detail that can and does change over time. For example, right now I believe that loopback connections still go via the kernel, but it’s not hard to imagine that changing.
It’s hard to answer such a general question. However, I suspect that the most pressing limit right now is the NECP one.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"