EdgeRouter - Policy-based routing (source address based)

Overview


Readers will learn the how to configure policy-based routing based on source address. 

This article is an example of how to configure PBR (Policy Based Routing) in EdgeOS to route based on a source address instead of destination.

In the diagram below, we have 2 Internet connections, ISP-1 and ISP-2:

  1. ISP-1 on eth0 192.0.2.0/24
  2. ISP-2 on eth1 203.0.113.0/24


On the LAN side we'll use vlans:

  1. eth2.100 192.168.0.0/24
  2. eth2.200 172.16.0.0/24


For this example we want traffic from vlan 100 to use ISP-1 and traffic from vlan 200 to use ISP-2. 

Network Diagram

 

Policy Based Routing

There are 3 parts to configuring PBR in EdgeOS:

  1. Define a new routing table with static routes for each destination.
  2. Define a firewall modify policy to select a different routing table.
  3. Apply the firewall modify policy to an interface.

Define New Routing Tables

For vlan 100, we will define table 1 with a default route to ISP-1 next-hop address (192.0.2.1).

ubnt@RTR# set protocols static table 1 route 0.0.0.0/0 next-hop 192.0.2.1
[edit]

For vlan 200, we will define table 2 with a default route to ISP-2 next-hop address (203.0.113.1).

ubnt@RTR# set protocols static table 2 route 0.0.0.0/0 next-hop 203.0.113.1
[edit]

Define Modify Policy

modify policy allows us to modify various items when the rule matches. So if the source address came from 192.168.0.0, then we want to use routing table 1:

ubnt@RTR# set firewall modify SOURCE_ROUTE rule 10 description 'traffic from eth2.100 to ISP1'
ubnt@RTR# set firewall modify SOURCE_ROUTE rule 10 source address 192.168.0.0/24
ubnt@RTR# set firewall modify SOURCE_ROUTE rule 10 modify table 1

Similarly we'll create a rule 20 for source address 172.16.0.0/24 to use table 2.

ubnt@RTR# set firewall modify SOURCE_ROUTE rule 20 description 'traffic from eth2.200 to ISP2'
ubnt@RTR# set firewall modify SOURCE_ROUTE rule 20 source address 172.16.0.0/24
ubnt@RTR# set firewall modify SOURCE_ROUTE rule 20 modify table 2

Apply Policy to Interface

When it comes to applying a policy to an interface(s), it needs to be done on the input interface before the routing lookup takes place.

ubnt@RTR# set interfaces ethernet eth2 vif 100 firewall in modify SOURCE_ROUTE
[edit]
ubnt@RTR# set interfaces ethernet eth2 vif 200 firewall in modify SOURCE_ROUTE
[edit]

Testing

This now should be enough to verify that hosts on the 2 different vlans are routed based on their source address.

Perform a traceroute from a host on 192.168.0.0/24 and we see the 2nd router is 192.0.2.1:

ubnt@H1:~$ traceroute google.com
Resolving Address: google.com
traceroute to google.com (74.125.224.110), 30 hops max, 60 byte packets
 1 192.168.0.1 (192.168.0.1) 0.448 ms 0.603 ms 0.704 ms
 2 192.0.2.1 (192.0.2.1) 1.354 ms 1.397 ms 1.444 ms
 <snip>

Perform a traceroute from a host on 172.168.0.0/24 and we see the 2nd router is 203.0.113.1:

ubnt@H2:~$ traceroute google.com
traceroute to google.com (74.125.224.110), 30 hops max, 38 byte packets
 1 172.16.0.1 (172.16.0.1) 0.331 ms 0.275 ms 0.264 ms
 2 203.0.113.1 (203.0.113.1) 0.545 ms 0.406 ms 0.357 ms
 <snip> 

Show Command

The following command is used to show the main routing table:

ubnt@RTR:~$ show ip route
 Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
 I - ISIS, B - BGP, > - selected route, * - FIB route
 C>* 127.0.0.0/8 is directly connected, lo
 C>* 192.0.2.0/24 is directly connected, eth0
 C>* 203.0.113.0/24 is directly connected, eth1
 C>* 172.16.0.0/24 is directly connected, eth2.200
 C>* 192.168.0.0/24 is directly connected, eth2.100


