909-744-2891

Vyos Policy Based Routing

What is Policy Based Routing?

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.

Vyos

We add some stock Vyos commands to setup the policy based routing. Two interfaces and their BGP configuration are omitted here to simplify this, and to concentrate on the policy based issues.

The bulk-data port-group defines the TCP ports that we want to route over the DSL link. The source-route policy selects the packets to be routed via table 1. That policy is applied to the local ethernet interface, not the outgoing DSL link. The static protocol section defines the content of routing table 1, which forces those packets out thru the DSL link, rather than via the default BGP configured links.

firewall {
    group {
        port-group bulk-data {
            port 22
            port 143
            port 443
            port 8000
            port 80
        }
    }
}

policy {
    route source-route {
        rule 10 {
            destination {
                group {
                    port-group bulk-data
                }
            }
            protocol tcp
            set {
                table 1
            }
            source {
                address 0.0.0.0/0
            }
        }
    }
}

interfaces {
    ethernet eth0 {
        description "local network"
        policy {
            route source-route
        }
    }
    ethernet eth1 {
        address dhcp
        description "dsl link"
    }
}

protocols {
    static {
        table 1 {
            interface-route 0.0.0.0/0 {
                next-hop-interface eth1 {
                }
            }
        }
    }
}

This all works as advertised, with the small exception of DHCP renewals. There are times when the DSL link may die (as in no traffic), but the Vyos router won't notice that until it tries to do a DHCP renewal many hours later. So we monitor the next hop by periodic pings, and when the link state changes, we force a 'renew dhcp interface eth1' command. If that renewal attempt fails, the default route in table 1 is removed, even though the Vyos configuration thinks it is still there. When the link comes back up, and the DHCP renewal succeeds, we need to reinstate that default route in table 1.

We also need to ensure that traffic from the vyos itself is properly natted out over that dhcp configured eth1. This is required to allow outside systems (IPv6 tunnelbroker.net) to ping the local IPv4 endpoint.

The following script in /etc/dhcp3/dhclient-exit-hooks.d/bgp handles those issues.

#!/bin/bash

(
if [ -z "$reason" ]; then
    reason="NONE"
fi

case $reason in
    BOUND|RENEW|REBIND|REBOOT|NONE|EXPIRE|FAIL|RELEASE|STOP)

    # check if dhcp to verizon is up
    #
    dev=eth1
    table=1
    cmd="show ip route 0.0.0.0/0
         exit
        "
    gwip=$( (echo "$cmd" | ssh -t -t vyos@localhost 2>/dev/null) | grep "via $dev" | rev | awk '{print $3}' | rev | cut -d, -f1 )
    if [ "$gwip" != "" ]; then
        # link is up, replace the mangle table
        ip=$(ip addr list $dev | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
        iptables -t mangle -F OUTPUT
        # vyos source eth1 natted out eth1
        iptables -t mangle -A OUTPUT -s $ip/32 -j MARK --set-mark 0x80000000
        iptables -n -t mangle -L OUTPUT

        # link is up, check if table $table contains default route
        echo "dhcp $dev link is up at $(date)"
        cmd="ip route list table $table
             exit
             exit
        "
        ok=$( (echo "$cmd" | ssh -t -t vyos@localhost 2>/dev/null) | grep "default dev $dev")
        if [ "$ok" == "" ]; then
            # table $table does not contain the default route
            echo "route table $table has no default route at $(date)"
            cmd="configure
                 delete protocols static table $table
                 commit
                 set protocols static table $table interface-route 0.0.0.0/0 next-hop-interface $dev
                 commit
                 save
                 exit
                 exit
                "
            echo "$cmd" | ssh -t -t vyos@localhost 2>/dev/null
            echo "remove/re-add protocols static table $table interface-route 0.0.0.0/0 next-hop-interface $dev $(date)"
        fi
    fi
    ;;

    PREINIT)
    ;;
esac

) </dev/null >>/tmp/xxx 2>&1