Route the traffic over specific interface for a process in linux

21

24

Is it possible to route the traffic used by a process over a specific interface?

For example, network traffic by download application should always use the interface wlan0 whereas all other applications on the machine should use eth0.

Is it possible to have this kind of rule in Linux?

Suresh

Posted 2011-04-17T11:38:53.760

Reputation: 453

Answers

22

It can be done using Linux Network Namespaces.

Here is an article that explains how. Basically you create a network namespace with a different default route and run the processes that need it there. You connect the newly created network namespace to the physical adapter with a bridge (but other solutions are possible of course).

Update: from kernel 3.14 it's even easier using control groups, as described in this article. You have to:

1) define a net_cls control group to annotate the packets from a given process with a classid (or group of process, note that there hasn't to be any parent-child relationship between them)

2) use the iptables cgroup module (added in Linux 3.14) to fwmark the packets

3) use policy routing (ip rule add fwmark ....) to create a new routing table for the marked packets

The advantage is that we don't have to do the bridging thing and everything is much more dynamic thanks s to cgroups.

chripell

Posted 2011-04-17T11:38:53.760

Reputation: 221

15

I have sooo struggled with this so here's a COMPLETE solution. It's tested on Ubuntu 15 to 19.10. You can especially use it with OpenVPN to route certain apps outside the VPN tunnel interface.

The complete "cgroup" solution

How that works?

  • Linux kernel will put the app into a control group. Network traffic from apps in this cgroup will be identified by their class ID at the network controller level.
  • iptables will mark this traffic and force it to exit with the right IP
  • ip route will process marked traffic in a different routing table, with a default route to whatever gateway IP you want.

Automated script

I've made a novpn.sh script to automate dependencies install & run. Tested on Ubuntu 15 to 19.10.

Start your VPN first.

wget https://gist.githubusercontent.com/kriswebdev/a8d291936fe4299fb17d3744497b1170/raw/novpn.sh
# If you don't use eth0, edit the script setting.
sudo chmod +x novpn.sh
./novpn.sh traceroute www.google.com
./novpn.sh --help

Manual HowTo

First, install cgroup support and tools:

sudo apt-get install cgroup-lite cgroup-tools

You need iptables 1.6.0+. Get iptables 1.6.0 release source, extract it, then run this (--disable-nftables flag will avoid errors) from iptables source dir:

iptables --version
sudo apt-get install dh-autoreconf bison flex
./configure --prefix=/usr      \
            --sbindir=/sbin    \
            --disable-nftables \
            --enable-libipq    \
            --with-xtlibdir=/lib/xtables
make
sudo make install
iptables --version

Now, the real config. Define a control group named novpn. Processes in this cgroup will have a classid of 0x00110011 (11:11).

sudo su
mkdir /sys/fs/cgroup/net_cls/novpn
cd /sys/fs/cgroup/net_cls/novpn
echo 0x00110011 > net_cls.classid

Now, we'll suppose the real interface you want to use for the specific app is eth0 with a gateway IP of 10.0.0.1. REPLACE these by what you really want (get the info from ip route), especially in newer Ubuntu versions where interfaces have weird names. Run still as root:

# Add mark 11 on packets of classid 0x00110011
iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x00110011 -j MARK --set-mark 11

# Force the packets to exit through eth0 with NAT
iptables -t nat -A POSTROUTING -m cgroup --cgroup 0x00110011 -o eth0 -j MASQUERADE

# Define a new "novpn" routing table
# DO THIS JUST ONCE !
echo 11 novpn >> /etc/iproute2/rt_tables

# Packets with mark 11 will use novpn
ip rule add fwmark 11 table novpn

# Novpn has a default gateway to the interface you want to use
ip route add default via 10.0.0.1 table novpn

# Unset reverse path filtering for all interfaces, or at least for "eth0" and "all"
for i in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $i; done

Finally, run your app on the specific interface:

exit
sudo cgcreate -t $USER:$USER -a $USER:$USER -g net_cls:novpn
cgexec -g net_cls:novpn traceroute www.google.com
# Close all Firefox windows first
killall firefox; cgexec -g net_cls:novpn firefox

Or if you want to move an already running process to the cgroup, well... you can't! That seems to be due to the NAT (masquerade) function: iptables -nvL -t nat doesn't match when the cgroup is switched, but iptables -nvL -t mangle does match.