To see one of the additional routing tables:

ubnt@RTR:~$ show ip route table 1
 table 1:
 Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
 I - ISIS, B - BGP, > - selected route, * - FIB route
 S>* 0.0.0.0/0 [1/0] via 192.0.2.1, eth0
ubnt@RTR:~$ show ip route table 2
 table 2:
 Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
 I - ISIS, B - BGP, > - selected route, * - FIB route
 S>* 0.0.0.0/0 [1/0] via 203.0.113.1, eth1 

Fail Over

Given that we have 2 ISPs, it would be good that if 1 link failed, then all the traffic could use the good link. For example, if the link to ISP-2 is down, in the routing table you'll see the route is inactive.

ubnt@RTR:~$ show ip route table 2
 table 2:
 Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
 I - ISIS, B - BGP, > - selected route, * - FIB route
 S 0.0.0.0/0 [1/0] via 203.0.113.1 inactive

If there is no route in table 2, then it'll try the main table, but currently there is no default route in the main table. Use the following commands to add a default route to each ISP.

ubnt@RTR:~$ configure
[edit]
ubnt@RTR# set protocols static route 0.0.0.0/0 next-hop 192.0.2.1
[edit]
ubnt@RTR# set protocols static route 0.0.0.0/0 next-hop 203.0.113.1
[edit]
ubnt@RTR# commit
[edit]
ubnt@RTR# save; exit
Saving configuration to '/config/config.boot'...
Done
exit

Now if we look at route table #2, we see that it will use the other path instead:

ubnt@RTR:~$ show ip route table 2
 table 2:
 Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
 I - ISIS, B - BGP, > - selected route, * - FIB route
 S>* 0.0.0.0/0 [1/0] via 203.0.113.1 (recursive via 192.0.2.1)

A traceroute shows that it's now taking the alternate path:

ubnt@H2:~$ traceroute google.com
traceroute to google.com (74.125.224.137), 30 hops max, 38 byte packets
 1 172.16.0.1 (172.16.0.1) 0.327 ms 0.281 ms 0.255 ms
 2 192.0.2.1 (192.0.2.1) 0.551 ms 0.390 ms 0.357 ms
 <snip>

LAN to LAN Traffic

In the example above, the vlan won't be able to talk to each other since their routing tables only have a default route. That may be what you want, but if not, we can add a rule ahead of the PBR rules to use the main routing table if the destination network is one of the LAN networks.

First we'll create a firewall group with our LAN networks:

ubnt@RTR# set firewall group network-group LAN_NETS network 192.168.0.0/24
[edit]
ubnt@RTR# set firewall group network-group LAN_NETS network 172.16.0.0/24
[edit]

Then we'll create a rule 5, which uses the LAN_NETS group and selects the main routing table.

ubnt@RTR# set firewall modify SOURCE_ROUTE rule 5 description "LAN to LAN skip PBR"
[edit]
ubnt@RTR# set firewall modify SOURCE_ROUTE rule 5 destination group network-group LAN_NETS
[edit]
ubnt@RTR# set firewall modify SOURCE_ROUTE rule 5 modify table main
[edit]

After this, our SOURCE_ROUTE rules look like:

ubnt@RTR# show firewall modify SOURCE_ROUTE
enable-default-log
rule 5 {
   description "LAN to LAN skip PBR"
   destination {
       group {
           network-group LAN_NETS
       }
   }
   modify {
       table main
   }
}
rule 10 {
   action modify
   description "traffic from eth2.100 to ISP1"
   modify {
       table 1
   }
   source {
       address 192.168.0.0/24
   }
}
rule 20 {
   action modify
   description "traffic from eth2.200 to ISP2"
   modify {
       table 2
   }
   source {
       address 172.16.0.0/24
   }
}
[edit]


Note: The rules that use a modify table command are "terminal" in that once a match is found, it stops. So in the example above, if rule 5 is a match, then it won't look at rule 10 or 20. However, this can be a bad thing if you're also using the modify command to change other items (mark, dscp, tcp-mss). In those cases, those other modify rules should come before the PBR rules.

Example Configuration

