Skip to content
Log in Sign up My account

1.6 Policy-Based Routing

Blueprint 1.6: Configure and verify policy-based routing

  • Route maps for PBR: match clauses (ACL, packet length) and set clauses (next hop, interface, default next hop)
  • Applying PBR to transit traffic (ip policy route-map) and to router-generated traffic (ip local policy route-map)
  • Tracking the next hop: set ip next-hop verify-availability with IP SLA and object tracking
  • Verifying and troubleshooting: show ip policy, show route-map, debug ip policy, traceroute

Why PBR exists

Normal IP routing looks at one thing: the destination address. The longest match in the routing table wins, whatever the source, application or packet size. Policy-based routing (PBR) lets the router make a forwarding decision on other fields before (or instead of) that lookup. Typical business reasons:

  • Send guest or bulk traffic out a cheap Internet link while corporate traffic uses the MPLS link.
  • Send voice from one subnet over a low-latency carrier.
  • Push traffic from a specific source through a firewall or WAN optimiser that is not in the normal routing path.
  • Drop traffic from a source entirely (set interface Null0) without touching the routing table.

PBR is local to the router that runs it. It does not change what the router advertises, so neighbours know nothing about it. That matters when troubleshooting: the routing tables on every router can look perfect while traffic takes an unexpected path, or the reverse.

graph TB
  P["Packet arrives on Gi0/0<br/>ip policy route-map WAN-PBR"] --> S{"Route-map sequence<br/>matches?"}
  S -->|"permit sequence matches"| SET["Apply set clauses<br/>in precedence order"]
  S -->|"deny sequence matches"| NR["Normal routing<br/>destination lookup"]
  S -->|"no sequence matches"| NR
  SET --> OK{"Chosen next hop or<br/>interface usable?"}
  OK -->|"yes"| PBR["Policy routed"]
  OK -->|"no"| NR

How a PBR route map is processed

PBR reuses the route-map structure from 1.2, but the meaning of permit and deny is different from route filtering.

Route-map result In redistribution or BGP filtering In PBR
Matches a permit sequence Route is allowed (and set applies) Packet is policy routed using the set clauses
Matches a deny sequence Route is filtered Packet is routed normally (not dropped)
Matches no sequence (implicit deny) Route is filtered Packet is routed normally (not dropped)

Inside a sequence, the ACL in match ip address only decides whether the packet matches. An ACL deny line means "this sequence does not match", so the router moves on to the next sequence. It does not drop the packet.

Match clauses

Clause Matches on
match ip address ACL Numbered or named standard/extended ACL: source, destination, protocol, ports, DSCP
match length MIN MAX Layer 3 packet length, for example small packets for interactive traffic
match ipv6 address ACL IPv6 ACL, for IPv6 PBR

If a sequence lists several ACLs on one match ip address line, a packet that matches any of them matches (logical OR). Separate match lines of different types are ANDed.

Set clauses and their precedence

Clause Uses the routing table first? Typical use
set ip next-hop A.B.C.D [A.B.C.D ...] No. Used before the RIB lookup. Force a path. The first listed next hop that is reachable is used.
set ip next-hop recursive A.B.C.D No, but the next hop itself may be remote; it is resolved through the RIB Steer to a router that is not directly connected
set ip next-hop verify-availability A.B.C.D SEQ track N No, but only while track object N is up Force a path with health checking
set interface TYPE NUM No Send out an interface, mostly point-to-point or Null0 to drop
set ip default next-hop A.B.C.D Yes. Used only if the RIB has no explicit route. Gateway of last resort for matched traffic only
set default interface TYPE NUM Yes. Used only if the RIB has no explicit route. Same, by interface

When several are configured in one sequence, IOS evaluates them in this order: set ip next-hop, then set interface, then set ip default next-hop, then set default interface. The first usable one wins. If none is usable, the packet is routed normally.

Exam trap

set ip default next-hop does not override a default route. A default route (0.0.0.0/0) in the routing table does not count as an explicit route, so the PBR default next hop is used for any destination that only matches 0.0.0.0/0. If the router has a specific route (for example a /16 learned from OSPF) for the destination, the PBR default next hop is ignored. A question that says "the router has a default route to ISP1, and traffic still goes to ISP1 despite PBR" usually has a specific route in the RIB.

What "reachable" means for set ip next-hop

A plain set ip next-hop address must be on a directly connected subnet with a resolved adjacency. If the interface toward that next hop goes down, the next hop is skipped and, if there is no other usable set clause, the packet falls back to normal routing. That fallback is by design, but it only reacts to the local interface going down. If a switch sits between the routers, or the failure is further upstream, the interface stays up and PBR keeps sending packets into a black hole.

The fix is verify-availability with object tracking:

ip sla 10
 icmp-echo 10.1.3.2 source-interface GigabitEthernet0/2
 frequency 5
ip sla schedule 10 life forever start-time now
!
track 10 ip sla 10 reachability
!
route-map GUEST-PBR permit 10
 match ip address GUEST-SRC
 set ip next-hop verify-availability 10.1.3.2 10 track 10

The 10 before track is a sequence number for this next hop. You can list several next hops, each with its own sequence and track object, and the lowest sequence whose track is up is used. When every tracked next hop is down, the packet is routed normally.

set ip next-hop verify-availability with no arguments is an older form that checks the next hop through CDP neighbour information. Because CDP is often disabled on WAN links, prefer the IP SLA form.

Exam trap

An IP SLA probe does nothing until it is scheduled. If show track reports the object as down while the next hop answers pings from the CLI, check for a missing ip sla schedule line. A track that is permanently down silently turns the PBR policy off, so all traffic follows the routing table.

Where to apply the policy

Command Where What it catches
ip policy route-map NAME Interface (or subinterface, SVI, tunnel) Packets received on that interface and forwarded through the router
ip local policy route-map NAME Global configuration Packets generated by the router itself (pings, traceroute, Telnet/SSH sessions it originates, syslog, SNMP traps)
ipv6 policy route-map NAME Interface IPv6 packets received on that interface

PBR is ingress only. There is no outbound ip policy. Apply it on the interface facing the users whose traffic you want to steer, never on the WAN interface the traffic leaves through.

Exam trap

Testing PBR with a ping from the router that runs PBR proves nothing. Router-generated packets do not arrive on an interface, so the interface policy never sees them. Either test from a host or downstream router behind the ingress interface, or configure ip local policy route-map if the router's own traffic must follow the policy. The reverse is also tested: ip local policy has no effect on transit traffic.

Configuration example

ip access-list extended GUEST-SRC
 permit ip 10.4.20.0 0.0.0.255 any
!
route-map GUEST-PBR permit 10
 match ip address GUEST-SRC
 set ip next-hop verify-availability 10.1.3.2 10 track 10
!
interface GigabitEthernet0/0
 description LAN toward R4
 ip policy route-map GUEST-PBR
ip access-list extended BLOCKED-SRC
 permit ip 10.4.99.0 0.0.0.255 any
!
route-map EDGE-PBR permit 10
 match ip address BLOCKED-SRC
 set interface Null0
!
interface GigabitEthernet0/0
 ip policy route-map EDGE-PBR
ip access-list extended MGMT-TRAFFIC
 permit udp host 10.255.255.1 any eq syslog
 permit udp host 10.255.255.1 any eq snmptrap
!
route-map LOCAL-PBR permit 10
 match ip address MGMT-TRAFFIC
 set ip next-hop 10.1.2.2
!
ip local policy route-map LOCAL-PBR
ipv6 access-list GUEST6
 permit ipv6 2001:db8:20::/64 any
!
route-map GUEST6-PBR permit 10
 match ipv6 address GUEST6
 set ipv6 next-hop 2001:db8:1:3::2
!
interface GigabitEthernet0/0
 ipv6 policy route-map GUEST6-PBR

Verification

Command What it proves
show ip policy Which interfaces have a policy and which route map. A missing line means the policy is not applied. Local policy shows as local.
show route-map NAME The match and set clauses as IOS parsed them, the track state, and Policy routing matches counters per sequence
show access-lists NAME ACL hit counters. Combine with the route-map counter to see whether packets match.
show track N and show ip sla statistics N Whether a tracked next hop is considered up
traceroute DEST source SRC from a downstream host or router The actual path, which is the final proof
debug ip policy Per-packet decision: matched and routed, or rejected and routed normally. Use with an ACL-filtered debug ip packet only in the lab.
R1# show ip policy
Interface      Route map
Gi0/0          GUEST-PBR

R1# show route-map GUEST-PBR
route-map GUEST-PBR, permit, sequence 10
  Match clauses:
    ip address (access-lists): GUEST-SRC
  Set clauses:
    ip next-hop verify-availability 10.1.3.2 10 track 10  [up]
  Policy routing matches: 215 packets, 21500 bytes

The [up] tag reflects the track object. The counter shows packets that matched the sequence and were policy routed. If the counter stays at zero while users send traffic, the packets are either arriving on another interface or not matching the ACL.

debug ip policy output looks like this (exact wording varies by release):

IP: s=10.4.20.1 (GigabitEthernet0/0), d=10.255.255.5, len 100, FIB policy match
IP: s=10.4.20.1 (GigabitEthernet0/0), d=10.255.255.5, g=10.1.3.2, len 100, FIB policy routed
IP: s=10.4.10.1 (GigabitEthernet0/0), d=10.255.255.5, len 100, FIB policy rejected(no match) - normal forwarding

"Policy match" plus "policy routed" with a g= gateway means PBR forwarded the packet. "Policy rejected" means the packet was routed normally, either because it matched nothing or because the next hop was unusable.

Troubleshooting PBR

Symptom Likely cause Check
Counter stays at 0, traffic uses normal path Policy on the wrong interface (egress, or a different ingress) show ip policy, where the traffic actually arrives
Counter stays at 0, policy on the right interface ACL matches the wrong field (destination instead of source), wrong wildcard, wrong protocol show access-lists, compare with real flows
Counter increases but path is unchanged Next hop not directly connected, track down, or set ip default next-hop with a specific route in the RIB show route-map (look for [down]), show track, show ip route DEST
Policy works for hosts, not for router pings Testing with router-generated traffic Test from a host, or add ip local policy
Traffic black-holed after an upstream failure Plain set ip next-hop, interface stays up Add verify-availability with IP SLA tracking
Return traffic takes a different path PBR is one-way; the remote side routes back normally Traceroute from both ends; asymmetric paths may break stateful firewalls
Some matched traffic dropped on purpose by design but other traffic affected Overly broad ACL with set interface Null0 ACL hit counts

Exam trap

Route-map name typos are silent. ip policy route-map GUEST_PBR referencing a route map that does not exist (the real one is GUEST-PBR) is accepted, and every packet is routed normally. Compare the name in show ip policy with the names in show route-map.

PBR also affects only the router where it is configured. If R1 policy-routes guest traffic to R3, but R3's own routing table sends that destination back to R1, you get a loop between them. Always confirm that the PBR next hop has a sensible route onward to the destination.

Lab: guest traffic over the backup carrier

Topology: five IOSv routers. R4 simulates the LAN with two loopbacks: staff 10.4.10.0/24 and guests 10.4.20.0/24. R1 is the edge. R2 is the primary carrier and R3 the backup carrier, both connecting to R5, which simulates the Internet with Loopback0 10.255.255.5. OSPF area 0 runs everywhere, and R1's Gi0/2 cost is raised so normal routing prefers R2.

graph LR
  R4["R4 LAN<br/>Lo10 10.4.10.1/24 staff<br/>Lo20 10.4.20.1/24 guest"] -- "Gi0/0 .2  10.1.4.0/30  .1 Gi0/0" --- R1["R1 edge<br/>PBR on Gi0/0"]
  R1 -- "Gi0/1 .1  10.1.2.0/30  .2 Gi0/1" --- R2["R2 primary"]
  R1 -- "Gi0/2 .1  10.1.3.0/30  .2 Gi0/1<br/>OSPF cost 100" --- R3["R3 backup"]
  R2 -- "Gi0/2 .1  10.2.5.0/30  .2 Gi0/1" --- R5["R5 Internet<br/>Lo0 10.255.255.5"]
  R3 -- "Gi0/2 .1  10.3.5.0/30  .2 Gi0/2" --- R5

Requirement: guest traffic (source 10.4.20.0/24) must use R3. Staff traffic follows normal routing through R2. If R3 stops answering, guest traffic must fall back to normal routing.

Build

  1. Address all links as shown and run router ospf 1 with network 10.0.0.0 0.255.255.255 area 0 on every router. Set ip ospf cost 100 on R1 Gi0/2.
  2. Verify that show ip route 10.255.255.5 on R1 points to 10.1.2.2 only.
  3. Configure the IP SLA, track, ACL and route map from the configuration example above on R1, and apply ip policy route-map GUEST-PBR on R1 Gi0/0.
  4. Save the working configuration: copy running-config flash:pbr-good.cfg.

Verify the baseline

R4# traceroute 10.255.255.5 source Loopback20 numeric
Type escape sequence to abort.
Tracing the route to 10.255.255.5
VRF info: (vrf in name/id, vrf out name/id)
  1 10.1.4.1 1 msec 1 msec 1 msec
  2 10.1.3.2 2 msec 1 msec 1 msec
  3 10.3.5.2 2 msec *  2 msec

