# nsntrace: tracing one process's network traffic by putting it in its own namespace

> nsntrace is a small C program that runs the process you name inside a fresh Linux network namespace, bridges that namespace back to your real network with a virtual interface and NAT, and writes the result as a pcap file. It suits a developer who wants to know exactly what one command sends, and it needs root and an isolated network stack to work.

**nsntrace/nsntrace** — Perform network trace of a single process by using network namespaces.

- Repository: https://github.com/nsntrace/nsntrace
- Stars: 548 · Forks: 28
- Language: C
- License: GPL-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/nsntrace-nsntrace

## The problem it solves, and the trick it uses

nsntrace performs a network trace of a single process by using network namespaces, and the trace is saved as a pcap file that can later be analysed with, for instance, Wireshark or tshark.

The problem it solves is attribution. A host-wide capture tells you what went over the wire but not which program did it, and correlating packets with processes is either a matter of per-process tooling that does not exist for everything, or a matter of capturing on many interfaces and guessing from the ports. nsntrace takes a different route: it makes the answer structural.

The mechanism is the clone syscall with CLONE_NEWNET, which creates a new network namespace, and from that namespace the tool launches the requested process and starts a trace using libpcap. Everything the traced process sends has to leave through the namespace, so the capture contains that process's traffic and nothing else. The documentation says it in one line: this will ensure that all the packets we trace come from the process.

The project is explicitly inspired by a reply on an Ubuntu forum, and says it uses the same approach confined to a single C program. That is worth noting because it says where the design came from: a shell recipe that did the same thing with several tools, reduced to one binary.

The rest of the design is the price of that isolation, and the documentation walks through it honestly.

The problem left over is that the process is isolated in the namespace and cannot reach any other network. The way around it is to create virtual network interfaces, keep one of them in the root network namespace and the other in the newly created one where the tracing takes place, and set the root namespaced one as the default gateway of the trace namespaced virtual device. Then iptables and NAT forward all traffic from the virtual device to the default network interface.

So the traced process believes it is on a network, sends its packets into a veth pair, they are captured there, and they are forwarded out through the real interface.

## The two limitations, and the DNS problem that has its own flag

The documentation states three consequences of this design, and each one is a decision a user needs to know about before interpreting a capture.

The first is about addressing. This will allow us to capture the packets from a single process while it is communicating with our default network, but a limitation is that our ip address will be the NAT one of the virtual device. So the source and destination addresses in your pcap are not the addresses your machine really has, and a capture taken on a laptop will show an address in the private range the tool assigned. The example in the documentation makes this concrete, reporting your IP address in this trace as 172.16.42.255. Any analysis that depends on the real address, such as an access control decision on the server side, will not match what you see.

The second is privileges. Another limitation is that since we are using iptables and since we are tracing raw sockets, this application needs to be run as root. There is no unprivileged mode, and no fallback to a less capable capture.

The third is the resolver, and it is the one that will bite you first if you are on a modern distribution.

On many systems today the nameserver functionality is handled by an application such as systemd-resolved or dnsmasq, and the nameserver address is a loopback address, like 127.0.0.53, where that application listens for incoming DNS queries. That will not work inside the new network namespace, because the namespace has its own loopback device and the resolver on the host is not reachable there.

The workaround is the --use-public-dns option, which overrides resolv.conf in the namespace, and nsntrace then uses nameservers from Quad9 at 9.9.9.9, Cloudflare at 1.1.1.1, Google at 8.8.8.8 and OpenDNS at 208.67.222.222 to perform the DNS queries.

That workaround is easy to apply and it is also a decision with consequences, because your queries now go to a public resolver rather than to whatever your organisation runs, and if your network requires an internal resolver for split-horizon names the trace will fail to resolve them.

## The command line, and the two ways to use the output

The usage is short, and every option maps to one of the design decisions above.

```
usage: nsntrace [options] program [arguments]
Perform network trace of a single process by using network namespaces.

Options:
-o file          	send trace output to file (default nsntrace.pcap)
-d device        	the network device to trace
-f filter        	an optional capture filter
-u username      	run program as username/uid
--use-public-dns	override resolv.conf to use public nameservers from
                    	Quad9, Cloudflare, Google and OpenDNS
```

The -o option names the output file, which defaults to nsntrace.pcap. The -d option selects the network device to trace, which is the device whose traffic you want to see rather than the virtual one. The -f option takes a capture filter, which is the libpcap filter syntax and is the one most people already know. The -u option runs the program as a different user or uid, which is how you trace a program that drops privileges or that you do not want to run as root, given that the tool itself needs root.

The documented example traces a single wget:

```bash
$ sudo nsntrace -d eth1 wget www.google.com
Starting network trace of 'wget' on interface eth1.
Your IP address in this trace is 172.16.42.255.
Use ctrl-c to end at any time.
```

and then the capture is analysed with tshark the way any pcap file would be:

```bash
$ tshark -r nsntrace.pcap -Y 'http.response or http.request'
```

The second documented mode is a live capture piped straight into tshark, which is the way to use the tool as a network diagnostic rather than as an evidence collector:

```bash
$ sudo nsntrace -f tcp -o - wget www.google.com  2> /dev/null | tshark -r -
```

The -o - combination sends the capture to standard output, and the 2> /dev/null discards the tool's own status output so it does not corrupt the stream that tshark is reading. The result is a live tshark view of one program's traffic, with no intermediate file.

## Building it, and what it needs

The build is a standard GNU autotools project, and the steps are the familiar four.

