An IP packet has no data checksum or any other footer after the data section. Typically the link layer encapsulates IP packets in frames with a CRC footer that detects most errors, and typically the end-to-end TCP layer checksum detects most other errors. The IPv4 packet header consists of 14 fields, of which 13 are required. IP Packet Structure. All IP packets are structured the same way - an IP header followed by a variable-length data field. A summary of the contents of the internet header follows: Version: 4 bits The Version field indicates the format of the internet header. This document describes version 4.
My home server has two main interfaces,
eth1
(a standard internet connection) and tun0
(an OpenVPN tunnel). I'd like to use iptables
to force all packets generated by a local process owned by UID 1002 to exit through tun0
, and all other packets to exit through eth1
.I can easily mark matched packets:
Now, I'd like to put some rule in the POSTROUTING chain (probably of the mangle table) to match packets marked with 11 and send them to
tun0
, followed by a rule that matches all packets and send them to eth1
.I found the ROUTE target, but that seems to only re-write the source interface (unless I'm reading it incorrectly).
Is iptables capable of this? Do I have to mess around with the routing table (via
ip route
or just the legacy route
commands) instead?Edit: I thought that maybe I should provide more information. I have no other iptables rules at present (although I may create some rules to carry out unrelated tasks in the future). Also, the output of
ip route
is:I haven't touched the routing table - this is just how it is at present (although it looks fairly dirty). I'm sorry, but I don't have the legacy
route
command installed on this machine.And the output of
ip addr
(of course, eth0 and eth2 can be ignored - they're NICs that aren't being used at present):Edit: I've gotten something sorta working, but it's not forwarding marked packets to tun0. Basically, I added a table (11), and used:
When I just
sudo -u user1000 wget -qO- whatismyip.org
, I get my home's external IP address, but if I do sudo -u user1002 wget -qO- whatismyip.org
, I also get my home's IP address (but I should be getting the IP at the other end of the OpenVPN tunnel).Running
iptables -vL
confirms that the packets are getting matched by the marking rule, but they don't appear to be following the routing rule.EDIT: I've spent a long time on this, and although it still doesn't work, I think I'm a bit closer.
The iptables rule has to be in the
mangle
table's OUTPUT chain. I think I also need a MASQUERADE rule in the nat
table's POSTROUTING chain, to set the source address. However, the re-routing that occurs after OUTPUT's mangle is not working correctly.I've spent 5 hours on this now, so I'm taking a break and will probably return to it later tonight or sometime tomorrow.
Ethan
EthanEthan
4 Answers
I've recently hit a similar issue, albeit a slightly different. I wanted to route only TCP port 25 (SMTP) over an OpenVPN tap0 interface, while routing all other traffic (even for the same host) over the default interface.
To do so, I had to mark packets and set up rules for handling it. First, add a rule that make the kernel route packets marked with
2
through table 3
(explained later):You could have added a symbolic name to
/etc/iproute2/rt_tables
, but I did not bother to do so. The number 2
and 3
are randomly chosen. In fact, these can be the same but for clarity I did not do it in this example (although I do it in my own setup).Add a route for redirecting traffic over a different interface, assuming the gateway being
10.0.0.1
:Very important! Flush your routing cache, otherwise you will not get a response back and sit with your hands in your hair for some hours:
Now, set a firewall rule for marking designated packets:
The above rule applies only if the packets come from the local machine. See http://inai.de/images/nf-packet-flow.png. Adjust it to your requirements. For instance, if you only want to route outgoing HTTP traffic over the
tap0
interface, change 465 to 80.To prevent the packets sent over
tap0
getting your LAN address as source IP, use the following rule to change it to your interface address (assuming 10.0.0.2
as IP address for interface tap0
):Finally, relax the reverse path source validation. Some suggest you to set it to
0
, but 2
seems a better choice according to https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt. If you skip this, you will receive packets (this can be confirmed using tcpdump -i tap0 -n
), but packets do not get accepted. The command to change the setting so packets get accepted:LekensteynLekensteyn
I solved this. The issue was with the routing rules in table 11. Table 11 was getting hit, but the routing rules make it inoperable. This script is what I now use, and it seems to work well (although it's obviously specific to my setup). Also, I created a new table 21 devoted to the main uplink (eth1).
## MeanderingCode edit (because I can't comment, yet)
Thanks for this answer! It seems as though this could get messy, as you would have to maintain route info here (possibly duplicating, or breaking other things which may want to set routes.
You may be experiencing 'weird things' in your routing table from OpenVPN because the server is configured to 'push' routes, enabling all traffic to route through the VPN network interface, rather than the 'bare' internet. Or your OpenVPN config or whatever script/application sets it up is setting routes.
In the former case, you can edit your OpenVPN configuration and put in a line containing 'route-nopull'
In the latter, check configuration for OpenVPN or any wrapper (network-manager-openvpn, for example on many current linux desktop distros)
In either case, eliminating the routing configuration where it's getting set is cleaner and safer than flushing the table, depending on when you run this script and what else your system is doing.
In the latter, check configuration for OpenVPN or any wrapper (network-manager-openvpn, for example on many current linux desktop distros)
In either case, eliminating the routing configuration where it's getting set is cleaner and safer than flushing the table, depending on when you run this script and what else your system is doing.
Cheers!
EthanEthan
I think you want:
But I haven't tested the above.
mfarvermfarver
This can be done without iptables command Execute simple ip command
For uid 1002:
Add default route for table 502 over interface that you want say
jainendrajainendra
Not the answer you're looking for? Browse other questions tagged iptablesrouting or ask your own question.
My home server has two main interfaces,
eth1
(a standard internet connection) and tun0
(an OpenVPN tunnel). I'd like to use iptables
to force all packets generated by a local process owned by UID 1002 to exit through tun0
, and all other packets to exit through eth1
.I can easily mark matched packets:
Now, I'd like to put some rule in the POSTROUTING chain (probably of the mangle table) to match packets marked with 11 and send them to
tun0
, followed by a rule that matches all packets and send them to eth1
.I found the ROUTE target, but that seems to only re-write the source interface (unless I'm reading it incorrectly).
Is iptables capable of this? Do I have to mess around with the routing table (via
ip route
or just the legacy route
commands) instead?Edit: I thought that maybe I should provide more information. I have no other iptables rules at present (although I may create some rules to carry out unrelated tasks in the future). Also, the output of
ip route
is:I haven't touched the routing table - this is just how it is at present (although it looks fairly dirty). I'm sorry, but I don't have the legacy
route
command installed on this machine.And the output of
ip addr
(of course, eth0 and eth2 can be ignored - they're NICs that aren't being used at present):Edit: I've gotten something sorta working, but it's not forwarding marked packets to tun0. Basically, I added a table (11), and used:
When I just
sudo -u user1000 wget -qO- whatismyip.org
, I get my home's external IP address, but if I do sudo -u user1002 wget -qO- whatismyip.org
, I also get my home's IP address (but I should be getting the IP at the other end of the OpenVPN tunnel).Running
iptables -vL
confirms that the packets are getting matched by the marking rule, but they don't appear to be following the routing rule.EDIT: I've spent a long time on this, and although it still doesn't work, I think I'm a bit closer.
The iptables rule has to be in the
mangle
table's OUTPUT chain. I think I also need a MASQUERADE rule in the nat
table's POSTROUTING chain, to set the source address. However, the re-routing that occurs after OUTPUT's mangle is not working correctly.I've spent 5 hours on this now, so I'm taking a break and will probably return to it later tonight or sometime tomorrow.
Ethan
EthanEthan
4 Answers
I've recently hit a similar issue, albeit a slightly different. I wanted to route only TCP port 25 (SMTP) over an OpenVPN tap0 interface, while routing all other traffic (even for the same host) over the default interface.
To do so, I had to mark packets and set up rules for handling it. First, add a rule that make the kernel route packets marked with
2
through table 3
(explained later):You could have added a symbolic name to
/etc/iproute2/rt_tables
, but I did not bother to do so. The number 2
and 3
are randomly chosen. In fact, these can be the same but for clarity I did not do it in this example (although I do it in my own setup).Add a route for redirecting traffic over a different interface, assuming the gateway being
10.0.0.1
:Very important! Flush your routing cache, otherwise you will not get a response back and sit with your hands in your hair for some hours:
Now, set a firewall rule for marking designated packets:
The above rule applies only if the packets come from the local machine. See http://inai.de/images/nf-packet-flow.png. Adjust it to your requirements. For instance, if you only want to route outgoing HTTP traffic over the
tap0
interface, change 465 to 80.To prevent the packets sent over
tap0
getting your LAN address as source IP, use the following rule to change it to your interface address (assuming 10.0.0.2
as IP address for interface tap0
):Finally, relax the reverse path source validation. Some suggest you to set it to
0
, but 2
seems a better choice according to https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt. If you skip this, you will receive packets (this can be confirmed using tcpdump -i tap0 -n
), but packets do not get accepted. The command to change the setting so packets get accepted:LekensteynLekensteyn
I solved this. The issue was with the routing rules in table 11. Table 11 was getting hit, but the routing rules make it inoperable. This script is what I now use, and it seems to work well (although it's obviously specific to my setup). Also, I created a new table 21 devoted to the main uplink (eth1).
## MeanderingCode edit (because I can't comment, yet)
Thanks for this answer! It seems as though this could get messy, as you would have to maintain route info here (possibly duplicating, or breaking other things which may want to set routes.
You may be experiencing 'weird things' in your routing table from OpenVPN because the server is configured to 'push' routes, enabling all traffic to route through the VPN network interface, rather than the 'bare' internet. Or your OpenVPN config or whatever script/application sets it up is setting routes.
In the former case, you can edit your OpenVPN configuration and put in a line containing 'route-nopull'
In the latter, check configuration for OpenVPN or any wrapper (network-manager-openvpn, for example on many current linux desktop distros)
In either case, eliminating the routing configuration where it's getting set is cleaner and safer than flushing the table, depending on when you run this script and what else your system is doing.
In the latter, check configuration for OpenVPN or any wrapper (network-manager-openvpn, for example on many current linux desktop distros)
In either case, eliminating the routing configuration where it's getting set is cleaner and safer than flushing the table, depending on when you run this script and what else your system is doing.
Cheers!
EthanEthan
I think you want:
But I haven't tested the above.
mfarvermfarver
This can be done without iptables command Execute simple ip command
For uid 1002:
Add default route for table 502 over interface that you want say
jainendrajainendra