R4# traceroute 10.255.255.5 source Loopback10 numeric
Type escape sequence to abort.
Tracing the route to 10.255.255.5
VRF info: (vrf in name/id, vrf out name/id)
  1 10.1.4.1 1 msec 1 msec 1 msec
  2 10.1.2.2 1 msec 1 msec 1 msec
  3 10.2.5.2 2 msec *  2 msec

Guest traffic takes R3 and staff traffic takes R2. show route-map GUEST-PBR shows the counter increasing and the track [up].

Break and fix

Have a lab partner apply one fault at a time, or apply it yourself and wait a minute before looking. Restore the baseline between faults with configure replace flash:pbr-good.cfg force.

  1. Fault: on R1, interface GigabitEthernet0/0, no ip policy route-map GUEST-PBR, then interface GigabitEthernet0/2, ip policy route-map GUEST-PBR.
    • Symptom: the guest traceroute now goes through 10.1.2.2. The route-map counter stops increasing.
    • Diagnosis: show ip policy lists Gi0/2. Guest packets arrive on Gi0/0, so the policy never sees them.
    • Fix: move the policy back to Gi0/0.
  2. Fault: on R1, ip sla 10, icmp-echo 10.1.3.20 source-interface GigabitEthernet0/2 (re-create the operation with a wrong target and reschedule it).
    • Symptom: guest traffic uses R2 even though R3 is healthy.
    • Diagnosis: show route-map GUEST-PBR shows [down] next to the next hop, and show track 10 shows "Reachability is Down". show ip sla statistics 10 reports timeouts. PBR is behaving correctly for a down track: it falls back to normal routing.
    • Fix: correct the probe target to 10.1.3.2.
  3. Fault: on R1, replace the ACL line with permit ip any 10.4.20.0 0.0.0.255.
    • Symptom: guest traffic uses R2. The route-map counter is frozen, but the track is up.
    • Diagnosis: show access-lists GUEST-SRC shows zero hits. The ACL matches the guest subnet as a destination, but packets arriving on Gi0/0 have it as their source.
    • Fix: permit ip 10.4.20.0 0.0.0.255 any.
  4. Failover test: with the baseline restored, simulate a carrier fault that keeps R1's link up. On R3, create ip access-list extended CARRIER-DOWN with deny ip any any and apply it with ip access-group CARRIER-DOWN in on Gi0/1.
    • With tracking: after the next probe times out (5 to 10 seconds with a 5-second frequency) show track 10 goes down, the route map shows [down], and the guest traceroute moves to R2.
    • Now restore the baseline, change the set clause to a plain set ip next-hop 10.1.3.2, and repeat. R1 Gi0/2 stays up/up, so PBR keeps sending guest packets to R3 and they are dropped. The OSPF adjacency to R3 dies after the 40-second dead interval, but that does not matter: PBR does not consult OSPF, only the connected interface state.
    • Remove the ACL on R3 and restore the baseline.

Lesson from step 4

A plain set ip next-hop only reacts to the local interface going down. Tracking reacts to the next hop failing to answer. It still only proves the first hop is alive, so in production you track something that represents the whole path, and force the probe out the tracked link.

Quick check

A route map used for PBR has one sequence, route-map PBR deny 10 with match ip address 101. A packet matches ACL 101. What happens to it?

It is routed normally using the destination lookup. In PBR, a deny sequence means "do not policy route", not "drop". To drop traffic with PBR you need a permit sequence with set interface Null0.

Why does show route-map show a non-zero match counter while traceroute proves the traffic still follows the routing table?

The packets match the sequence, but the set clause is not usable. Typical causes are a tracked next hop in [down] state, a set ip next-hop address that is not directly connected, or set ip default next-hop when the RIB has a specific route for the destination.

Which command makes the router's own syslog messages follow a PBR policy?

ip local policy route-map NAME in global configuration. The interface ip policy command only acts on packets received on that interface.

References

  • Cisco IOS XE IP Routing configuration guide, "Policy-Based Routing" and "PBR Support for Multiple Tracking Options" chapters: search on cisco.com
  • Cisco IOS XE IP SLAs configuration guide: search "IP SLAs ICMP Echo Operation" on cisco.com
  • Cisco Live On-Demand Library: search "policy based routing" and "ENARSI"
  • Related practice: PBR next-hop decision diagram, Sim 08 fault 2