Per-Process Network Control on Linux: What the macOS Version Gets for Free
Source: lobsters
Objective Development has been shipping Little Snitch on macOS since 2002. It is one of those tools that, once you use it, you cannot imagine working without: every outbound connection from every process on your machine passes through a prompt, and you decide whether to allow it. The company has now announced a Linux version, which is more technically interesting than a typical platform port, because the underlying kernel infrastructure on macOS and Linux is almost entirely different.
What macOS Gives You for Free
On macOS, the mechanism that makes Little Snitch possible is Appleās Network Extension framework, specifically the NEFilterDataProvider API. An app that registers a content filter gets called synchronously for every new connection, before the connection proceeds. The kernel hands over a NEFilterSocketFlow object that contains the remote address, the local port, and, importantly, the sourceAppSigningIdentifier of the process that initiated the connection. The app returns a verdict: allow, deny, or pause for user input. Process attribution is solved by Apple at the API level.
Before macOS 10.15 Catalina, Little Snitch used a kernel extension (KEXT) that hooked into the BSD socket layer directly. That approach was more powerful but fragile, and Apple eventually deprecated KEXTs in favor of System Extensions, which run in userspace. The trade-off is a sandboxed, stable API in exchange for some flexibility. For a firewall, the Network Extension framework is sufficient and the restriction is not a significant loss.
This matters because the architecture of the macOS version reflects a platform where the OS vendor has explicitly decided that per-application network control is a legitimate and supported use case. Apple built an API for it. Objective Development implemented it. The hard problem of reliable process attribution is not something Little Snitch has to solve itself.
The Linux Gap
Linux has no equivalent API. There is no NEFilterDataProvider. There is no kernel mechanism that says: pause this connection, provide the initiating process identity, wait for a userspace verdict, then proceed. What Linux does have is a collection of lower-level mechanisms, each of which covers part of the problem.
Netfilter NFQUEUE is the most commonly used approach for userspace packet inspection. It allows you to queue packets to a userspace program via the libnetfilter_queue library, and the userspace process issues a verdict (accept or drop). The problem is process attribution. When a packet arrives in an NFQUEUE handler, you know the source address and port, but not which process sent it. You have to correlate that with /proc/net/tcp (or /proc/net/tcp6, /proc/net/udp, and so on), look up the inode for that socket, then trace the inode to a process via /proc/[pid]/fd. This works, but it is inherently racy: by the time your userspace handler does the lookup, the process may have already closed the socket or exited. The race window is small but real, and it creates edge cases in high-frequency connection scenarios.
eBPF offers several hook points that could contribute to a solution. Programs attached via BPF_PROG_TYPE_CGROUP_SOCK and BPF_PROG_TYPE_SOCK_OPS can intercept socket operations at the cgroup level, which maps cleanly to systemd services but not to arbitrary processes in a desktop environment where many processes share a cgroup. LSM BPF hooks, available since kernel 5.7, allow attaching programs to Linux Security Module hooks including socket_connect. eBPF can make process attribution more reliable by recording socket-to-process mappings at the moment of connect() rather than attempting a post-hoc lookup, but it still does not provide the synchronous, hold-the-connection model that a user-facing firewall needs.
SECCOMP_RET_USER_NOTIF is probably the closest Linux equivalent to the macOS NEFilterDataProvider model. Added in Linux 5.0 (released March 2019), it allows a seccomp filter to emit a notification rather than immediately issuing a verdict on a syscall. A supervisor process receives the notification, can read /proc/[pid]/mem, /proc/[pid]/exe, and the full syscall arguments, then issues a response that determines what the kernel does with the original syscall. Applied to connect(), this gives you a paused connection, reliable process identity (no race, because the process is frozen waiting for the syscall to complete), and full information to present to the user. Tools like gVisor and Flatpak portals already use this mechanism for sandboxing and capability delegation.
The limitation is that seccomp filters are attached per-process and inherited across fork(), which means you need a way to apply the filter to every process on the system, including processes you did not start. For a system-wide firewall, you would need to inject the filter early enough in the process lifecycle that it catches everything, which typically means operating at the init or session level, or using a kernel module.
What Existing Linux Tools Do
OpenSnitch, which began as an explicit attempt to bring Little Snitch to Linux, takes the NFQUEUE approach. It runs a Go daemon that intercepts packets via iptables/nftables NFQUEUE rules and a Python Qt GUI for user interaction. The process attribution race is a known issue, and the project has added partial eBPF support to improve reliability by tracking socket creation events with kprobes. OpenSnitch is functional and actively maintained, but the architecture reflects the constraints of what was available when it was written.
Portmaster by Safing takes a similar NFQUEUE base and layers eBPF on top for more reliable socket-to-process mapping. It also adds DNS filtering, a VPN overlay called SPN, and a more polished UI. Portmaster skews toward a privacy suite rather than a pure per-process firewall, which makes it a different product despite the overlapping kernel-level mechanisms.
Both tools do the best they can with available Linux primitives. Neither achieves the clean, synchronous, attribution-guaranteed interception model that macOS provides through NEFilterDataProvider.
What Objective Development Might Do Differently
Objective Development has not published the full technical architecture of their Linux implementation, so the specifics remain unknown. There are a few paths they could take.
A kernel module gives the most direct access and the most reliable interception, but introduces maintenance burden across kernel versions and creates a trust problem: users are understandably wary of loading third-party kernel code. This is the approach older macOS versions used with KEXTs, and Apple moved away from it for good reasons.
A combination of NFQUEUE for packet interception with eBPF kprobes for process attribution at socket creation time would follow the Portmaster model and avoid kernel module territory. This is probably the most practical approach given the available kernel APIs, though it requires supporting modern enough kernels for the eBPF features involved. Kernel 5.7 is a reasonable floor for LSM BPF, and most distributions shipping today use kernels well beyond that.
Seccomp-notify with system-wide coverage is architecturally elegant but difficult to deploy universally without root-level hooks at session startup. It may be more suited to sandboxed application contexts than a system-wide desktop firewall, though combined with a lightweight LD_PRELOAD or PAM module to inject filters at process creation it becomes more viable.
What Objective Development brings that OpenSnitch and Portmaster do not is twenty-plus years of experience designing the user interaction layer. The prompt design, rule management, connection map, and profile system in the macOS version are genuinely refined. A Linux firewall that solves the UX side of the problem as well as the kernel side would be meaningfully different from what currently exists, regardless of which interception mechanism sits underneath.
Why This Matters for Linux Desktop
The Linux desktop has improved substantially in recent years. Wayland adoption has reached the point where it is the default on most major distributions, font rendering and HiDPI support are no longer constant sources of friction, and hardware compatibility has gotten substantially better. Security tooling has lagged behind. The fact that a serious macOS security vendor is making the effort to port their flagship product to Linux signals something real about where the platform stands as a viable daily-driver environment.
Per-process network visibility is useful beyond the security use case. Knowing that a text editor is phoning home, that a build tool is making unexpected network requests, or that a desktop application is contacting ad-tracking infrastructure is the kind of operational awareness that helps you understand what software is actually doing on your machine. Linux has had good low-level networking tools for decades: ss, netstat, Wireshark, tcpdump. What it has lacked is a polished, always-on, user-friendly layer on top of those primitives that ordinary users can interact with meaningfully.
The kernel gap is real and will not be solved by this product release. But a well-implemented Little Snitch for Linux could demonstrate what is achievable within existing constraints, and generate user demand for better kernel APIs to support it. That feedback loop, where applications surface missing platform capabilities and create pressure for upstream improvements, is how a lot of Linux infrastructure has advanced over time. The per-process firewall problem is a good candidate for exactly that kind of pressure.