So in your configuration, all the packets you try to send to the network initially originating from 10.0.0.1
(because they are going through tun0
interface and its local address is 10.0.0.1
). You capture the packets, everything is fine so far.
Now, tun0
sends the packets further. Source address is 10.0.0.1
and you want the packets to leave through a different interface (wlp2s0
in your case). That's routing so let's enable routing first:
sysctl -w net.ipv4.ip_forward=1
After that, if you'll look at tcpdump
for wlp2s0
you can notice the packets leave with source address 10.0.0.1
and not with the source address of the wlan interface (what you would expect I guess). So we need to change the source address and it's called source NAT. In linux it's easy with help of netfilter/iptables:
iptables -t nat -A POSTROUTING -o wlp2s0 -s 10.0.0.1 -j MASQUERADE
Please also check that your FORWARD
chain has ACCEPT
policy or you would need to allow forwarding with something like:
iptables -A FORWARD -i tun0 -o wlp2s0 -s 10.0.0.1 -j ACCEPT
iptables -A FORWARD -i wlp2s0 -o tun0 -d 10.0.0.1 -j ACCEPT
Everything should work now: linux kernel does the routing, it's moving packets from tun0
interface to wlp2s0
. netfilter should change the source IP 10.0.0.1
to your wlp2s0
interface assigned address for output packets. It memorizes all the connections and when the reply packets go back (if they) it changes the destination address of the wlp2s0
interface assigned address to 10.0.0.1
(the "conntrack" feature).
Well, it should but it doesn't. It seems, netfilter gets confused with this complicated routing configuration and the fact that the same packet first goes through the OUTPUT
chain and then being routed and comes to PREROUTING
chain. At least on by Debian 8 box it doesn't work.
The best way to troubleshoot netfilter is the TRACE
feature:
modprobe ipt_LOG
iptables -t raw -A OUTPUT -p icmp -j TRACE
iptables -t raw -A PREROUTING -p icmp -j TRACE
I only enable tracing for ICMP packets, you may use other filter to debug.
It will show what tables and chains the packet goes through. And I can see that the packet goes no further the FORWARD
chain (and it's not being caught by the nat/POSTROUTING
chain that actually does SNAT
).
Below are several approaches to make this work.
APPROACH #1
The best way to un-confuse netfilter is to change the source IP address of packets in tun0.c
application. It's also the most natural way.
We need to change 10.0.0.1 to 10.0.0.2 on the way outwards and 10.0.0.2 to 10.0.0.1 on the way back.
I've modified tun0.c
with source address change code. Here is the new file and here is patchfile for your tun0.c
. Changes to IP header also involve checksum correction, so I took some code from OpenVPN project.
Here is the full list of commands I execute after a clean reboot and tun0_changeip.c
launch:
ifconfig tun0 inet 10.0.0.1/30 up
sysctl -w net.ipv4.ip_forward=1
ip route add default dev tun0 table John
ip rule add from all lookup John
ip rule add from 10.0.0.2 lookup main priority 500
iptables -t nat -A POSTROUTING -o wlp2s0 -s 10.0.0.2 -j MASQUERADE
Please note that you don't need to turn off the reverse path filtering in that case, because everything is legal - tun0
only receives and sends packets that belong to its subnet. Also you can do a source-based routing instead of interface-based.
APPROACH #2
It's possible to do SNAT
before the packet reach tun0
interface. It's not very correct though. You will definitely need to turn off the reverse path filtering in this case:
sysctl -w net.ipv4.conf.tun0.rp_filter=0
# It won't work without also changing the "all" value
sysctl -w net.ipv4.conf.all.rp_filter=0
Now, do SNAT
:
iptables -t nat -A POSTROUTING -o tun0 -s 10.0.0.1 -j SNAT --to-source ip.address.of.your.wlan.interface
Here we change the source address just before the packets reach the tun0
device. tun0.c
code resend these packets "as is" (with changed source address) and they are successfully routed through wlan interface.
But you might have a dynamic IP on wlan interface and want to use MASQUERADE
(in order to not specify the interface address explicitly). Here is how you can make use of MASQUERADE
:
iptables -t nat -A POSTROUTING -o tun0 -s 10.0.0.1 -j SNAT --to-source 10.0.55.1
iptables -t nat -A POSTROUTING -o wlp2s0 -s 10.0.55.1 -j MASQUERADE
Please note the "10.0.55.1
" IP address - it's different. You can use any IP here, it doesn't matter. The packets reach nat/POSTROUTING
chain on wlp2s0
interface if we change the source IP before. And now it's not dependent on a static IP for wlan interface.
APPROACH #3
You can also use fwmark
. That way you don't need SNAT
but you'll capture only outgoing packets:
First we need to disable reverse path filtering for tun0
because it will forward packets that belong to another network:
sysctl -w net.ipv4.conf.tun0.rp_filter=0
# It won't work without also changing the "all" value
sysctl -w net.ipv4.conf.all.rp_filter=0
Now let's alter the routing rules a bit:
# Delete old rules
ip rule del iif tun0 lookup main
ip rule del from all lookup John
# Packets will start going from wlan interface so they will have source address of it
iptables -t mangle -A OUTPUT -o wlp2s0 -j MARK --set-mark 1
ip rule add fwmark 0x1 lookup John
That's another "hack" for routing and netfilter that works on my Debian 8 box, but still I recommend to take the first approach as it's more natural and doesn't use any hacks.
You may also consider to build your application as a transparent proxy. I think it would be much easier instead of analyzing packets from tun device.