I would like to route traffic to some specific addresses through VPN. Easy enough, one can simply add a route:

ip route add <destination ip> dev tun0 scope link

Here comes the catch: I do not want to route all traffic through the VPN, just some. Ideally, I would like to "map" an IP (e.g. a 10.x.x.x address) which would "point" to the real destination IP, but routed through the VPN. As far as I understand, this amounts to adding a route (like above), and then carrying out the equivalent of DNAT in a POSTROUTING chain.

Essentially, I would like to do a Dirty NAT trick in reverse (i.e. on the client instead of the server).

(I would like to create a simple HTTP proxy that routes traffic through the VPN only if the URL matches a given pattern. The ultimate solution would be to influence the routing of a given connection directly from a user-space program, but I think that's impossible.)

Let's say I would like to direct some traffic to, say, through tun0, but I still want some traffic to go through eth0. For example, image hosted multiple sites and I want to tunnel traffic going to only some sites.

I would like to set up a "fake" address, say, Any traffic directed from my computer to would go to, but get routed through tun0 instead of the main routing table.

I partially managed to set up: first I picked to an IP range, say Than I mark packages in the OUTPUT chain goind to that range with, say, 0x40:

sudo iptables -A OUTPUT -t mangle --dst -j MARK --set-mark 64

Next, I add DNAT rules to map the "fake" IPs from that range to the real destination. This could be probably automated:

sudo iptables -A OUTPUT -t nat --dst -j DNAT --to

Based on the mark, I route the packages by a different routing table:

sudo ip rule add fwmark 64 lookup vpn
sudo ip route add default dev tun0 scope global proto static table vpn

Unfortunately, I still cannot ping If I remove the MARK rule, I do get response, so the DNAT part works as expected.


I'm not sure I understand how you're envisioning the IP routing based solution to look, but I have a suggestion for your actual problem:

I would like to create a simple HTTP proxy that routes traffic through the VPN only if the URL matches a given pattern.

The easiest way to achieve this is by (conditional) proxy chaining. In the simplest case, for example, with squid you'd set up a parent cache_peer and set the cache_peer_domain to .netflix.com or whatever - http://wiki.squid-cache.org/Features/CacheHierarchy is a good starting point for configuring this; with apache's mod_proxy you'd use the ProxyRemote directive (and possibly NoProxy, depending on the details of your setup).

Answering your IP-based example: I believe the only problem with the MARK rule is that it's in the wrong chain; should be PREROUTING, not OUTPUT. Quoting from the iptables(1) man page:

   This target is used to set the Netfilter mark value associated with
   the packet.  It can, for example, be used in conjunction with rout‐
   ing based on fwmark (needs iproute2). If you plan on doing so, note
   that the mark needs to be set in the PREROUTING chain of the mangle
   table to affect routing.
| improve this answer | |
  • Unfortunately, there is no proxy (be it HTTP or SOCKS) on the VPN I could use. My university's library said to access full text papers (say, on link.springer.com) we should direct all our traffic to the VPN. This give us an IP address on the tunnel interface from the uni's public IP range that the publishers' sites can detect. However, I only want specific HTTP connections to go through that interface. – Kristóf Marussy Jan 8 '14 at 11:07
  • Thanks! MARK in OUTPUT indeed cannot affect the routing. I added a to lookup vpn rule to route the packages (and set the correct source address). The fwmark routing is still needed to avoid re-routing after the NAT, however. Now the packages show up in Wireshark correclty (interface, src, dst is right), but ping still shows 100% packet loss even though the ping replies arrive. – Kristóf Marussy Jan 8 '14 at 16:16

There were two problems with my original routing setup:

  1. As indicated in Gabe's answer, MARK in OUTPUT cannot influence the routing decision, only the re-routing decision after the NAT.

    This resulted in the having the wrong source address in the outgoing packets, because the source address is assigned by the routing decision and is left alone by the re-routing. I finally decided the best solution is to handle this with a MASQUERADE action based on the mark:

    iptables -A POSTROUTING -t nat -m mark --mark 64 -j MASQUERADE

  2. Linux has a reverse packet filter.

    See, for example, a comment on https://superuser.com/a/355408/184915 for the effect of the filter on VPN-related routing scenarios.

    If a packet from source address A is received on interface I, it will be dropped unless a packet to destination address A would have been routed to interface I. Of course, answers from, e.g. incoming to tun0 will be dropped, since normal routing would route packets to through eth0 in the absence of the mark.

    One can disable the packet filter with

    echo 2 > /proc/sys/net/ipv4/conf/tun0/rp_filter

    but this is considered harmful, since now forged packets pretending to come from e.g. could hit the machine through tun0. We can set up appropriate firewall rules to prevent this breach of security. Note that exploiting the aforementioned vulnerability would require compromise of the VPN server itself (only we disable the rp-filter for tun0 only), so, in my opinion, it is not a very big risk anyway.

A neat side-effect of this setup is that tun0 is never explicitly mentioned in the iptables rules. I can now add any kind of routing information to the vpn routing table and create "fake" IPs that obey it (although one must mind the rp-filter). For example, the setup is equally useful for PPTP-based VPNs by modifying a single routing table entry.

| improve this answer | |

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.