iptables - Route Traffic from Specific Interface to a Specific Gateway

By Tait Clarridge, Thu 29 October 2009, Category Linux

firewall, iptables, linux

Recently I set up Openfire as a corporate messaging service, the problem I ran into was that since our VPN server and Openfire server both have external access and are in the same address space, the VPN server would try to send the Openfire traffic out the external gateway but couldn't reach the server because of some ACLs that were set in place.

So what I did was take the traffic from the VPN tunnel that is going to the Openfire ports and route it through our internal network.

What you will need to do is create another route table and mark the desired packets with iptables.

First you should pick a table number to create, I picked table #300. I made sure that the table did not exist before creating it by doing the following:

[root@tclarrltp ~]# ip route show table 300

This shouldn't return any information, if it does you must select a new one as it already contains routing data and overwriting it will most likely cause some issues.

Now that we know that the table doesn't exist, we are going to clear it anyways to make sure that it is clean.

[root@tclarrltp ~]# ip route flush table 300

You may or may not get a message telling you that there was nothing to flush or it doesn't exist.

Next we have to pick a number to have iptables mark the packets with. I chose the number 85 because in one of my previous posts I had used 80 to do some custom routing.

To set an ip rule we need to figure out the hex value of the number above. The easiest way to do this is in a calculator that can display hex and decimal values. In GNOME you can select the calculator from Applications > Accessories > Calculator, then select View > Programming. To make the conversion, make sure that the "Dec" radio button is selected and type in the number you want (eg. 85). Now click the "Hex" radio button and it will give you the hex value. 85 becomes 55 so our rule will use 0x55 as the hex value. As I don't have a great understanding of hex, I am not sure if larger numbers would mean that the "0x" part preceding the hex value from the calculator would change.

Now we should check to make sure that no ip rule exists for our hex value.

[root@tclarrltp ~]# ip rule

This will show all the rules that are active, if there is an entry that has 0x55 in it, pick another number.

Next, flush the rule to make it clean for us to use.

[root@tclarrltp ~]# ip rule del fwmark 0x55

You will probably get a message that states "RTNETLINK answers: No such file or directory", this is a good thing and means that it really does not exist.

So now we are ready to start creating the iptables rules, I do these first but you can do them after creating the routing rules if you would like (those will follow this section).

Our Openfire installation uses a custom port, but I will show this with the default ports (tcp ports 5222 and 5223). This will work for any port you would like. Our OpenVPN setup uses tun0 as the tunnel adaptor so that is the interface we wish to forward packets from.

We are going to use the PREROUTING table as we need to mark the packets before they get routed.

[root@tclarrltp ~]# iptables -t mangle -A PREROUTING -j MARK --set-mark 85 -i tun0 -p tcp --dport 5222
[root@tclarrltp ~]# iptables -t mangle -A PREROUTING -j MARK --set-mark 85 -i tun0 -p tcp --dport 5223

As you can see, the --set-mark flag value is our number that we picked in a previous section. It correlates to the fwmark 0x55 rule that we will be creating (because they are equal values, just one is decimal and the other is hex).

The -i flag is the interface we want iptables to mark packets from.

The -p flag is for the protocol (tcp/udp) and the -dport flag is the destination port of the packets.

This basically means that any traffic coming through tun0 heading for tcp port 5222 or 5223 will get marked with the number 85.

So now we can set the routing rules to deal with the marked packets.

First we are going to set the default route for table 300 so all our marked packets will go through there. I am going to use 192.168.0.1 as the gateway.

[root@tclarrltp ~]# ip route add table 300 default via 192.168.0.1
# Now tell the rule which table to look up
[root@tclarrltp ~]# ip rule add fwmark 0x55 table 300

Remember that unless you make the rule persistent or have a script start on boot with these commands, the routing will not be there when you restart your machine.

A simple script would look like the following:

#!/bin/bash

ip route flush table 300
ip rule del fwmark 0x55
iptables -t mangle -A PREROUTING -j MARK --set-mark 85 -i tun0 -p tcp --dport 5222
iptables -t mangle -A PREROUTING -j MARK --set-mark 85 -i tun0 -p tcp --dport 5223
ip route add table 300 default via 192.168.0.1
ip rule add fwmark 0x55 table 300

If you have any questions, feel free to leave me a comment or send me an email.