Normal ip packet routing only looks at the destination ip (v4 or v6) address to decide which outbound interface should send that packet. Policy based routing allows that interface selection process to use information other than the destination ip address. For example, you might want all TCP packets with destination port number 80 to exit via a particular interface.
In this particular case, we have two interfaces running a full BGP (IPv4) feed from a provider, and we also have a higher-speed consumer grade DSL line configured via DHCP. As long as the DSL link is up, we can use it for web browsing. If the link is down, we want to automatically fall back to the main data links.
We add a second routing table, add a default route in that routing table thru our DSL link, use the firewall to add firewall marks to some packets, and add an "ip rule" to select that second routing table for those marked packets.
Since the DSL link is configured via DHCP, we can place a bash script in /etc/dhcp3/dhclient-exit-hooks.d/bgp which will be run whenever the dhcp state of that link changes. If the DSL link is up, it will cause some packets to get sent via the DSL link. If the link is down, the firewall marking will be removed, and those packets will take the normal outbound route via one of the BGP configured links.
Note that this use of the PREROUTING chain in the mangle table conflicts with Vyatta's use for the wan load balancing feature.
#!/bin/bash
if [ -z "$reason" ]; then
reason="NONE"
fi
case $reason in
BOUND|RENEW|REBIND|REBOOT|NONE|EXPIRE|FAIL|RELEASE|STOP)
fn=/etc/iproute2/rt_tables
ta=510sg
prior=20
mark=20 # for routing table selection
localdev=eth0
dev=eth1
# create our second routing table if it does not exist
grep $ta $fn >/dev/null || echo "$prior $ta" >>$fn
# flush all the custom entries
iptables -t mangle -F PREROUTING
iptables -t mangle -F OUTPUT
ip route flush table $ta
# do we have a default route? It must have come from dhcp on $dev
# if we have a default route that link is up.
cmd="show ip route 0.0.0.0/0; exit
"
gwip=$( (echo "$cmd" | ssh -t -t vyatta@localhost 2>/dev/null) | grep "via $dev" | rev | awk '{print $3}' | rev | cut -d, -f1 )
if [ "$gwip" != "" ]; then
# find our ip address on $dev
ip=$(ip addr list $dev | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
# find our local network on $localdev
localnet=$(ip addr list $localdev | grep 'inet ' | awk '{print $2}')
# local destinations that should not be routed out $dev
iptables -t mangle -A PREROUTING -d 192.168.0.0/16 -j ACCEPT
# anything from the local network to some fixed ports gets forced out $dev
iptables -t mangle -A PREROUTING -p tcp --dport 143 -s $localnet -j MARK --set-mark $mark
iptables -t mangle -A PREROUTING -p tcp --dport 80 -s $localnet -j MARK --set-mark $mark
iptables -t mangle -A PREROUTING -p tcp --dport 443 -s $localnet -j MARK --set-mark $mark
iptables -t mangle -A PREROUTING -p tcp --dport 8000 -s $localnet -j MARK --set-mark $mark
# vyatta source $dev natted out $dev
iptables -t mangle -A OUTPUT -s $ip/32 -j MARK --set-mark $mark
# add a default route in our second routing table
ip route add default via $gwip dev $dev table $ta
# setup routing based on the firewall mark
ip rule list | grep "lookup $ta" >/dev/null || ip rule add fwmark $mark priority $prior table $ta
fi
ip route flush cache
d=$(date)
echo ""
echo "$d $reason is reason for running this bgp script"
echo ""
echo "$d rule list"
ip rule list
echo ""
echo "$d mangle table"
iptables -n -t mangle -L PREROUTING
iptables -n -t mangle -L OUTPUT
;;
PREINIT)
;;
esac