Linux: routing based on domain names
On ubuntu 16.04, I would like to route my traffic either through the direct internet eth0
or my VPN tun0
based on the domain name entered into the browser. The reason being local sites are either slow or location dependent.
I realize the kernel routing table is IP based and domain names are usually resolved in the software layer, but with linux being a script friendly platform, I'm hoping for a workaround. Though, I have no idea how to write such a script.
So far I've found the dig example.com +short @8.8.8.8
command will list the IPs associated with a domain, and I've figured out the sudo route add -net 8.8.8.8 netmask 255.255.255.255 gw 192.168.2.1
command will bypass the VPN for a given IP (where 192.168.2.1 is my default eth0
). Would somebody be kind enough to template a script which reads a file containing the domain names and enters the route rules upon system boot. Bonus points for allowing masked sub domains *.example.com
.
If there is an easier method to this madness I'll accept it as a solution.
Note: I could very easily hard code the IPs into /etc/network/interfaces
but then they become hard to manage. I also tried hard coding all known IPs for my country into this file, but it was very hit and miss along with a delayed boot up time.
linux networking vpn dns routing
add a comment |
On ubuntu 16.04, I would like to route my traffic either through the direct internet eth0
or my VPN tun0
based on the domain name entered into the browser. The reason being local sites are either slow or location dependent.
I realize the kernel routing table is IP based and domain names are usually resolved in the software layer, but with linux being a script friendly platform, I'm hoping for a workaround. Though, I have no idea how to write such a script.
So far I've found the dig example.com +short @8.8.8.8
command will list the IPs associated with a domain, and I've figured out the sudo route add -net 8.8.8.8 netmask 255.255.255.255 gw 192.168.2.1
command will bypass the VPN for a given IP (where 192.168.2.1 is my default eth0
). Would somebody be kind enough to template a script which reads a file containing the domain names and enters the route rules upon system boot. Bonus points for allowing masked sub domains *.example.com
.
If there is an easier method to this madness I'll accept it as a solution.
Note: I could very easily hard code the IPs into /etc/network/interfaces
but then they become hard to manage. I also tried hard coding all known IPs for my country into this file, but it was very hit and miss along with a delayed boot up time.
linux networking vpn dns routing
The only way I see is to modify an existing DNS caching server like pdns to add routes as soon as hosts are looked up. I don't think you can solve that with scripting, and you'll also need a way to prevent the routing table from getting too full. Not an easy problem.
– dirkt
Mar 7 '17 at 6:56
add a comment |
On ubuntu 16.04, I would like to route my traffic either through the direct internet eth0
or my VPN tun0
based on the domain name entered into the browser. The reason being local sites are either slow or location dependent.
I realize the kernel routing table is IP based and domain names are usually resolved in the software layer, but with linux being a script friendly platform, I'm hoping for a workaround. Though, I have no idea how to write such a script.
So far I've found the dig example.com +short @8.8.8.8
command will list the IPs associated with a domain, and I've figured out the sudo route add -net 8.8.8.8 netmask 255.255.255.255 gw 192.168.2.1
command will bypass the VPN for a given IP (where 192.168.2.1 is my default eth0
). Would somebody be kind enough to template a script which reads a file containing the domain names and enters the route rules upon system boot. Bonus points for allowing masked sub domains *.example.com
.
If there is an easier method to this madness I'll accept it as a solution.
Note: I could very easily hard code the IPs into /etc/network/interfaces
but then they become hard to manage. I also tried hard coding all known IPs for my country into this file, but it was very hit and miss along with a delayed boot up time.
linux networking vpn dns routing
On ubuntu 16.04, I would like to route my traffic either through the direct internet eth0
or my VPN tun0
based on the domain name entered into the browser. The reason being local sites are either slow or location dependent.
I realize the kernel routing table is IP based and domain names are usually resolved in the software layer, but with linux being a script friendly platform, I'm hoping for a workaround. Though, I have no idea how to write such a script.
So far I've found the dig example.com +short @8.8.8.8
command will list the IPs associated with a domain, and I've figured out the sudo route add -net 8.8.8.8 netmask 255.255.255.255 gw 192.168.2.1
command will bypass the VPN for a given IP (where 192.168.2.1 is my default eth0
). Would somebody be kind enough to template a script which reads a file containing the domain names and enters the route rules upon system boot. Bonus points for allowing masked sub domains *.example.com
.
If there is an easier method to this madness I'll accept it as a solution.
Note: I could very easily hard code the IPs into /etc/network/interfaces
but then they become hard to manage. I also tried hard coding all known IPs for my country into this file, but it was very hit and miss along with a delayed boot up time.
linux networking vpn dns routing
linux networking vpn dns routing
asked Mar 6 '17 at 18:51
TwiftyTwifty
11816
11816
The only way I see is to modify an existing DNS caching server like pdns to add routes as soon as hosts are looked up. I don't think you can solve that with scripting, and you'll also need a way to prevent the routing table from getting too full. Not an easy problem.
– dirkt
Mar 7 '17 at 6:56
add a comment |
The only way I see is to modify an existing DNS caching server like pdns to add routes as soon as hosts are looked up. I don't think you can solve that with scripting, and you'll also need a way to prevent the routing table from getting too full. Not an easy problem.
– dirkt
Mar 7 '17 at 6:56
The only way I see is to modify an existing DNS caching server like pdns to add routes as soon as hosts are looked up. I don't think you can solve that with scripting, and you'll also need a way to prevent the routing table from getting too full. Not an easy problem.
– dirkt
Mar 7 '17 at 6:56
The only way I see is to modify an existing DNS caching server like pdns to add routes as soon as hosts are looked up. I don't think you can solve that with scripting, and you'll also need a way to prevent the routing table from getting too full. Not an easy problem.
– dirkt
Mar 7 '17 at 6:56
add a comment |
3 Answers
3
active
oldest
votes
I'd recomend you to avoid managing routing based on domain names (by the way, it is also impossible to resolve wildcard subdomain, whether it is bonus points for it or not :D)
To be a bit descriptive, you shouldn't do that because:
1) some domains changes their IPs time to time,
2) it is impossible to match wildcards in subdomains
3) it is impossible to know/fetch all subdomains of any domain
4) any random subdomain can have any random IP address.
So, the solution as browser addon (and/or custom local proxy like squid) is the best option for your issue.
But, I guess, "FoxyProxy" addon (it is originally Firefox addon, but AFAIRC, it is alfo Chrome version exists) is exactly what you want.
And, also, answering to your notice that "FoxyProxy is paid service and you already have your vpn":
FoxyProxyPlus is paid service, but not FoxyProxy.
FoxyProxy is addon, available for major browsers:
Standard Edition (Firefox) | Basic Edition (Firefox)
Standard Edition (Chrom{e,ium}) | Basic Edition (Chrom{e,ium})
So, if you want to go to some domains through VPN, you should:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) and/or write the rules list for squid
3) capture http/https traffic not owned by squid with iptables and point it to squid by rule like this:
iptables -m owner -m multiport -t nat -A OUTPUT ! -o lo ! --uid-owner $squid_user_id -p tcp --dports 80,443,8080,... -j REDIRECT --to-ports $SQUID_PORT
(--syn
option may be needed for -p tcp)
4) capture http/https traffic owned by squid, and mark it for next routing it to VPN with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
5)
echo 11 forcevpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table forcevpn
ip route add default via 10.0.0.1 table forcevpn
where 10.0.0.1
is you gateway inside VPN. Or you can use dev $VPN_IF
instead of via 10.0.0.1
if you have no gateway and just want to push all the traffic in the vpn interface.
6) optionally, you may be need to run sudo sysctl ipv4.conf.all.rp_filter
=0
===
And one more thing:
If you want to do same magick with non-http(s) TCP traffic, you'll be need something like proxychains, and perform similar capturing magic.
And, if you want to do that magic with UDP, I've a bad news: I don't know any proxy capable of proxying of UDP (because of the nature of this protocol) :)
⇓⇓⇓ EDIT ⇓⇓⇓
In case, you want the reverse thing (default gw = vpn, and rule some domains directly through ISP), it can be:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) capture traffic owned by squid, and mark it for next routing it another way with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
3)
echo 11 novpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table novpn
ip route add default via ${ISP_GW} table novpn
where ISP_GW
is the gateway you use to route traffic to your VPN server. Some users may want to use dev ppp0
(or ppp1
, ..., pppN
) instead of via ${ISP_GW}
in case if they use pptp to connect to the internet.
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
To anybody wishing to set up something similar. Check your/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in/etc/network/if-up.d/
to be set upon boot.
– Twifty
Mar 18 '17 at 18:47
add a comment |
Routing based on destination domain is not impossible, and, with the right tools, not all that hard.
I'll present a few methods that require little or no special client side configuration. These all assume you are using OpenVPN to connect. This should be achievable with other VPNs, but may require more manual configuration after the VPN is brought up.
For example purposes, I'll use the domains "example.com", "us1.example.com", "us2.example.com", and "geoblocked.com" for the domains we want to route through the non-VPN interface.
All commands should be run as root.
Method 1 - OpenVPN
I would only recommend this if you are certain the IP addresses you are routing have static IPs that never change.
Pros:
- Extremely simple
Cons:
- Only reliable for domains with IPs that never change
- Need an explicit entry for every domain and subdomain
Method:
Add the following lines to your OpenVPN configuration:
route example.com 255.255.255.255 net_gateway
route us1.example.com 255.255.255.255 net_gateway
route us2.example.com 255.255.255.255 net_gateway
route geoblocked.com 255.255.255.255 net_gateway
Restart OpenVPN.
That's it, but you'll have to restart VPN again if those IP addresses ever change.
NOTE: Some sources say you also need to specify allow-pull-fqdn
, but that did not seem to be the case in my experience. YMMV.
Method 2 - Policy based routing
Policy based routing is the ability to route based on certain criteria; commonly a source address or protocol, but in this case we will inspect the destination domain name prior to routing and use marked packets ("fwmark").
So what we need to do first is create a separate table for your VPN routed
packets, so that we can mark those that go through the VPN, and passing marked packets through the non-VPN interface. (Keep in mind, this is one approach and there are many other ways to approach this, such as letting the VPN do its routing as normal through the main table and creating a separate table for non-VPN traffic.)
Your kernel must be recent enough and have the proper modules, although modern systems probably have them in their default kernels.
The name "vpn_table" (the routing table name), and the numbers "201" (routing table ID) and "3" (fwmark) are arbitrarily chosen.
Create the new routing table (as root):
echo 201 vpn_table >> /etc/iproute2/rt_tables
Configure OpenVPN:
Create the following script somewhere (I am calling it "/etc/openvpn/client/setup-routing") and make it executable:
#!/bin/bash
ip route add 0.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
ip route add 128.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
sysctl -w net.ipv4.conf.$dev.rp_filter=2
# You can optionally leave the next two lines out but run the `ip rule add`
# command at each boot instead
ip rule del fwmark 3 table vpn_table &>/dev/null # This might fail but that's ok
ip rule add fwmark 3 table vpn_table
The variables in the above script will be populated as environment variables by OpenVPN. Also note this sets up routing to all addresses through the VPN gateway in the "vpn_table" routing table. If your VPN setup requires more complex routing, refer to the OpenVPN documentation and adjust accordingly.
Add the following to your OpenVPN configuration:
## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
The "route-noexec" line permits OpenVPN to fetch the route from the server, but prevents it from actually populating the routes. Instead the route-up script is called. "script-security 2" is necessary to call a user-defined script.
That is all the necessary set up to route the marked packets, but we need to set up a way to actually mark the packets. Two options are using dnsmasq with ipset, or setting up a squid proxy.
Method 2a - Policy based routing using ipset and dnsmasq
I would recommend this method if you are already running this on a dnsmasq-based router or your clients do not support proxy config. This is effectively the same as a caching DNS that updates the routing table whenever a domain name is looked up.
Pros:
- Handles subdomains
- Works on devices that can't access proxies (do those exist?)
Cons:
- Does not handle referrer field (see Method 2b)
- Needs complicated ipset and iptables configs
- Requires the VPN connected system to be set up as a router (needs dedicated
interface) - I have no idea how scalable ipset is (my use case is for a whole ccTLD)
This assumes you already have dnsmasq configured and set up, and acting as a gateway and DNS server for clients connected to a dedicated interface "eth1".
Create the ipset:
ipset create SKIP_VPN_IPSET iphash
Tell iptables to mark the ipset packets (n.b., this must be done after creating the ipset list):
# Mark ALL packets coming in on eth1 - change this to the interface dnsmasq listens on
iptables -A PREROUTING -i eth1 -t mangle -j MARK --set-mark 3
# REMOVE mark on any addresses that match our ipset
iptables -A PREROUTING -t mangle -m set --match-set SKIP_VPN_IPSET dst -j MARK --set-mark 0/3
NOTE: The above commands (ipset
and iptables
) must be run at each boot. Alternatively, your OS may provide some options for saving/restoring iptable rules and ipsets.
NOTE2: There is documented an inverse ! --match-set
but that resulted in all packets just disappearing when I tried it.
Add the following to your dnsmasq.conf:
ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
Obviously adjust that line too whichever domain names you want routed. This will also add ALL subdomains to the ipset, so you do not need to explicitly specify them. Even using a TLD will work.
Restart dnsmasq and set up your clients to use the VPN connected system as both a gateway and DNS (which should be implied if it is set up as a DHCP server).
Method 2b - Policy based routing using squid
This is my favorite method and works well with my PS4 and other devices I use to connect.
Pros:
- Handles subdomains
- Handles referrer field
- Does not require replacing your existing router
- Clients (browsers) can optionally use it or not
Cons:
- Clients must support proxy connection
This assumes you have a working squid setup and a basic knowledge of squid configuration.
Add the following lines to squid.conf:
# redirect example domains
acl domain_to_remote_proxy dstdomain .example.com
acl ref_to_remote_proxy referer_regex [^.]*.example.com.*
# redirect geoblocked domain
acl domain_to_remote_proxy dstdomain .geoblocked.com
acl ref_to_remote_proxy referer_regex [^.]*.geoblocked.com.*
# mark packets that we want routed through the VPN
tcp_outgoing_mark 0x03 !ref_to_remote_proxy !domain_to_remote_proxy
Note there are 2 lines per domain, and subdomains are matched. The first line checks the destination domain, and the second matches the "Referer" header. This is useful because browsers send the referer when fetching content on a webpage such as images, CSS, or javascript; this means that content requested by the site will also route through the non-VPN address even if it is hosted on a different domain (e.g., example-cdn.com).
On the clients, set up the connection like normal but set the proxy settings to use the proxy server and port for this system. Most devices (including game consoles) allow system-wide configuration. On PCs, most browsers can be configured to use a proxy independently of system settigs.
Final note - My use case is actually to route specific domains through the VPN and everything else through the non-VPN. The methods are similar to the above, but inverted.
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
add a comment |
Squid does not support socks (like ssh tunnel)... there is an option to build squid with socks support, but it's hard to get it to work.
Privoxy can do the job
- Support parent socks
- Support http/https proxy
- Support referrer
- etc.
Privoxy Setup:
Install privoxy
Edit the config file (remove everything on
/etc/privoxy
and add/etc/privoxy/config
)
user-manual /usr/share/doc/privoxy/webserver/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile default.action
filterfile default.filter
logfile logfile
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 0
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forwarded-connect-retries 0
accept-intercepted-requests 0
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
tolerate-pipelining 1
socket-timeout 300
listen-address 127.0.0.1:8888
forward-socks5 .whatismyipaddress.com 127.0.0.1:8080 .
forward-socks5 .whatismyip.com 127.0.0.1:8080 .
Restart the service
systemctl start privoxy
Setup the privoxy proxy on the client application
If you want to route referrer as well add default.action and default.filter you can test that with http://www.play-hookey.com/htmltest/ with the html code
<a href="http://amibehindaproxy.com/">test-ip</a></br>
<a href="http://www.stardrifter.org/cgi-bin/ref.cgi">test-referrer</a>
default.action and default.filter
default.action
{+client-header-tagger{referer}}
/
{+forward-override{forward-socks5 127.0.0.1:8080 .}}
TAG:.*?hookey.com
default.filter
CLIENT-HEADER-TAGGER: referer
s@^Referer:.*?$@$0@i
Restart privoxy service
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "3"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f1185861%2flinux-routing-based-on-domain-names%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
I'd recomend you to avoid managing routing based on domain names (by the way, it is also impossible to resolve wildcard subdomain, whether it is bonus points for it or not :D)
To be a bit descriptive, you shouldn't do that because:
1) some domains changes their IPs time to time,
2) it is impossible to match wildcards in subdomains
3) it is impossible to know/fetch all subdomains of any domain
4) any random subdomain can have any random IP address.
So, the solution as browser addon (and/or custom local proxy like squid) is the best option for your issue.
But, I guess, "FoxyProxy" addon (it is originally Firefox addon, but AFAIRC, it is alfo Chrome version exists) is exactly what you want.
And, also, answering to your notice that "FoxyProxy is paid service and you already have your vpn":
FoxyProxyPlus is paid service, but not FoxyProxy.
FoxyProxy is addon, available for major browsers:
Standard Edition (Firefox) | Basic Edition (Firefox)
Standard Edition (Chrom{e,ium}) | Basic Edition (Chrom{e,ium})
So, if you want to go to some domains through VPN, you should:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) and/or write the rules list for squid
3) capture http/https traffic not owned by squid with iptables and point it to squid by rule like this:
iptables -m owner -m multiport -t nat -A OUTPUT ! -o lo ! --uid-owner $squid_user_id -p tcp --dports 80,443,8080,... -j REDIRECT --to-ports $SQUID_PORT
(--syn
option may be needed for -p tcp)
4) capture http/https traffic owned by squid, and mark it for next routing it to VPN with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
5)
echo 11 forcevpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table forcevpn
ip route add default via 10.0.0.1 table forcevpn
where 10.0.0.1
is you gateway inside VPN. Or you can use dev $VPN_IF
instead of via 10.0.0.1
if you have no gateway and just want to push all the traffic in the vpn interface.
6) optionally, you may be need to run sudo sysctl ipv4.conf.all.rp_filter
=0
===
And one more thing:
If you want to do same magick with non-http(s) TCP traffic, you'll be need something like proxychains, and perform similar capturing magic.
And, if you want to do that magic with UDP, I've a bad news: I don't know any proxy capable of proxying of UDP (because of the nature of this protocol) :)
⇓⇓⇓ EDIT ⇓⇓⇓
In case, you want the reverse thing (default gw = vpn, and rule some domains directly through ISP), it can be:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) capture traffic owned by squid, and mark it for next routing it another way with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
3)
echo 11 novpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table novpn
ip route add default via ${ISP_GW} table novpn
where ISP_GW
is the gateway you use to route traffic to your VPN server. Some users may want to use dev ppp0
(or ppp1
, ..., pppN
) instead of via ${ISP_GW}
in case if they use pptp to connect to the internet.
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
To anybody wishing to set up something similar. Check your/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in/etc/network/if-up.d/
to be set upon boot.
– Twifty
Mar 18 '17 at 18:47
add a comment |
I'd recomend you to avoid managing routing based on domain names (by the way, it is also impossible to resolve wildcard subdomain, whether it is bonus points for it or not :D)
To be a bit descriptive, you shouldn't do that because:
1) some domains changes their IPs time to time,
2) it is impossible to match wildcards in subdomains
3) it is impossible to know/fetch all subdomains of any domain
4) any random subdomain can have any random IP address.
So, the solution as browser addon (and/or custom local proxy like squid) is the best option for your issue.
But, I guess, "FoxyProxy" addon (it is originally Firefox addon, but AFAIRC, it is alfo Chrome version exists) is exactly what you want.
And, also, answering to your notice that "FoxyProxy is paid service and you already have your vpn":
FoxyProxyPlus is paid service, but not FoxyProxy.
FoxyProxy is addon, available for major browsers:
Standard Edition (Firefox) | Basic Edition (Firefox)
Standard Edition (Chrom{e,ium}) | Basic Edition (Chrom{e,ium})
So, if you want to go to some domains through VPN, you should:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) and/or write the rules list for squid
3) capture http/https traffic not owned by squid with iptables and point it to squid by rule like this:
iptables -m owner -m multiport -t nat -A OUTPUT ! -o lo ! --uid-owner $squid_user_id -p tcp --dports 80,443,8080,... -j REDIRECT --to-ports $SQUID_PORT
(--syn
option may be needed for -p tcp)
4) capture http/https traffic owned by squid, and mark it for next routing it to VPN with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
5)
echo 11 forcevpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table forcevpn
ip route add default via 10.0.0.1 table forcevpn
where 10.0.0.1
is you gateway inside VPN. Or you can use dev $VPN_IF
instead of via 10.0.0.1
if you have no gateway and just want to push all the traffic in the vpn interface.
6) optionally, you may be need to run sudo sysctl ipv4.conf.all.rp_filter
=0
===
And one more thing:
If you want to do same magick with non-http(s) TCP traffic, you'll be need something like proxychains, and perform similar capturing magic.
And, if you want to do that magic with UDP, I've a bad news: I don't know any proxy capable of proxying of UDP (because of the nature of this protocol) :)
⇓⇓⇓ EDIT ⇓⇓⇓
In case, you want the reverse thing (default gw = vpn, and rule some domains directly through ISP), it can be:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) capture traffic owned by squid, and mark it for next routing it another way with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
3)
echo 11 novpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table novpn
ip route add default via ${ISP_GW} table novpn
where ISP_GW
is the gateway you use to route traffic to your VPN server. Some users may want to use dev ppp0
(or ppp1
, ..., pppN
) instead of via ${ISP_GW}
in case if they use pptp to connect to the internet.
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
To anybody wishing to set up something similar. Check your/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in/etc/network/if-up.d/
to be set upon boot.
– Twifty
Mar 18 '17 at 18:47
add a comment |
I'd recomend you to avoid managing routing based on domain names (by the way, it is also impossible to resolve wildcard subdomain, whether it is bonus points for it or not :D)
To be a bit descriptive, you shouldn't do that because:
1) some domains changes their IPs time to time,
2) it is impossible to match wildcards in subdomains
3) it is impossible to know/fetch all subdomains of any domain
4) any random subdomain can have any random IP address.
So, the solution as browser addon (and/or custom local proxy like squid) is the best option for your issue.
But, I guess, "FoxyProxy" addon (it is originally Firefox addon, but AFAIRC, it is alfo Chrome version exists) is exactly what you want.
And, also, answering to your notice that "FoxyProxy is paid service and you already have your vpn":
FoxyProxyPlus is paid service, but not FoxyProxy.
FoxyProxy is addon, available for major browsers:
Standard Edition (Firefox) | Basic Edition (Firefox)
Standard Edition (Chrom{e,ium}) | Basic Edition (Chrom{e,ium})
So, if you want to go to some domains through VPN, you should:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) and/or write the rules list for squid
3) capture http/https traffic not owned by squid with iptables and point it to squid by rule like this:
iptables -m owner -m multiport -t nat -A OUTPUT ! -o lo ! --uid-owner $squid_user_id -p tcp --dports 80,443,8080,... -j REDIRECT --to-ports $SQUID_PORT
(--syn
option may be needed for -p tcp)
4) capture http/https traffic owned by squid, and mark it for next routing it to VPN with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
5)
echo 11 forcevpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table forcevpn
ip route add default via 10.0.0.1 table forcevpn
where 10.0.0.1
is you gateway inside VPN. Or you can use dev $VPN_IF
instead of via 10.0.0.1
if you have no gateway and just want to push all the traffic in the vpn interface.
6) optionally, you may be need to run sudo sysctl ipv4.conf.all.rp_filter
=0
===
And one more thing:
If you want to do same magick with non-http(s) TCP traffic, you'll be need something like proxychains, and perform similar capturing magic.
And, if you want to do that magic with UDP, I've a bad news: I don't know any proxy capable of proxying of UDP (because of the nature of this protocol) :)
⇓⇓⇓ EDIT ⇓⇓⇓
In case, you want the reverse thing (default gw = vpn, and rule some domains directly through ISP), it can be:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) capture traffic owned by squid, and mark it for next routing it another way with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
3)
echo 11 novpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table novpn
ip route add default via ${ISP_GW} table novpn
where ISP_GW
is the gateway you use to route traffic to your VPN server. Some users may want to use dev ppp0
(or ppp1
, ..., pppN
) instead of via ${ISP_GW}
in case if they use pptp to connect to the internet.
I'd recomend you to avoid managing routing based on domain names (by the way, it is also impossible to resolve wildcard subdomain, whether it is bonus points for it or not :D)
To be a bit descriptive, you shouldn't do that because:
1) some domains changes their IPs time to time,
2) it is impossible to match wildcards in subdomains
3) it is impossible to know/fetch all subdomains of any domain
4) any random subdomain can have any random IP address.
So, the solution as browser addon (and/or custom local proxy like squid) is the best option for your issue.
But, I guess, "FoxyProxy" addon (it is originally Firefox addon, but AFAIRC, it is alfo Chrome version exists) is exactly what you want.
And, also, answering to your notice that "FoxyProxy is paid service and you already have your vpn":
FoxyProxyPlus is paid service, but not FoxyProxy.
FoxyProxy is addon, available for major browsers:
Standard Edition (Firefox) | Basic Edition (Firefox)
Standard Edition (Chrom{e,ium}) | Basic Edition (Chrom{e,ium})
So, if you want to go to some domains through VPN, you should:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) and/or write the rules list for squid
3) capture http/https traffic not owned by squid with iptables and point it to squid by rule like this:
iptables -m owner -m multiport -t nat -A OUTPUT ! -o lo ! --uid-owner $squid_user_id -p tcp --dports 80,443,8080,... -j REDIRECT --to-ports $SQUID_PORT
(--syn
option may be needed for -p tcp)
4) capture http/https traffic owned by squid, and mark it for next routing it to VPN with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
5)
echo 11 forcevpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table forcevpn
ip route add default via 10.0.0.1 table forcevpn
where 10.0.0.1
is you gateway inside VPN. Or you can use dev $VPN_IF
instead of via 10.0.0.1
if you have no gateway and just want to push all the traffic in the vpn interface.
6) optionally, you may be need to run sudo sysctl ipv4.conf.all.rp_filter
=0
===
And one more thing:
If you want to do same magick with non-http(s) TCP traffic, you'll be need something like proxychains, and perform similar capturing magic.
And, if you want to do that magic with UDP, I've a bad news: I don't know any proxy capable of proxying of UDP (because of the nature of this protocol) :)
⇓⇓⇓ EDIT ⇓⇓⇓
In case, you want the reverse thing (default gw = vpn, and rule some domains directly through ISP), it can be:
1) write rules for foxyproxy to go through you squid instance for the list of domains
2) capture traffic owned by squid, and mark it for next routing it another way with rule like this:
iptables -A OUTPUT -m owner --uid-owner $squid_user_id -j MARK --set-mark 11
3)
echo 11 novpn >> /etc/iproute2/rt_tables
ip rule add fwmark 11 table novpn
ip route add default via ${ISP_GW} table novpn
where ISP_GW
is the gateway you use to route traffic to your VPN server. Some users may want to use dev ppp0
(or ppp1
, ..., pppN
) instead of via ${ISP_GW}
in case if they use pptp to connect to the internet.
edited Mar 18 '17 at 11:04
answered Mar 7 '17 at 21:43
Vadim A. Misbakh-SoloviovVadim A. Misbakh-Soloviov
815
815
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
To anybody wishing to set up something similar. Check your/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in/etc/network/if-up.d/
to be set upon boot.
– Twifty
Mar 18 '17 at 18:47
add a comment |
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
To anybody wishing to set up something similar. Check your/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in/etc/network/if-up.d/
to be set upon boot.
– Twifty
Mar 18 '17 at 18:47
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
Yes indeed, a higher level solution is the way to go. With some hacking one could probably create a proxy server that would connect to the Internet on specific interfaces based on the requested hostname.
– Daniel B
Mar 7 '17 at 21:49
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
FoxyProxy is a paid service, a VPN. I already have a system wide VPN in place. Though I do like the browser plugin method of doing what I want, this does not answer my original question.
– Twifty
Mar 11 '17 at 3:17
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@DanielB, A local proxy server may be what I am looking for, but I haven't come across any method of having a software selectively use eth0 vs tun0.
– Twifty
Mar 11 '17 at 3:20
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
@Twifty my answer do contain the answer on your original question :) Also, I edited it to add a bit descriptive explaination of the things.
– Vadim A. Misbakh-Soloviov
Mar 11 '17 at 7:56
To anybody wishing to set up something similar. Check your
/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in /etc/network/if-up.d/
to be set upon boot.– Twifty
Mar 18 '17 at 18:47
To anybody wishing to set up something similar. Check your
/etc/resolv.conf
for your default DNS IP and configure a rule for it. You can then configure these rules in /etc/network/if-up.d/
to be set upon boot.– Twifty
Mar 18 '17 at 18:47
add a comment |
Routing based on destination domain is not impossible, and, with the right tools, not all that hard.
I'll present a few methods that require little or no special client side configuration. These all assume you are using OpenVPN to connect. This should be achievable with other VPNs, but may require more manual configuration after the VPN is brought up.
For example purposes, I'll use the domains "example.com", "us1.example.com", "us2.example.com", and "geoblocked.com" for the domains we want to route through the non-VPN interface.
All commands should be run as root.
Method 1 - OpenVPN
I would only recommend this if you are certain the IP addresses you are routing have static IPs that never change.
Pros:
- Extremely simple
Cons:
- Only reliable for domains with IPs that never change
- Need an explicit entry for every domain and subdomain
Method:
Add the following lines to your OpenVPN configuration:
route example.com 255.255.255.255 net_gateway
route us1.example.com 255.255.255.255 net_gateway
route us2.example.com 255.255.255.255 net_gateway
route geoblocked.com 255.255.255.255 net_gateway
Restart OpenVPN.
That's it, but you'll have to restart VPN again if those IP addresses ever change.
NOTE: Some sources say you also need to specify allow-pull-fqdn
, but that did not seem to be the case in my experience. YMMV.
Method 2 - Policy based routing
Policy based routing is the ability to route based on certain criteria; commonly a source address or protocol, but in this case we will inspect the destination domain name prior to routing and use marked packets ("fwmark").
So what we need to do first is create a separate table for your VPN routed
packets, so that we can mark those that go through the VPN, and passing marked packets through the non-VPN interface. (Keep in mind, this is one approach and there are many other ways to approach this, such as letting the VPN do its routing as normal through the main table and creating a separate table for non-VPN traffic.)
Your kernel must be recent enough and have the proper modules, although modern systems probably have them in their default kernels.
The name "vpn_table" (the routing table name), and the numbers "201" (routing table ID) and "3" (fwmark) are arbitrarily chosen.
Create the new routing table (as root):
echo 201 vpn_table >> /etc/iproute2/rt_tables
Configure OpenVPN:
Create the following script somewhere (I am calling it "/etc/openvpn/client/setup-routing") and make it executable:
#!/bin/bash
ip route add 0.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
ip route add 128.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
sysctl -w net.ipv4.conf.$dev.rp_filter=2
# You can optionally leave the next two lines out but run the `ip rule add`
# command at each boot instead
ip rule del fwmark 3 table vpn_table &>/dev/null # This might fail but that's ok
ip rule add fwmark 3 table vpn_table
The variables in the above script will be populated as environment variables by OpenVPN. Also note this sets up routing to all addresses through the VPN gateway in the "vpn_table" routing table. If your VPN setup requires more complex routing, refer to the OpenVPN documentation and adjust accordingly.
Add the following to your OpenVPN configuration:
## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
The "route-noexec" line permits OpenVPN to fetch the route from the server, but prevents it from actually populating the routes. Instead the route-up script is called. "script-security 2" is necessary to call a user-defined script.
That is all the necessary set up to route the marked packets, but we need to set up a way to actually mark the packets. Two options are using dnsmasq with ipset, or setting up a squid proxy.
Method 2a - Policy based routing using ipset and dnsmasq
I would recommend this method if you are already running this on a dnsmasq-based router or your clients do not support proxy config. This is effectively the same as a caching DNS that updates the routing table whenever a domain name is looked up.
Pros:
- Handles subdomains
- Works on devices that can't access proxies (do those exist?)
Cons:
- Does not handle referrer field (see Method 2b)
- Needs complicated ipset and iptables configs
- Requires the VPN connected system to be set up as a router (needs dedicated
interface) - I have no idea how scalable ipset is (my use case is for a whole ccTLD)
This assumes you already have dnsmasq configured and set up, and acting as a gateway and DNS server for clients connected to a dedicated interface "eth1".
Create the ipset:
ipset create SKIP_VPN_IPSET iphash
Tell iptables to mark the ipset packets (n.b., this must be done after creating the ipset list):
# Mark ALL packets coming in on eth1 - change this to the interface dnsmasq listens on
iptables -A PREROUTING -i eth1 -t mangle -j MARK --set-mark 3
# REMOVE mark on any addresses that match our ipset
iptables -A PREROUTING -t mangle -m set --match-set SKIP_VPN_IPSET dst -j MARK --set-mark 0/3
NOTE: The above commands (ipset
and iptables
) must be run at each boot. Alternatively, your OS may provide some options for saving/restoring iptable rules and ipsets.
NOTE2: There is documented an inverse ! --match-set
but that resulted in all packets just disappearing when I tried it.
Add the following to your dnsmasq.conf:
ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
Obviously adjust that line too whichever domain names you want routed. This will also add ALL subdomains to the ipset, so you do not need to explicitly specify them. Even using a TLD will work.
Restart dnsmasq and set up your clients to use the VPN connected system as both a gateway and DNS (which should be implied if it is set up as a DHCP server).
Method 2b - Policy based routing using squid
This is my favorite method and works well with my PS4 and other devices I use to connect.
Pros:
- Handles subdomains
- Handles referrer field
- Does not require replacing your existing router
- Clients (browsers) can optionally use it or not
Cons:
- Clients must support proxy connection
This assumes you have a working squid setup and a basic knowledge of squid configuration.
Add the following lines to squid.conf:
# redirect example domains
acl domain_to_remote_proxy dstdomain .example.com
acl ref_to_remote_proxy referer_regex [^.]*.example.com.*
# redirect geoblocked domain
acl domain_to_remote_proxy dstdomain .geoblocked.com
acl ref_to_remote_proxy referer_regex [^.]*.geoblocked.com.*
# mark packets that we want routed through the VPN
tcp_outgoing_mark 0x03 !ref_to_remote_proxy !domain_to_remote_proxy
Note there are 2 lines per domain, and subdomains are matched. The first line checks the destination domain, and the second matches the "Referer" header. This is useful because browsers send the referer when fetching content on a webpage such as images, CSS, or javascript; this means that content requested by the site will also route through the non-VPN address even if it is hosted on a different domain (e.g., example-cdn.com).
On the clients, set up the connection like normal but set the proxy settings to use the proxy server and port for this system. Most devices (including game consoles) allow system-wide configuration. On PCs, most browsers can be configured to use a proxy independently of system settigs.
Final note - My use case is actually to route specific domains through the VPN and everything else through the non-VPN. The methods are similar to the above, but inverted.
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
add a comment |
Routing based on destination domain is not impossible, and, with the right tools, not all that hard.
I'll present a few methods that require little or no special client side configuration. These all assume you are using OpenVPN to connect. This should be achievable with other VPNs, but may require more manual configuration after the VPN is brought up.
For example purposes, I'll use the domains "example.com", "us1.example.com", "us2.example.com", and "geoblocked.com" for the domains we want to route through the non-VPN interface.
All commands should be run as root.
Method 1 - OpenVPN
I would only recommend this if you are certain the IP addresses you are routing have static IPs that never change.
Pros:
- Extremely simple
Cons:
- Only reliable for domains with IPs that never change
- Need an explicit entry for every domain and subdomain
Method:
Add the following lines to your OpenVPN configuration:
route example.com 255.255.255.255 net_gateway
route us1.example.com 255.255.255.255 net_gateway
route us2.example.com 255.255.255.255 net_gateway
route geoblocked.com 255.255.255.255 net_gateway
Restart OpenVPN.
That's it, but you'll have to restart VPN again if those IP addresses ever change.
NOTE: Some sources say you also need to specify allow-pull-fqdn
, but that did not seem to be the case in my experience. YMMV.
Method 2 - Policy based routing
Policy based routing is the ability to route based on certain criteria; commonly a source address or protocol, but in this case we will inspect the destination domain name prior to routing and use marked packets ("fwmark").
So what we need to do first is create a separate table for your VPN routed
packets, so that we can mark those that go through the VPN, and passing marked packets through the non-VPN interface. (Keep in mind, this is one approach and there are many other ways to approach this, such as letting the VPN do its routing as normal through the main table and creating a separate table for non-VPN traffic.)
Your kernel must be recent enough and have the proper modules, although modern systems probably have them in their default kernels.
The name "vpn_table" (the routing table name), and the numbers "201" (routing table ID) and "3" (fwmark) are arbitrarily chosen.
Create the new routing table (as root):
echo 201 vpn_table >> /etc/iproute2/rt_tables
Configure OpenVPN:
Create the following script somewhere (I am calling it "/etc/openvpn/client/setup-routing") and make it executable:
#!/bin/bash
ip route add 0.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
ip route add 128.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
sysctl -w net.ipv4.conf.$dev.rp_filter=2
# You can optionally leave the next two lines out but run the `ip rule add`
# command at each boot instead
ip rule del fwmark 3 table vpn_table &>/dev/null # This might fail but that's ok
ip rule add fwmark 3 table vpn_table
The variables in the above script will be populated as environment variables by OpenVPN. Also note this sets up routing to all addresses through the VPN gateway in the "vpn_table" routing table. If your VPN setup requires more complex routing, refer to the OpenVPN documentation and adjust accordingly.
Add the following to your OpenVPN configuration:
## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
The "route-noexec" line permits OpenVPN to fetch the route from the server, but prevents it from actually populating the routes. Instead the route-up script is called. "script-security 2" is necessary to call a user-defined script.
That is all the necessary set up to route the marked packets, but we need to set up a way to actually mark the packets. Two options are using dnsmasq with ipset, or setting up a squid proxy.
Method 2a - Policy based routing using ipset and dnsmasq
I would recommend this method if you are already running this on a dnsmasq-based router or your clients do not support proxy config. This is effectively the same as a caching DNS that updates the routing table whenever a domain name is looked up.
Pros:
- Handles subdomains
- Works on devices that can't access proxies (do those exist?)
Cons:
- Does not handle referrer field (see Method 2b)
- Needs complicated ipset and iptables configs
- Requires the VPN connected system to be set up as a router (needs dedicated
interface) - I have no idea how scalable ipset is (my use case is for a whole ccTLD)
This assumes you already have dnsmasq configured and set up, and acting as a gateway and DNS server for clients connected to a dedicated interface "eth1".
Create the ipset:
ipset create SKIP_VPN_IPSET iphash
Tell iptables to mark the ipset packets (n.b., this must be done after creating the ipset list):
# Mark ALL packets coming in on eth1 - change this to the interface dnsmasq listens on
iptables -A PREROUTING -i eth1 -t mangle -j MARK --set-mark 3
# REMOVE mark on any addresses that match our ipset
iptables -A PREROUTING -t mangle -m set --match-set SKIP_VPN_IPSET dst -j MARK --set-mark 0/3
NOTE: The above commands (ipset
and iptables
) must be run at each boot. Alternatively, your OS may provide some options for saving/restoring iptable rules and ipsets.
NOTE2: There is documented an inverse ! --match-set
but that resulted in all packets just disappearing when I tried it.
Add the following to your dnsmasq.conf:
ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
Obviously adjust that line too whichever domain names you want routed. This will also add ALL subdomains to the ipset, so you do not need to explicitly specify them. Even using a TLD will work.
Restart dnsmasq and set up your clients to use the VPN connected system as both a gateway and DNS (which should be implied if it is set up as a DHCP server).
Method 2b - Policy based routing using squid
This is my favorite method and works well with my PS4 and other devices I use to connect.
Pros:
- Handles subdomains
- Handles referrer field
- Does not require replacing your existing router
- Clients (browsers) can optionally use it or not
Cons:
- Clients must support proxy connection
This assumes you have a working squid setup and a basic knowledge of squid configuration.
Add the following lines to squid.conf:
# redirect example domains
acl domain_to_remote_proxy dstdomain .example.com
acl ref_to_remote_proxy referer_regex [^.]*.example.com.*
# redirect geoblocked domain
acl domain_to_remote_proxy dstdomain .geoblocked.com
acl ref_to_remote_proxy referer_regex [^.]*.geoblocked.com.*
# mark packets that we want routed through the VPN
tcp_outgoing_mark 0x03 !ref_to_remote_proxy !domain_to_remote_proxy
Note there are 2 lines per domain, and subdomains are matched. The first line checks the destination domain, and the second matches the "Referer" header. This is useful because browsers send the referer when fetching content on a webpage such as images, CSS, or javascript; this means that content requested by the site will also route through the non-VPN address even if it is hosted on a different domain (e.g., example-cdn.com).
On the clients, set up the connection like normal but set the proxy settings to use the proxy server and port for this system. Most devices (including game consoles) allow system-wide configuration. On PCs, most browsers can be configured to use a proxy independently of system settigs.
Final note - My use case is actually to route specific domains through the VPN and everything else through the non-VPN. The methods are similar to the above, but inverted.
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
add a comment |
Routing based on destination domain is not impossible, and, with the right tools, not all that hard.
I'll present a few methods that require little or no special client side configuration. These all assume you are using OpenVPN to connect. This should be achievable with other VPNs, but may require more manual configuration after the VPN is brought up.
For example purposes, I'll use the domains "example.com", "us1.example.com", "us2.example.com", and "geoblocked.com" for the domains we want to route through the non-VPN interface.
All commands should be run as root.
Method 1 - OpenVPN
I would only recommend this if you are certain the IP addresses you are routing have static IPs that never change.
Pros:
- Extremely simple
Cons:
- Only reliable for domains with IPs that never change
- Need an explicit entry for every domain and subdomain
Method:
Add the following lines to your OpenVPN configuration:
route example.com 255.255.255.255 net_gateway
route us1.example.com 255.255.255.255 net_gateway
route us2.example.com 255.255.255.255 net_gateway
route geoblocked.com 255.255.255.255 net_gateway
Restart OpenVPN.
That's it, but you'll have to restart VPN again if those IP addresses ever change.
NOTE: Some sources say you also need to specify allow-pull-fqdn
, but that did not seem to be the case in my experience. YMMV.
Method 2 - Policy based routing
Policy based routing is the ability to route based on certain criteria; commonly a source address or protocol, but in this case we will inspect the destination domain name prior to routing and use marked packets ("fwmark").
So what we need to do first is create a separate table for your VPN routed
packets, so that we can mark those that go through the VPN, and passing marked packets through the non-VPN interface. (Keep in mind, this is one approach and there are many other ways to approach this, such as letting the VPN do its routing as normal through the main table and creating a separate table for non-VPN traffic.)
Your kernel must be recent enough and have the proper modules, although modern systems probably have them in their default kernels.
The name "vpn_table" (the routing table name), and the numbers "201" (routing table ID) and "3" (fwmark) are arbitrarily chosen.
Create the new routing table (as root):
echo 201 vpn_table >> /etc/iproute2/rt_tables
Configure OpenVPN:
Create the following script somewhere (I am calling it "/etc/openvpn/client/setup-routing") and make it executable:
#!/bin/bash
ip route add 0.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
ip route add 128.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
sysctl -w net.ipv4.conf.$dev.rp_filter=2
# You can optionally leave the next two lines out but run the `ip rule add`
# command at each boot instead
ip rule del fwmark 3 table vpn_table &>/dev/null # This might fail but that's ok
ip rule add fwmark 3 table vpn_table
The variables in the above script will be populated as environment variables by OpenVPN. Also note this sets up routing to all addresses through the VPN gateway in the "vpn_table" routing table. If your VPN setup requires more complex routing, refer to the OpenVPN documentation and adjust accordingly.
Add the following to your OpenVPN configuration:
## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
The "route-noexec" line permits OpenVPN to fetch the route from the server, but prevents it from actually populating the routes. Instead the route-up script is called. "script-security 2" is necessary to call a user-defined script.
That is all the necessary set up to route the marked packets, but we need to set up a way to actually mark the packets. Two options are using dnsmasq with ipset, or setting up a squid proxy.
Method 2a - Policy based routing using ipset and dnsmasq
I would recommend this method if you are already running this on a dnsmasq-based router or your clients do not support proxy config. This is effectively the same as a caching DNS that updates the routing table whenever a domain name is looked up.
Pros:
- Handles subdomains
- Works on devices that can't access proxies (do those exist?)
Cons:
- Does not handle referrer field (see Method 2b)
- Needs complicated ipset and iptables configs
- Requires the VPN connected system to be set up as a router (needs dedicated
interface) - I have no idea how scalable ipset is (my use case is for a whole ccTLD)
This assumes you already have dnsmasq configured and set up, and acting as a gateway and DNS server for clients connected to a dedicated interface "eth1".
Create the ipset:
ipset create SKIP_VPN_IPSET iphash
Tell iptables to mark the ipset packets (n.b., this must be done after creating the ipset list):
# Mark ALL packets coming in on eth1 - change this to the interface dnsmasq listens on
iptables -A PREROUTING -i eth1 -t mangle -j MARK --set-mark 3
# REMOVE mark on any addresses that match our ipset
iptables -A PREROUTING -t mangle -m set --match-set SKIP_VPN_IPSET dst -j MARK --set-mark 0/3
NOTE: The above commands (ipset
and iptables
) must be run at each boot. Alternatively, your OS may provide some options for saving/restoring iptable rules and ipsets.
NOTE2: There is documented an inverse ! --match-set
but that resulted in all packets just disappearing when I tried it.
Add the following to your dnsmasq.conf:
ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
Obviously adjust that line too whichever domain names you want routed. This will also add ALL subdomains to the ipset, so you do not need to explicitly specify them. Even using a TLD will work.
Restart dnsmasq and set up your clients to use the VPN connected system as both a gateway and DNS (which should be implied if it is set up as a DHCP server).
Method 2b - Policy based routing using squid
This is my favorite method and works well with my PS4 and other devices I use to connect.
Pros:
- Handles subdomains
- Handles referrer field
- Does not require replacing your existing router
- Clients (browsers) can optionally use it or not
Cons:
- Clients must support proxy connection
This assumes you have a working squid setup and a basic knowledge of squid configuration.
Add the following lines to squid.conf:
# redirect example domains
acl domain_to_remote_proxy dstdomain .example.com
acl ref_to_remote_proxy referer_regex [^.]*.example.com.*
# redirect geoblocked domain
acl domain_to_remote_proxy dstdomain .geoblocked.com
acl ref_to_remote_proxy referer_regex [^.]*.geoblocked.com.*
# mark packets that we want routed through the VPN
tcp_outgoing_mark 0x03 !ref_to_remote_proxy !domain_to_remote_proxy
Note there are 2 lines per domain, and subdomains are matched. The first line checks the destination domain, and the second matches the "Referer" header. This is useful because browsers send the referer when fetching content on a webpage such as images, CSS, or javascript; this means that content requested by the site will also route through the non-VPN address even if it is hosted on a different domain (e.g., example-cdn.com).
On the clients, set up the connection like normal but set the proxy settings to use the proxy server and port for this system. Most devices (including game consoles) allow system-wide configuration. On PCs, most browsers can be configured to use a proxy independently of system settigs.
Final note - My use case is actually to route specific domains through the VPN and everything else through the non-VPN. The methods are similar to the above, but inverted.
Routing based on destination domain is not impossible, and, with the right tools, not all that hard.
I'll present a few methods that require little or no special client side configuration. These all assume you are using OpenVPN to connect. This should be achievable with other VPNs, but may require more manual configuration after the VPN is brought up.
For example purposes, I'll use the domains "example.com", "us1.example.com", "us2.example.com", and "geoblocked.com" for the domains we want to route through the non-VPN interface.
All commands should be run as root.
Method 1 - OpenVPN
I would only recommend this if you are certain the IP addresses you are routing have static IPs that never change.
Pros:
- Extremely simple
Cons:
- Only reliable for domains with IPs that never change
- Need an explicit entry for every domain and subdomain
Method:
Add the following lines to your OpenVPN configuration:
route example.com 255.255.255.255 net_gateway
route us1.example.com 255.255.255.255 net_gateway
route us2.example.com 255.255.255.255 net_gateway
route geoblocked.com 255.255.255.255 net_gateway
Restart OpenVPN.
That's it, but you'll have to restart VPN again if those IP addresses ever change.
NOTE: Some sources say you also need to specify allow-pull-fqdn
, but that did not seem to be the case in my experience. YMMV.
Method 2 - Policy based routing
Policy based routing is the ability to route based on certain criteria; commonly a source address or protocol, but in this case we will inspect the destination domain name prior to routing and use marked packets ("fwmark").
So what we need to do first is create a separate table for your VPN routed
packets, so that we can mark those that go through the VPN, and passing marked packets through the non-VPN interface. (Keep in mind, this is one approach and there are many other ways to approach this, such as letting the VPN do its routing as normal through the main table and creating a separate table for non-VPN traffic.)
Your kernel must be recent enough and have the proper modules, although modern systems probably have them in their default kernels.
The name "vpn_table" (the routing table name), and the numbers "201" (routing table ID) and "3" (fwmark) are arbitrarily chosen.
Create the new routing table (as root):
echo 201 vpn_table >> /etc/iproute2/rt_tables
Configure OpenVPN:
Create the following script somewhere (I am calling it "/etc/openvpn/client/setup-routing") and make it executable:
#!/bin/bash
ip route add 0.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
ip route add 128.0.0.0/1 via $route_vpn_gateway dev $dev scope global table vpn_table
sysctl -w net.ipv4.conf.$dev.rp_filter=2
# You can optionally leave the next two lines out but run the `ip rule add`
# command at each boot instead
ip rule del fwmark 3 table vpn_table &>/dev/null # This might fail but that's ok
ip rule add fwmark 3 table vpn_table
The variables in the above script will be populated as environment variables by OpenVPN. Also note this sets up routing to all addresses through the VPN gateway in the "vpn_table" routing table. If your VPN setup requires more complex routing, refer to the OpenVPN documentation and adjust accordingly.
Add the following to your OpenVPN configuration:
## Policy routing
route-noexec
script-security 2
route-up /etc/openvpn/client/setup-routing
The "route-noexec" line permits OpenVPN to fetch the route from the server, but prevents it from actually populating the routes. Instead the route-up script is called. "script-security 2" is necessary to call a user-defined script.
That is all the necessary set up to route the marked packets, but we need to set up a way to actually mark the packets. Two options are using dnsmasq with ipset, or setting up a squid proxy.
Method 2a - Policy based routing using ipset and dnsmasq
I would recommend this method if you are already running this on a dnsmasq-based router or your clients do not support proxy config. This is effectively the same as a caching DNS that updates the routing table whenever a domain name is looked up.
Pros:
- Handles subdomains
- Works on devices that can't access proxies (do those exist?)
Cons:
- Does not handle referrer field (see Method 2b)
- Needs complicated ipset and iptables configs
- Requires the VPN connected system to be set up as a router (needs dedicated
interface) - I have no idea how scalable ipset is (my use case is for a whole ccTLD)
This assumes you already have dnsmasq configured and set up, and acting as a gateway and DNS server for clients connected to a dedicated interface "eth1".
Create the ipset:
ipset create SKIP_VPN_IPSET iphash
Tell iptables to mark the ipset packets (n.b., this must be done after creating the ipset list):
# Mark ALL packets coming in on eth1 - change this to the interface dnsmasq listens on
iptables -A PREROUTING -i eth1 -t mangle -j MARK --set-mark 3
# REMOVE mark on any addresses that match our ipset
iptables -A PREROUTING -t mangle -m set --match-set SKIP_VPN_IPSET dst -j MARK --set-mark 0/3
NOTE: The above commands (ipset
and iptables
) must be run at each boot. Alternatively, your OS may provide some options for saving/restoring iptable rules and ipsets.
NOTE2: There is documented an inverse ! --match-set
but that resulted in all packets just disappearing when I tried it.
Add the following to your dnsmasq.conf:
ipset=/example.com/geoblocked.com/SKIP_VPN_IPSET
Obviously adjust that line too whichever domain names you want routed. This will also add ALL subdomains to the ipset, so you do not need to explicitly specify them. Even using a TLD will work.
Restart dnsmasq and set up your clients to use the VPN connected system as both a gateway and DNS (which should be implied if it is set up as a DHCP server).
Method 2b - Policy based routing using squid
This is my favorite method and works well with my PS4 and other devices I use to connect.
Pros:
- Handles subdomains
- Handles referrer field
- Does not require replacing your existing router
- Clients (browsers) can optionally use it or not
Cons:
- Clients must support proxy connection
This assumes you have a working squid setup and a basic knowledge of squid configuration.
Add the following lines to squid.conf:
# redirect example domains
acl domain_to_remote_proxy dstdomain .example.com
acl ref_to_remote_proxy referer_regex [^.]*.example.com.*
# redirect geoblocked domain
acl domain_to_remote_proxy dstdomain .geoblocked.com
acl ref_to_remote_proxy referer_regex [^.]*.geoblocked.com.*
# mark packets that we want routed through the VPN
tcp_outgoing_mark 0x03 !ref_to_remote_proxy !domain_to_remote_proxy
Note there are 2 lines per domain, and subdomains are matched. The first line checks the destination domain, and the second matches the "Referer" header. This is useful because browsers send the referer when fetching content on a webpage such as images, CSS, or javascript; this means that content requested by the site will also route through the non-VPN address even if it is hosted on a different domain (e.g., example-cdn.com).
On the clients, set up the connection like normal but set the proxy settings to use the proxy server and port for this system. Most devices (including game consoles) allow system-wide configuration. On PCs, most browsers can be configured to use a proxy independently of system settigs.
Final note - My use case is actually to route specific domains through the VPN and everything else through the non-VPN. The methods are similar to the above, but inverted.
answered May 17 '17 at 15:02
PossumPossum
18114
18114
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
add a comment |
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
i added a how to for privoxy ;) to do the same thing it's a lighter solution
– intika
Jan 17 at 8:22
add a comment |
Squid does not support socks (like ssh tunnel)... there is an option to build squid with socks support, but it's hard to get it to work.
Privoxy can do the job
- Support parent socks
- Support http/https proxy
- Support referrer
- etc.
Privoxy Setup:
Install privoxy
Edit the config file (remove everything on
/etc/privoxy
and add/etc/privoxy/config
)
user-manual /usr/share/doc/privoxy/webserver/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile default.action
filterfile default.filter
logfile logfile
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 0
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forwarded-connect-retries 0
accept-intercepted-requests 0
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
tolerate-pipelining 1
socket-timeout 300
listen-address 127.0.0.1:8888
forward-socks5 .whatismyipaddress.com 127.0.0.1:8080 .
forward-socks5 .whatismyip.com 127.0.0.1:8080 .
Restart the service
systemctl start privoxy
Setup the privoxy proxy on the client application
If you want to route referrer as well add default.action and default.filter you can test that with http://www.play-hookey.com/htmltest/ with the html code
<a href="http://amibehindaproxy.com/">test-ip</a></br>
<a href="http://www.stardrifter.org/cgi-bin/ref.cgi">test-referrer</a>
default.action and default.filter
default.action
{+client-header-tagger{referer}}
/
{+forward-override{forward-socks5 127.0.0.1:8080 .}}
TAG:.*?hookey.com
default.filter
CLIENT-HEADER-TAGGER: referer
s@^Referer:.*?$@$0@i
Restart privoxy service
add a comment |
Squid does not support socks (like ssh tunnel)... there is an option to build squid with socks support, but it's hard to get it to work.
Privoxy can do the job
- Support parent socks
- Support http/https proxy
- Support referrer
- etc.
Privoxy Setup:
Install privoxy
Edit the config file (remove everything on
/etc/privoxy
and add/etc/privoxy/config
)
user-manual /usr/share/doc/privoxy/webserver/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile default.action
filterfile default.filter
logfile logfile
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 0
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forwarded-connect-retries 0
accept-intercepted-requests 0
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
tolerate-pipelining 1
socket-timeout 300
listen-address 127.0.0.1:8888
forward-socks5 .whatismyipaddress.com 127.0.0.1:8080 .
forward-socks5 .whatismyip.com 127.0.0.1:8080 .
Restart the service
systemctl start privoxy
Setup the privoxy proxy on the client application
If you want to route referrer as well add default.action and default.filter you can test that with http://www.play-hookey.com/htmltest/ with the html code
<a href="http://amibehindaproxy.com/">test-ip</a></br>
<a href="http://www.stardrifter.org/cgi-bin/ref.cgi">test-referrer</a>
default.action and default.filter
default.action
{+client-header-tagger{referer}}
/
{+forward-override{forward-socks5 127.0.0.1:8080 .}}
TAG:.*?hookey.com
default.filter
CLIENT-HEADER-TAGGER: referer
s@^Referer:.*?$@$0@i
Restart privoxy service
add a comment |
Squid does not support socks (like ssh tunnel)... there is an option to build squid with socks support, but it's hard to get it to work.
Privoxy can do the job
- Support parent socks
- Support http/https proxy
- Support referrer
- etc.
Privoxy Setup:
Install privoxy
Edit the config file (remove everything on
/etc/privoxy
and add/etc/privoxy/config
)
user-manual /usr/share/doc/privoxy/webserver/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile default.action
filterfile default.filter
logfile logfile
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 0
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forwarded-connect-retries 0
accept-intercepted-requests 0
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
tolerate-pipelining 1
socket-timeout 300
listen-address 127.0.0.1:8888
forward-socks5 .whatismyipaddress.com 127.0.0.1:8080 .
forward-socks5 .whatismyip.com 127.0.0.1:8080 .
Restart the service
systemctl start privoxy
Setup the privoxy proxy on the client application
If you want to route referrer as well add default.action and default.filter you can test that with http://www.play-hookey.com/htmltest/ with the html code
<a href="http://amibehindaproxy.com/">test-ip</a></br>
<a href="http://www.stardrifter.org/cgi-bin/ref.cgi">test-referrer</a>
default.action and default.filter
default.action
{+client-header-tagger{referer}}
/
{+forward-override{forward-socks5 127.0.0.1:8080 .}}
TAG:.*?hookey.com
default.filter
CLIENT-HEADER-TAGGER: referer
s@^Referer:.*?$@$0@i
Restart privoxy service
Squid does not support socks (like ssh tunnel)... there is an option to build squid with socks support, but it's hard to get it to work.
Privoxy can do the job
- Support parent socks
- Support http/https proxy
- Support referrer
- etc.
Privoxy Setup:
Install privoxy
Edit the config file (remove everything on
/etc/privoxy
and add/etc/privoxy/config
)
user-manual /usr/share/doc/privoxy/webserver/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile default.action
filterfile default.filter
logfile logfile
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 0
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forwarded-connect-retries 0
accept-intercepted-requests 0
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
tolerate-pipelining 1
socket-timeout 300
listen-address 127.0.0.1:8888
forward-socks5 .whatismyipaddress.com 127.0.0.1:8080 .
forward-socks5 .whatismyip.com 127.0.0.1:8080 .
Restart the service
systemctl start privoxy
Setup the privoxy proxy on the client application
If you want to route referrer as well add default.action and default.filter you can test that with http://www.play-hookey.com/htmltest/ with the html code
<a href="http://amibehindaproxy.com/">test-ip</a></br>
<a href="http://www.stardrifter.org/cgi-bin/ref.cgi">test-referrer</a>
default.action and default.filter
default.action
{+client-header-tagger{referer}}
/
{+forward-override{forward-socks5 127.0.0.1:8080 .}}
TAG:.*?hookey.com
default.filter
CLIENT-HEADER-TAGGER: referer
s@^Referer:.*?$@$0@i
Restart privoxy service
edited Jan 17 at 8:30
answered Jan 17 at 1:53
intikaintika
744316
744316
add a comment |
add a comment |
Thanks for contributing an answer to Super User!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f1185861%2flinux-routing-based-on-domain-names%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
The only way I see is to modify an existing DNS caching server like pdns to add routes as soon as hosts are looked up. I don't think you can solve that with scripting, and you'll also need a way to prevent the routing table from getting too full. Not an easy problem.
– dirkt
Mar 7 '17 at 6:56