SLIRP4NETNS(1)
π slirp4netns(1) Β· User Commands π
β¨ NAME β¨
okay so slirp4netns is user-mode networking ("slirp") for network namespaces, and it's giving unprivileged energy π¦
π SYNOPSIS β¨
slirp4netns [OPTION]... PID|PATH|FD [TAPNAME]
πΈ DESCRIPTION πΈ
bestie, slirp4netns gives network namespaces user-mode networking ("slirp") π
unlike veth(4), slirp4netns does not need root privileges on the host. no cap, that's iconic π
Default configuration (these are the vibes it starts with):
- MTU:
1500 - CIDR:
10.0.2.0/24 - Gateway/Host:
10.0.2.2(network address + 2) - DNS:
10.0.2.3(network address + 3) - DHCP begin:
10.0.2.15(network address + 15) - DHCP end:
10.0.2.30(network address + 30) - IPv6 CIDR:
fd00::/64 - IPv6 Gateway/Host:
fd00::2 - IPv6 DNS:
fd00::3
π OPTIONS π
-c,--configureβ brings up the TAP interface and sets its IP to10.0.2.100(network address + 100) by default π«Ά The IPv6 address gets a random one. Starting with v0.4.0, the loopback interface (lo) gets brought up too β¨-e,--exit-fd=FDβ tellslirp4netnswhich FD to watch so it can quit πͺ Once you give it an FD, it exits when apoll(2)event happens on that FD.-r,--ready-fd=FDβ the FDslirp4netnswrites to when setup is done π It writes"1"to the FD and then closes it. Before v0.4.0, the write happened after the network configuration (-c) but before the API socket configuration (-a).-m,--mtu=MTU(since v0.2.0) β sets the MTU, max65521π-6,--enable-ipv6(since v0.2.0, EXPERIMENTAL) β turns on IPv6 π this one is still experimental, so be a little careful bestie π-a,--api-socket(since v0.3.0) β sets the API socket path π--cidr(since v0.3.0) β pick your CIDR, like10.0.2.0/24πΊοΈ--disable-host-loopback(since v0.3.0) β blocks connecting to127.0.0.1:*on the host namespace π« no sneaking into localhost!--netns-type=TYPE(since v0.4.0) β picks the network namespace type, one ofpath,pid, ortapfd. The default ispidπ
If the namespace type is tapfd, the first positional argument is supposed to be an inherited file descriptor that points at a /dev/net/tun connection. This clashes with --configure, and any TAPNAME gets ignored in this mode.
--userns-path=PATH(since v0.4.0) β sets the user namespace path π£οΈ--enable-sandbox(since v0.4.0) β enters the user namespace and makes a fresh mount namespace where only/etcand/runget mounted from the host π
β οΈ heads up: this needs/etc/resolv.confto not be a symlink to a file outside/etcand/run, or it won't work right.
When you run as root, the process doesn't enter the user namespace. Instead, every capability except CAP_NET_BIND_SERVICE gets dropped π
--enable-seccomp(since v0.4.0, EXPERIMENTAL) β turns onseccomp(2)to limit syscalls π§± it's usually paired with--enable-sandbox, so they're besties π«Ά--outbound-addr=IPv4(since v1.1.0, EXPERIMENTAL) β the outbound IPv4 address slirp should bind to π€--outbound-addr=INTERFACE(since v1.1.0, EXPERIMENTAL) β the outbound interface slirp should bind to, but ipv4 traffic only π‘--outbound-addr=IPv6(since v1.1.0, EXPERIMENTAL) β the outbound IPv6 address slirp should bind to π--outbound-addr6=INTERFACE(since v1.1.0, EXPERIMENTAL) β the outbound interface slirp should bind to, but ipv6 traffic only π°οΈ--disable-dns(since v1.1.0) β turns off the built-in DNS (10.0.2.3by default) π--macaddress(since v1.1.9) β sets the MAC address of the TAP interface π this only works together with-cπ--target-type=TYPE(since v1.2.0) β sets the target type, eithernetnsorbess. The default isnetnsπ§
The bess mode (since v1.2.0, EXPERIMENTAL) is meant to be used with User Mode Linux. It clashes with --configure, --netns-type, and --userns-path, so don't mix them π
-h,--help(since v0.2.0) β shows help and exits π-v,--version(since v0.2.0) β shows the version and exits π·οΈ
π§ EXAMPLE π§
π Terminal 1: Create user/network/mount namespaces
okay first we make the user, network, and mount namespaces π«Ά
(host)$ unshare --user --map-root-user --net --mount
(namespace)$ echo $$ > /tmp/pid
in this documentation, (host)$ is the prompt of the host shell, and (namespace)$ is the prompt of the shell running inside the namespaces β¨
if unshare fails, try these commands (they're known to be needed on Debian, Arch, and old CentOS 7.X) π
(host)$ sudo sh -c 'echo "user.max_user_namespaces=28633" >> /etc/sysctl.d/userns.conf'
(host)$ if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then sudo sh -c 'echo "kernel.unprivileged_userns_clone=1" >> /etc/sysctl.d/userns.conf'; fi
(host)$ sudo sysctl --system
π¦ Terminal 2: Start slirp4netns
now we start slirp4netns in the second terminal π
(host)$ slirp4netns --configure --mtu=65520 $(cat /tmp/pid) tap0
starting slirp, MTU=65520
π Terminal 1: Make sure tap0 is configured and connected to the Internet
back in terminal 1, let's check that tap0 is set up and actually online π
(namespace)$ ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: tap0: <BROADCAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN group default qlen 1000
link/ether c2:28:0c:0e:29:06 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
valid_lft forever preferred_lft forever
inet6 fe80::c028:cff:fe0e:2906/64 scope link
valid_lft forever preferred_lft forever
(namespace)$ echo "nameserver 10.0.2.3" > /tmp/resolv.conf
(namespace)$ mount --bind /tmp/resolv.conf /etc/resolv.conf
(namespace)$ curl https://example.com
π bind-mounting/etc/resolv.confis only needed when the host's/etc/resolv.confpoints at loopback addresses (127.0.0.X, typically from dnsmasq(8) or systemd-resolved.service(8)) that the namespace can't reach.
if the host's /etc/resolv.conf is managed by networkmanager(8) or systemd-resolved.service(8), you might need to mount a fresh filesystem on /etc instead π§ that way the new /etc/resolv.conf doesn't get unmounted unexpectedly when the host regenerates its own.
(namespace)$ mkdir /tmp/a /tmp/b
(namespace)$ mount --rbind /etc /tmp/a
(namespace)$ mount --rbind /tmp/b /etc
(namespace)$ mkdir /etc/.ro
(namespace)$ mount --move /tmp/a /etc/.ro
(namespace)$ cd /etc
(namespace)$ for f in .ro/*; do ln -s $f $(basename $f); done
(namespace)$ rm resolv.conf
(namespace)$ echo "nameserver 10.0.2.3" > resolv.conf
(namespace)$ curl https://example.com
these steps can be simplified with rootlesskit --copy-up=/etc if rootlesskit is installed π§β¨
(host)$ rootlesskit --net=slirp4netns --copy-up=/etc bash
(namespace)$ cat /etc/resolv.conf
nameserver 10.0.2.3
β¨ INTER-NAMESPACE COMMUNICATION β¨
okay so the easiest way to let namespaces talk to each other is to nest network namespaces inside slirp4netns's network namespace π§ no cap it's so clean
(host)$ nsenter -t $(cat /tmp/pid) -U --preserve-credentials -n -m
(namespace)$ mount -t tmpfs none /run
(namespace)$ ip netns add foo
(namespace)$ ip netns add bar
(namespace)$ ip link add veth-foo type veth peer name veth-bar
(namespace)$ ip link set veth-foo netns foo
(namespace)$ ip link set veth-bar netns bar
(namespace)$ ip netns exec foo ip link set veth-foo name eth0
(namespace)$ ip netns exec bar ip link set veth-bar name eth0
(namespace)$ ip netns exec foo ip link set lo up
(namespace)$ ip netns exec bar ip link set lo up
(namespace)$ ip netns exec foo ip link set eth0 up
(namespace)$ ip netns exec bar ip link set eth0 up
(namespace)$ ip netns exec foo ip addr add 192.168.42.100/24 dev eth0
(namespace)$ ip netns exec bar ip addr add 192.168.42.101/24 dev eth0
(namespace)$ ip netns exec bar ping 192.168.42.100
BUT π this method breaks the second you want namespaces across multiple slirp4netns instances. the fix is to team up with another network stack, like vde_plug(1), and pair it with slirp4netns π
(host)$ vde_plug --daemon switch:///tmp/switch null://
(host)$ nsenter -t $(cat /tmp/pid-instance0) -U --preserve-credentials -n
(namespace-instance0)$ vde_plug --daemon vde:///tmp/switch tap://vde
(namespace-instance0)$ ip link set vde up
(namespace-instance0)$ ip addr add 192.168.42.100/24 dev vde
(namespace-instance0)$ exit
(host)$ nsenter -t $(cat /tmp/pid-instance1) -U --preserve-credentials -n
(namespace-instance1)$ vde_plug --daemon vde:///tmp/switch tap://vde
(namespace-instance1)$ ip link set vde up
(namespace-instance1)$ ip addr add 192.168.42.101/24 dev vde
(namespace-instance1)$ ping 192.168.42.100
πΈ INTER-HOST COMMUNICATION πΈ
VXLAN is known to work, and it's giving π¦ For a super cute real-life example of a multi-node rootless Kubernetes cluster running on VXLAN, go check out the Usernetes project: https://github.com/rootless-containers/usernetes
π BESS MODE (FOR USER MODE LINUX) π
slirp4netns (since v1.2.0) can also act as a BESS-compatible server, which means it can give network connectivity to User Mode Linux. iconic π
Terminal 1: start slirp4netns
(host)$ slirp4netns --target-type=bess /tmp/bess.sock
Terminal 2: start User Mode Linux
(host)$ linux.uml vec0:transport=bess,dst=/tmp/bess.sock,depth=128,gro=1 root=/dev/root rootfstype=hostfs init=/bin/bash mem=2G
(UML)$ ip addr add 10.0.2.100/24 dev vec0
(UML)$ ip link set vec0 up
(UML)$ ip route add default via 10.0.2.2
β οΈ bestie, heads up: only a single instance of User Mode Linux can connect to the slirp4netns BESS server at a time. one UML only, no sharing π ββοΈ
If you want more UML deets, check out the User Mode Linux documentation: https://www.kernel.org/doc/html/latest/virt/uml/user_mode_linux_howto_v2.html#bess-socket-transport π
π BUGS π
okay this one is a little drama π Kernel 4.20 bumped the default value of /proc/sys/net/ipv4/tcp_rmem from 87380 to 131072. this is known to slow down slirp4netns port forwarding, so if your forwarding feels sluggish, this is probably why: https://github.com/rootless-containers/slirp4netns/issues/128.
the workaround is to adjust the value of /proc/sys/net/ipv4/tcp_rmem from inside the namespace. no real root privilege is needed to modify the file since kernel 4.15, so you can totally do this as a rootless girlie π
(host)$ nsenter -t $(cat /tmp/pid) -U --preserve-credentials -n -m
(namespace)$ c=$(cat /proc/sys/net/ipv4/tcp_rmem); echo $c | sed -e s/131072/87380/g > /proc/sys/net/ipv4/tcp_rmem
π SEE ALSO π
π AVAILABILITY π
the slirp4netns command lives at https://github.com/rootless-containers/slirp4netns and is released under the GNU GENERAL PUBLIC LICENSE Version 2 (or later) β¨ go forth and be free bestie π«Ά
Rootless Containers, January 2022, SLIRP4NETNS(1) π that's a wrap, bestie, you're literally a Unix queen now ππ§π
π¦ where this page comes from
- alpine 3.24: slirp4netns-doc 1.3.3-r0 Β· GPL-2.0-or-later
- arch rolling: slirp4netns 1.3.7-1 Β· GPL-2.0-or-later
- debian trixie: slirp4netns 1.2.1-1.1 Β· GPL-2.0-or-later
- nixpkgs unstable: slirp4netns 1.3.7
- ubuntu questing: slirp4netns 1.2.1-1.1 Β· GPL-2.0-or-later
- ubuntu resolute: slirp4netns 1.3.3-1 Β· GPL-2.0-or-later
- ubuntu stonking: slirp4netns 1.3.3-1 Β· GPL-2.0-or-later
- π girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms