1

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, 8.8.8.8 through tun0, but I still want some traffic to 8.8.8.8 go through eth0. For example, image 8.8.8.8 hosted multiple sites and I want to tunnel traffic going to only some sites.

I would like to set up a "fake" address, say, 10.1.2.3. Any traffic directed from my computer to 10.1.2.3 would go to 8.8.8.8, 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 10.1.0.0/16. Than I mark packages in the OUTPUT chain goind to that range with, say, 0x40:

sudo iptables -A OUTPUT -t mangle --dst 10.1.0.0/16 -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 10.1.2.3 -j DNAT --to 8.8.8.8

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 10.1.2.3. If I remove the MARK rule, I do get response, so the DNAT part works as expected.

1

2 Answers 2

1

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:

MARK
   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.
2
  • 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. Jan 8, 2014 at 11:07
  • Thanks! MARK in OUTPUT indeed cannot affect the routing. I added a to 10.1.0.0/16 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. Jan 8, 2014 at 16:16
1

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. 8.8.8.8 incoming to tun0 will be dropped, since normal routing would route packets to 8.8.8.8 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. 127.0.0.1 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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .