Linux – How to forward traffic between Linux network namespaces

iptableslinuxnetwork-namespacesnetworking

I was able to set up a network namespace and start a server that listens on 127.0.0.1 inside the namespace:

# ip netns add vpn
# ip netns exec vpn ip link set dev lo up
# ip netns exec vpn nc -l -s 127.0.0.1 -p 80 &

# ip netns exec vpn netstat -tlpn

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN      5598/nc

After that, I can connect to the server inside the namespace:

# ip netns exec vpn nc 127.0.0.1 80 -zv
localhost [127.0.0.1] 80 (http) open

But I can't connect to the server outside the namespace:

# nc 127.0.0.1 80
(UNKNOWN) [127.0.0.1] 80 (http) : Connection refused

How to configure iptables or namespace to forward traffic from the global namespace to the vpn namespace?

Best Answer

First: I don't think you can achieve this by using 127.0.0.0/8 and/or a loopback interface (like lo). You have to use some other IPs and interfaces, because there are specific things hardwired for 127.0.0.0/8 and for loopback.

Then there is certainly more than one method, but here's an example:

# ip netns add vpn
# ip link add name vethhost0 type veth peer name vethvpn0
# ip link set vethvpn0 netns vpn
# ip addr add 10.0.0.1/24 dev vethhost0
# ip netns exec vpn ip addr add 10.0.0.2/24 dev vethvpn0
# ip link set vethhost0 up
# ip netns exec vpn ip link set vethvpn0 up
# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.134 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.100 ms

The first command creates out of thin air a pair of virtual ethernet interfaces connected by a virtual ethernet cable. The second command moves one of these interfaces into the netns vpn. Consider it the equivalent of things like socketpair(2) or pipe(2): a process creates a pair, then forks, and each process keeps only one end of the pair and they can communicate.

Usually (LXC, virt-manager,...) there's also a bridge involved to put everything in the same LAN when you have many netns.

Once this is in place, for the host it's like any router. Enable ip forwarding (be more restrictive if you can: you need it at least for vethhost0 and the main interface):

# echo 1 > /proc/sys/net/ipv4/conf/all/forwarding

Add some DNAT rule, like:

# iptables -t nat -A PREROUTING ! -s 10.0.0.0/24 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.2

Now you can either add a default route inside vpn with:

# ip netns exec vpn ip route add default via 10.0.0.1

Or else, instead, add a SNAT rule to have everything be seen as coming from 10.0.0.1 inside vpn.

# iptables -t nat -A POSTROUTING -d 10.0.0.2/24 -j SNAT --to-source 10.0.0.1

With this in place you can test from any other host, but not from the host itself. To do this, also add a DNAT rule similar to the previous DNAT, but in OUTPUT and changed (else any outgoing http connexion would be changed too) to your own IP. Let's say your IP is 192.168.1.2:

# iptables -t nat -A OUTPUT -d 192.168.1.2 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.2

Now it will even work if you connect from the host to itself if you don't use a loopback ip, but any other IP belonging to the host with a nat rule as above. Let's say your IP is 192.168.1.2:

# ip netns exec vpn nc -l -s 10.0.0.2 -p 80 &
[1] 10639
# nc -vz 192.168.1.2 80
nc: myhost (192.168.1.2) 80 [http] open
#
[1]+  Done                    ip netns exec vpn nc -l -s 10.0.0.2 -p 80
Related Question