Run a Linux Program on a Different Network Interface
Sometimes, you have multiple Internet connections, whether physical or virtual, and you want a few programs to access the Internet through one connection without making it the default gateway. For example, if you want a program to connect to the Internet through a VPN, but without forcing the entire system’s traffic through the VPN as well.
The traditional way to do this is with packet marking with iptables
and an
ip rule
to force marked packets through a different routing table to send
the traffic to the correct destination. However, as the source IP was selected
before routing, an SNAT
rule in iptables
is required to change the source
IP. This is ugly and clearly a hack.
However, since around 2013, Linux has introduced networking namespaces, which
can be managed via ip netns
as part of the iproute2
package. We can easily
exploit this feature to achieve the desired goal with minimal fuss.
Assumptions
In this tutorial, we will assume that you have two network interfaces, eth0
and tap0
, with gateways 10.0.0.1
and 192.168.0.1
. The system’s default
route is 10.0.0.1
on eth0
, but you want to run curl
and have the traffic
go through tap0
. This should be easily adaptable to your situation.
Furthermore, we assume that our IP on tap0
is 192.168.0.2/24
, and that
all the commands listed here are run as root
.
We will only cover IPv4 in this guide, but the same concept works with IPv6. Only different IP addresses are necessary.
Steps
The first step is to create a new network namespace, which we will call test
.
To do this, run ip netns add test
. To run programs inside this namespace,
we can use ip netns exec test <command> [arguments...]
.
We then need to create a pair of virtual network interfaces to enable communication
between the original namespace and the test
namespace. To do this, we run
ip link add veth_test type veth peer name vpeer_test
. This creates a pair of
interfaces veth_test
and vpeer_test
. The former will used in the original
namespace, while the latter will be used in the test
namespace. We can move
vpeer_test
to the test
namespace by running
ip link set vpeer_test netns test
.
To make this interface work, we need to create a new network bridge, which we
will call br0
in this example. This is so that tap0
and veth_test
will be
connected at the link level, allowing vpeer_test
to be connected to the same
network as tap0
.
This can easily be done with brctl
:
brctl addbr br0
brctl addif br0 tap0 veth_test
ip link set br0 up
If the host is to retain access to tap0
, all configuration on tap0
will have
to be replicated onto br0
. You might be able to do this with:
ip addr flush dev tap0
ip addr add 192.168.0.2/24 dev br0
We need to assign an IP for the vpeer_test
interface in the test
namespace.
In this example, we will use 192.168.0.3
.
To configure the interface vpeer_test
(add IP, default route, and bring it up),
we can run:
ip netns exec test ip addr add 192.168.0.3/24 dev vpeer_test
ip netns exec test ip link set vpeer_test up
ip netns exec test ip route add default dev vpeer_test via 192.168.0.1
At this point, ip netns exec test curl
will now access the Internet through
tap0
. For example, running ip netns exec test curl ifconfig.co
will return
tap0
’s public IP address.
/etc/network/interfaces
The bridge configuration can be simplified by using /etc/network/interfaces
,
if you are using Debian or Ubuntu:
iface br0 inet static
bridge_ports br0 veth_test
address 192.168.0.2
netmask 255.255.255.0
Then, the bridge can easily be brought up by
ip addr flush dev tap0
ifup br0
Summary
The following script should perform the setup steps automatically:
#!/bin/sh
ip netns add test
ip link add veth_test type veth peer name vpeer_test
ip link set vpeer_test netns test
ip addr flush dev tap0
brctl addbr br0
brctl addif br0 tap0 veth_test
ip link set br0 up
ip addr add 192.168.0.2/24 dev br0
ip netns exec test ip addr add 192.168.0.3/24 dev vpeer_test
ip netns exec test ip link set vpeer_test up
ip netns exec test ip route add default dev vpeer_test via 192.168.0.1
Then, prefix commands with ip netns exec test
to run them in this namespace.
To undo the setup steps, simply run:
#!/bin/sh
ip link set br0 down
brctl delbr br0
ip link del veth_test
ip netns del test