```bash
$ ./autogen.sh
$ ./configure
$ make
```

and to install:

```bash
$ sudo make install
```

The repository layout matches that: an autogen.sh, a configure.ac, a Makefile.am, a src/ directory for the code, a man/ directory for the manual page, a tests/ directory and a snap/ directory for packaging.

The dependency list is given in Debian and Ubuntu naming, and it is the clearest statement of what the program touches:

```
automake
docbook-xml
docbook-xsl
iptables
libnl-route-3-dev
lippcap-dev
pkg-config
xsltproc
```

Those names tell the story. libpcap is the capture library, and the versioned name in the list is the Debian packaging of it. libnl-route is the netlink library for route manipulation, which is how the tool manipulates routes inside and outside the namespace. iptables is the firewall and NAT component, and it is a hard requirement rather than an optional one, which is the same fact as the root requirement. The docbook and xsltproc entries are for building the manual page from DocBook sources, which is why the man/ directory needs a stylesheet toolchain to build.

The project is GPL-2.0 licensed, the primary language is C, the default branch is master, and the last push to the repository was on 2026-09-29. The tagged releases are v4 from 2021-01-05, v3 from 2016-08-14 and v2 from 2016-07-22, and there is no homepage listed, so the repository and its issue tracker are the whole of the project's public presence.

The release history is worth a note, because the gap between the last tag and the recent pushes is unusual. The code is being worked on in 2026 with a release from 2021 as the most recent tag, which usually means the project is maintained in a way that does not produce tagged versions often, or that the tagged versions are for the packaged distributions rather than for the source.

## Where nsntrace is the wrong tool

The first limit is that it is a single process tool with a single network stack, and most real questions are not about a single process. If you want to know what is on the wire, a host capture with tcpdump is simpler, needs no root beyond the usual, and does not renumber your addresses. nsntrace earns its complexity when the question is specifically which of several programs did something.

The second is the root requirement combined with the network manipulation. The tool rewrites routing and NAT rules on the host while it runs, and a failure partway through that setup can leave iptables rules and routes behind. On a machine where the network matters, that is a real operational risk, and the tool gives you no indication of how to undo it.

The third is the address rewriting. Your capture shows the NAT address of the virtual device rather than the real one, so any analysis that depends on the source address, an access control log on the far side, a firewall rule matching on the client address, or a server that binds to a specific interface, will not line up with what the tool recorded. For a tool whose purpose is to produce evidence about what a program did, the evidence is accurate about the payloads and misleading about the addresses.

The fourth is the resolver. On a modern system the default nameserver is a loopback address served by a local resolver, and that resolver is not reachable from inside the new namespace. The --use-public-dns option works around it by sending queries to public resolvers, which means a split-horizon name, an internal-only host, or a network that blocks outbound DNS to the public resolvers will simply fail to resolve, and the failure looks like the traced program having no network rather than like a configuration problem.

And the fifth is the scope of the tool. It captures traffic, and that is all. It does not tell you which process opened a connection beyond the one you traced, it does not decrypt anything, and it does not correlate with a log or a syscall trace. A colleague asking for the network side of a strace, or an application-level trace, or a view of the whole system at once, wants a different tool.

## Conclusion

nsntrace is worth using when you need the traffic of exactly one program, because the namespace approach gives you attribution by construction rather than by filtering, and because the output is an ordinary pcap file that the rest of your tooling already understands. It is the wrong tool when you want a host-wide capture, and it is the wrong tool when the process under test needs a working local resolver. Before you run it, check three things: that you can run it as root, since the documentation states the application needs to be run as root because of iptables and because it traces raw sockets; that you use the --use-public-dns option if the process resolves names, because the nameserver on most modern systems is a loopback address such as 127.0.0.53 where a resolver like systemd-resolved or dnsmasq listens, and that address does not exist inside the new namespace; and that you read the two stated limitations, because the source address in your capture will be the NAT address of the virtual device rather than the machine's real address, and the tool falls back to the public resolvers from Quad9, Cloudflare, Google and OpenDNS when you override the resolver configuration.

## FAQ

### How does nsntrace capture the traffic of a single process?

It uses the clone syscall with CLONE_NEWNET to create a new network namespace, launches the requested process inside it and starts a libpcap trace there, so every traced packet comes from that process. A pair of virtual interfaces connects the new namespace to the root one, the root side is set as the default gateway, and iptables with NAT forwards traffic to the real interface. The capture is written as a pcap file.

### Why does nsntrace need root, and what are the other limitations?

The documentation states that the application needs to be run as root because it uses iptables and traces raw sockets. It also notes that the ip address in the trace is the NAT address of the virtual device rather than the real address of the machine, and that a system whose nameserver is a loopback address such as 127.0.0.53 will not resolve inside the namespace unless you use the --use-public-dns option.

### How do I build nsntrace from source?

Run ./autogen.sh, then ./configure, then make, and use sudo make install to install it. The build needs automake, iptables, libpcap development headers, the libnl-route development headers, pkg-config, and the docbook and xsltproc toolchain for the manual page, using Debian and Ubuntu package names.

## Sources

- [Issues](https://github.com/nsntrace/nsntrace/issues)
- [License: GPL-2.0](https://github.com/nsntrace/nsntrace/blob/master/LICENSE)
- [nsntrace/nsntrace on GitHub](https://github.com/nsntrace/nsntrace)
- [README](https://github.com/nsntrace/nsntrace/blob/master/README.md)
- [Releases](https://github.com/nsntrace/nsntrace/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nsntrace-nsntrace
