Suppose that there is a problem to redirect only packets destined to remote port Y of some internet server and only those packets owned by user with ID 100Z. We should properly utilize both MANGLE and NAT tables for redirecting specific ports opened by this user because rules from MANGLE table applies before NAT and before rerouting (e.g. "ip route" rules) procedure in contrast with rules from NAT table which applies after rerouting. In an example below NAT rules used only for sending packets and MANGE for sending and receiving for convenience.
There is could be following local configuration of interfaces: "tun0" interface for VPN with local machine address 10.???.???.??? and peer (gateway) 10.???.???.(???+1) and real hardware interface eth0 with local machine address 192.168.XXX.XXX and gateway 192.168.XXX.1.
We can now apply following rules to split-redirect traffic for desired user.
Part 1. Output of locally-generated (outbound connection) packages.
- Mark outgoing locally generated packages with connection-specific mark for future rerouting with different rt_table.
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p tcp --dport Y -m owner --uid-owner 100Z -j CONNMARK --set-mark 2
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p udp --dport Y -m owner --uid-owner 100Z -j CONNMARK --set-mark 2
- Translate connection-specific markers to package-specific markers.
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p tcp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j MARK --set-mark 2
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p udp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j MARK --set-mark 2
- Save marker for further restoring it for all tracked connections. This can be implemented in NAT POSTROUTING table only - it's just a precaution to not loose markers after connection traverse the routing table.
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p tcp -m owner --uid-owner 100Z -m tcp --dport Y -m connmark ! --mark 0 -j CONNMARK --save-mark
/sbin/iptables -t mangle -A OUTPUT -s 192.168.XXX.XXX -p udp -m owner --uid-owner 100Z -m udp --dport Y -m connmark ! --mark 0 -j CONNMARK --save-mark
Normally connection-specific markers should be saved after packages rerouted and reached NAT POSTROUTING table
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p tcp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j CONNMARK --save-mark
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p udp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j CONNMARK --save-mark
- Replace source with proper VPN-related local address for tun0 interface.
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p tcp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j SNAT --to-source 10.???.???.???
/sbin/iptables -t nat -A POSTROUTING -s 192.168.XXX.XXX -o tun0 -p udp --dport Y -m owner --uid-owner 100Z -m connmark --mark 2 -j SNAT --to-source 10.???.???.???
Part 2. Input of connections related to locally-generated (outbound connection) packages.
- Restore connection-related markers for input packets before they go to the routing table.
/sbin/iptables -t mangle -A PREROUTING -i tun0 -p tcp -j CONNMARK --restore-mark
/sbin/iptables -t mangle -A PREROUTING -i tun0 -p udp -j CONNMARK --restore-mark
Part 3. Optional optimization
Other than above rules there is a strong desire in optimizing marking process to prevent or minimize redundant and repeated checks with marking. The most general approach for this task is to distinguish rules between NEW and RELATED,ESTABLISHED connection and use below template for optimal remarking already marked connections:
* Restore old markers (you probably won't do this for NEW connections because they are obviously wasn't marked earlier):
/sbin/iptables -A OUTPUT -t mangle -j CONNMARK --restore-mark
- Skip already marked connections:
/sbin/iptables -A OUTPUT -t mangle -m mark ! --mark 0 -j ACCEPT
- Mark new (usually marking only NEW connection should be enough) connections:
/sbin/iptables -A OUTPUT -m mark --mark 0 -p tcp --dport 21 -t mangle -j MARK --set-mark 1
/sbin/iptables -A OUTPUT -m mark --mark 0 -p tcp --dport 80 -t mangle -j MARK --set-mark 2
- Mark other packages with a "dummy" marker (that is not used anywhere else at all):
/sbin/iptables -A OUTPUT -m mark --mark 0 -t mangle -p tcp -j MARK --set-mark 3
/sbin/iptables -A OUTPUT -t mangle -j CONNMARK --save-mark
NOTE: the above settings are not a mandatory part of netfilter configuration for split tunnel - it is just a template (i.e. proper suggestion) for the way to minimize redundant filter checks. It also can be POSTROUTING instead of OUTPUT, but only one place is recommended for markings, do not need to implement it twice.
Part 4. Create table aliases in rt_tables.
echo 2 vpn >/etc/iproute2/rt_tables
echo 3 novpn >/etc/iproute2/rt_tables
Part 5. Configuring routing tables for VPN connections.
This is a standard configuration for VPN that allows to redirect all local sources (0.0.0.0/1 and 128.0.0.0/1 subnets include the whole IP range for local addresses) and also provides backward compatibility for returning packages (i.e. the "default" route).
- Table with VPN connection
ip route flush table vpn
ip route add 10.???.???.(???+1) dev tun0 src 10.???.???.??? table vpn
ip route add 0.0.0.0/1 dev tun0 via 10.???.???.(???+1) table vpn
ip route add 128.0.0.0/1 dev tun0 via 10.???.???.(???+1) table vpn
ip route add 192.168.0.0/16 src 192.168.XXX.XXX dev eth0 table vpn
ip route add default via 192.168.XXX.1 dev eth0 table vpn
- The other table called "novpn". It's goal to provide direct routing bypassing VPN connection.
ip route flush table novpn
ip route add 192.168.0.0/16 src 192.168.XXX.XXX dev eth0 table novpn
ip route add default via 192.168.XXX.1 dev eth0 table novpn
Part 6. Configuring routing rules (it is better to follow rule order below)
ip rule add from all lookup novpn
ip rule add from all fwmark 2 lookup vpn
ip rule add from 10.XXX.XXX.XXX lookup vpn
Part 7. Final steps
Now it is time to flush main routing table (there is no need for it while split tunnel is working):
ip route flush table main
The last step in configuration is to enable IP forwarding and dynamic IP addresses for sockets (client application connections) in Linux kernel:
echo 1 >/proc/sys/net/ipv4/ip_forward
echo 1 >/proc/sys/net/ipv4/ip_dynaddr
Now we can launch OpenVPN client with "--route-noexec" and/or "--ifconfig-noexec" (whether you already know or still not receive push message from VPN server with your tun0 interface configuration) parameters. If there are troubles with tun0 addresses - it is better to not use "--ifconfig-noexec" and let OpenVPN client set tun0 addresses for you. After that you just need to delete some old rules from "ip route" and "/sbin/iptables" and replace them with similar ones containing proper tun0 addresses (refer to all lines with 10.XXX.XXX.XXX or 10.XXX.XXX.(XXX+1) from above).
openvpn --config ./<your_config>.ovpn --route-noexec --ifconfig-noexec --auth-nocache
Those are only rules for outbound connections.
The main difference between inbound and outbound configurations: inbound rules should be implemented mostly in "mangle" table, not "nat", and vice-verse for outbound as shown above. There is an exception - for output of both locally-generated (outbound) and answered (inbound connection) packages rules must be placed in "mangle" table due to netfilter architecture implementation (for some reason "nat" table will intercept packages only after the rerouting procedure - consult Wikipedia netfilter graph or netfilter official documentation).
Many corporate firewalls block ICMP
- which is the protocol is used by ping
utility.
Better solution for this is to try TCP
connection to google.com:80
.
Easiest way to check for basic internet connectivity in shell script is utility nc
(it should be easily available on most Linux systems):
nc -w 3 -z google.com 80
echo $?
This means check if port 80
on google.com
is open, and timeout after 3
seconds. If connection was successful, it will print 0
, and if it failed, it will print 1
.
If you want to check internet connectivity without checking DNS (which could be broken in itself), you can use Google's preferred DNS server 8.8.8.8
, but the only port that it has open is 53
(aka domain
):
nc -w 3 -z 8.8.8.8 53
echo $?
However, port 53
can be also blocked by your corporate firewall (not common, but possible). Ports 80
and 443
, on other hand, are almost never firewalled.
Best Answer
The
tap
is meant for bridged tunneling under OpenVPN - you're supposed to junction it into a bridge such asbr0
usingbrctl
.The idea is you can put
tap0
andeth0
, for example, into a bridgebr0
- then broadcast traffic traverses across this bridge. (Broadcast traffic coming in fromtap0
will be forwarded toeth0
and vice versa whereas in a routed, standard situation it would not.) Your OpenVPN tunnel viatap0
is then "switched" intoeth0
instead of "routed" into it. The entirebr0
gets an IP and you deal withbr0
instead ofeth0
ortap0
.Completely possible to have a bridge with only one interface and add/remove additional interfaces with
brctl
as needed.So either put
tap0
into a bridge and deal with the bridge interface instead, or usetun
interfaces.It's also possible
iptables
rules are interfering.Update - look here: http://backreference.org/2010/03/26/tuntap-interface-tutorial/ - particularly this excerpt:
So looks like if you don't send full ethernet frames to tap0 it won't work as your expect because of this above.