I have a linux machine that routes its whole internet traffic through an openVPN connection. It was explicitly configured to work that way.

However, the machine has a few applications that should not use the VPN connection (tun+) and go through the open connection (eth0).

As it's not a routing based on the destination ip, my first though was to create a local socks5 proxy where applications should use to avoid the openVPN channel. However, my attempts to do this with ssh -D or other sw) has failed (probably my fault) and I am now wondering if there is a different and better solution for that.

  • You are basically asking for split routing, meaning you need multiple route tables, and you need to use your firewall or something to direct applications to the correct table. See: lartc.org/howto/lartc.rpdb.multiple-links.html – Zoredache Feb 17 '15 at 23:02
  • @zoredache exactly, if it was based on ip dest or port, I could have used different route tables as you suggested. But as I am looking to do it per application, I am not so sure how I can implement it. – roymreelee Feb 17 '15 at 23:38
  • 1
    Spend some more time reading that document, not just the section I linked to. The answers are there. Specifically you'll want to look at marking things with iptables. Which can see users, and processes. – Zoredache Feb 17 '15 at 23:57

I recently posted another answer that addresses this specific situation, but questions are not duplicates as this question is more specific to OpenVPN. As some people like to downvote for links to answers rather than putting the answer directly in the question, I'm copy/pasting it here.

I have sooo struggled with this so here's a COMPLETE solution. It works on Ubuntu 15. 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.


First, install cgroup support and tools:

sudo apt-get install cgroup-lite cgmanager 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:

sudo apt-get install dh-autoreconf bison flex
./configure --prefix=/usr      \
            --sbindir=/sbin    \
            --disable-nftables \
            --enable-libipq    \
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 interface you want to use for the specific app is eth0 with a gateway IP of Replace these by what you really want (get the info from ip route). 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
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 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:

sudo cgcreate -t $USER:$USER -a $USER:$USER -g net_cls:novpn
cgexec -g net_cls:novpn traceroute www.google.com
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

  • This doesn't appear to work completely for me on Ubuntu 16.04 using iptables v1.6.0 from the Ubuntu repos. Running commands with cgexec -g net_cls:novpn shows that packets are going outside of the VPN, but then stop when they get to the router. – TommyPeanuts Jul 25 '18 at 19:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.