ubnt@RTR:~$ cat /config/config.boot
firewall {
   group {
       network-group LAN_NETS {
           network 192.168.0.0/24
           network 172.16.0.0/24
           network 10.0.0.0/24
        }
   }
   modify SOURCE_ROUTE {
   enable-default-log
       rule 5 {
           action modify
           description "LAN to LAN skip PBR"
           destination {
               group {
                   network-group LAN_NETS
               }
           }
           modify {
               table main
           }
       }
       rule 10 {
           action modify
           description "traffic from eth2.100 to ISP1"
           modify {
               table 1
           }
           source {
               address 192.168.0.0/24
           }
       }
       rule 20 {
           action modify
           description "traffic from eth2.200 to ISP2"
           modify {
               table 2
           }
           source {
               address 172.16.0.0/24
           }
       }
   }
}
interfaces {
   ethernet eth0 {
       address 192.0.2.2/24
   }
   ethernet eth1 {
       address 203.0.113.2/24
   }
   ethernet eth2 {
       address 10.0.0.1/24
       firewall {
           in {
               modify SOURCE_ROUTE
           }
       }
       vif 100 {
           address 192.168.0.1/24
           firewall {
               in {
                   modify SOURCE_ROUTE
               }
           }
       }
       vif 200 {
           address 172.16.0.1/24
           firewall {
               in {
                   modify SOURCE_ROUTE
               }
           }
       }
   }
}
protocols {
   static {
       route 0.0.0.0/0 {
           next-hop 192.0.2.1 {
           }
           next-hop 203.0.113.1 {
           }
       }
       table 1 {
           route 0.0.0.0/0 {
               next-hop 192.0.2.1 {
               }
           }
       }  
       table 2 {
           route 0.0.0.0/0 {
               next-hop 203.0.113.1 {
               }
           }
       }
    }
}
service {
   dhcp-server {
   shared-network-name LAN {
       authoritative enable
       subnet 10.0.0/24 {
           default-router 10.0.0.1
           dns-server 10.0.0.1
           lease 86400
           start 10.0.0.10 {
               stop 10.0.0.100
           }
       }    
   }
   shared-network-name LAN1 {
       authoritative enable
       subnet 192.168.0.0/24 {
           default-router 192.168.0.1
           dns-server 192.168.0.1
           lease 86400
           start 192.168.0.10 {
               stop 192.168.0.100
           }
       }
   }
   shared-network-name LAN2 {
        authoritative enable
            subnet 172.16.0.0/24 {
                default-router 172.16.0.1
                dns-server 172.16.0.1
                lease 86400
                start 172.16.0.10 {
                   stop 172.16.0.100
                }
            }
        }
    }
   dns {
        forwarding {
            cache-size 150
            listen-on eth2
            listen-on eth2.100
            listen-on eth2.200
        }
   }
   gui {
       https-port 443
   }
   nat {
       rule 5000 {
           outbound-interface eth0
           type masquerade
       }
       rule 5010 {
           outbound-interface eth1
           type masquerade
       }
       rule 5020 {
           outbound-interface eth0
           type masquerade
       }
    }
 }
 ssh {
     port 22
     protocol-version v2
 }
 telnet {
     port 23
 }
}
system {
   host-name RTR
   login {
       user ubnt {
           authentication {
               encrypted-password $1$zKNoUbAo$gomzUbYvgyUMcD436Wo66.
           }
       level admin
    }
 }
 name-server 8.8.8.8
 ntp {
     server 0.ubnt.pool.ntp.org {
     }
     server 1.ubnt.pool.ntp.org {
     }
     server 2.ubnt.pool.ntp.org {
     }
     server 3.ubnt.pool.ntp.org {
     }
 }
 syslog {
     global {
         facility all {
             level notice
         }
         facility protocols {
             level debug
         }
     }
 }
 time-zone US/Pacific
}
/* Warning: Do not remove the following line. */
/* === vyatta-config-version: "firewall@4:quagga@2:config-management@1:dhcp-relay@1:dhcp-server@4:ipsec@3:nat@3:qos@1:system@4:ubnt-pptp@1:vrrp@1:webgui@1:webproxy@1:zone-policy@1" === */
/* Release version: v1.0.3dev.4530060.130124.0102 */

Related Articles


Powered by Zendesk