# Get PID of the process (we'll then suppose it's 1234)
pidof firefox
# Add to cgroup - THIS DOESN'T WORK! Silently fails to produce the final result.
sudo echo 1234 > /sys/fs/cgroup/net_cls/novpn/tasks
# Remove - but this works...
sudo echo 1234 > /sys/fs/cgroup/net_cls

Credits: No answer were working as expected, but a mix of them did: chripell answer evolware article Per process routing take 2: using cgroups, iptables and policy routing, How do I make a specific process NOT going through a OpenVPN connection?, Kill switch for OpenVPN on the basis of iptables

KrisWebDev

Posted 2011-04-17T11:38:53.760

Reputation: 578

Thanks for the great answer, how do I make it work with apache? I tried cgexec -g net_cls:novpn apache2 and gave me whole list of variable undefined erros! – razzak – 2016-07-18T23:41:02.893

2You'll get the same errors by running apache2 directly from the terminal. That's because apache2 is normally launched as a service, with systemctl start apache2. However, that won't work with cgexec. The called program must be a parent of the desired (apache2) process for net_cls cgroup to propagate. So you need to find the launch script. In this case, it's sudo cgexec -g net_cls:novpn /usr/sbin/apache2ctl start. Check with ./novpn.sh --list. – KrisWebDev – 2016-07-19T09:24:12.933

It doesn't work in ubuntu 16.04 anymore! – razzak – 2016-08-04T15:10:45.213

2I'm using it on Ubuntu 16.04 and it's working fine. Interface names have changed in Ubuntu 16, you may replace need to replace eth0 by something like enp7s0. Get the info from ifconfig command. – KrisWebDev – 2017-06-24T05:48:49.277

Thank you. But it does not work on Ubuntu 18.04 because of cpmanager has been removed due to a conflict with systemd – skonsoft – 2019-11-26T14:06:28.267

Actually cgmanager is not needed, I remove it from the instructions. cgroup-lite is needed and it is still present currently in Ubuntu 19.10. – KrisWebDev – 2019-12-23T08:08:10.697

@KrisWebDev I'm having problems with apache2 in ubuntu 19.10. I start the script with sudo ./novpn.sh /usr/sbin/apache2ctl start which runs the setup process fine but the cgexec command seems to be doing nothing. When I do ./novpn.sh -l it shows nothing in the PID list. – razzak – 2019-12-26T13:47:45.443

4

A few people have written shims that use the LD_PRELOAD feature of Linux to achieve this:

user1686

Posted 2011-04-17T11:38:53.760

Reputation: 283 655

3

Combining the excellent answers of mariusmatutiae and KrisWebDev I have created an extensively modified version KrisWebDev's excellent novpn.sh script. Whereas KrisWebDev's script is designed to scratch a more specific itch (running and moving processes inside/outside a VPN), my version allows you to run basically any command in a networking environment you specify. You can specify the interface to bind to, the default route, specify your own iptables rules, static routes, specify a "test" to confirm everything is operating as you desire before running the command... etc). It allows you to use multiple config files so that you can define any number of specific networking environments that you can run commands/processes inside of.

I posted it as a gist here: https://gist.github.com/level323/54a921216f0baaa163127d960bfebbf0

It can even clean up the cgroup/iptables/routing tables afterwards!

Feedback welcome.

PS - It's designed for Debian 8 (Jessie)

allnatural

Posted 2011-04-17T11:38:53.760

Reputation: 51

Hi @allnatural, I would like to use your altnetworking.sh script, but what am I supposed to put into the configuration file? – Cris70 – 2017-03-29T10:44:11.557

0

Not per application, no. You can do it per port or per ip-address etc, or an application itself can bind to (and use) a specific network card.

You can't set up a rule to do it though.

Majenko

Posted 2011-04-17T11:38:53.760

Reputation: 29 007

I've a java application, is it possible to bind that app to an interface? – Suresh – 2011-04-17T13:36:57.980

A Java application you have the source code for and can re-program the internals? – Majenko – 2011-04-17T13:48:27.473

I've a java application source code that uses apache http library. – Suresh – 2011-04-17T13:52:31.077

Then I would head over to http://www.stackoverflow.com and ask the programmers there how to change your program. I have never programmed in Java.

– Majenko – 2011-04-17T14:04:40.337