IP Routing

Policy-Based Routing (PBR): Configuration and Verification

Patch Panel feature image: policy-based routing with debug ip policy output and route-map counters
In: IP Routing, Labs

Routing protocols answer one question: what is the best path to this destination? Policy-based routing (PBR) exists for every time that is the wrong question. Send this application out the backup circuit. Keep guest traffic off the MPLS link. Steer one customer's flows through the firewall stack. PBR lets you route on source address, protocol, port, or packet size instead of destination alone, overriding the routing table for traffic you select. This post configures PBR on Cisco IOS XE, proves it with debug ip policy and route-map counters from a live lab, and covers the failure modes. It is part of the IP Routing cluster.

How PBR Fits Into the Forwarding Path

PBR is evaluated on ingress, before the destination lookup that would normally decide the packet's fate. The interface-level command ip policy route-map subjects every packet arriving on that interface to a route-map. Packets that match a permit entry get the route-map's set action (a next hop or exit interface); packets that match a deny entry, or match nothing, fall through to normal destination-based routing. Locally generated traffic is exempt unless you separately enable ip local policy route-map.

On IOS XE this is CEF-switched, not process-switched, so the old performance folklore about PBR melting routers no longer applies at these scales. The debug output later in this post shows the "FIB policy" path doing the work.

The Lab Scenario

R1 has two links to R2: a primary (10.0.12.0/24) and a deliberately expensive backup (10.0.112.0/24, OSPF cost 100). The routing table therefore always chooses the primary. Our policy: traffic sourced from the lab VM at 192.168.99.100 must use the long path. This is the classic "separate this one source from everyone else" pattern, the same shape as steering guest VLANs or backup jobs.

Configuration: Three Building Blocks

An ACL to describe the interesting traffic, a route-map to bind match to action, and the interface command to arm it:

R1(config)# ip access-list extended VM-SOURCE
R1(config-ext-nacl)# permit ip host 192.168.99.100 any
R1(config-ext-nacl)# exit
R1(config)# route-map PBR-VM permit 10
R1(config-route-map)# match ip address VM-SOURCE
R1(config-route-map)# set ip next-hop 10.0.112.2
R1(config-route-map)# exit
R1(config)# interface Ethernet0/0
R1(config-if)# ip policy route-map PBR-VM

Two design notes. First, the ACL permits, it does not filter: a permit in a PBR ACL means "this packet is subject to the policy", nothing is dropped by it. Second, set ip next-hop expects a directly reachable adjacency; the router ARPs for it on the connected subnet. For next hops that might go away, prefer set ip next-hop verify-availability with an IP SLA tracker, or the packet blackholes while the interface stays up.

The Set Commands, and When Each Applies

set ip next-hop
Forward to this adjacent address, checked before the routing table. The workhorse.
set ip default next-hop
Only used when the routing table has NO route for the destination (a policy of last resort). Frequently confused with the one above; the difference is the whole design.
set interface / set default interface
Exit interface versions of the same pair. Only sensible on point-to-point links.
set ip next-hop verify-availability
Ties the next hop to an IP SLA track object; the policy self-disables when the target dies instead of blackholing.

Proof 1: The Path Actually Changes

From the VM, before PBR, the traceroute follows the routing table across the primary link (second hop 10.0.12.2):

j@llmbits:~$ traceroute -n -q 1 -w 1 4.4.4.4
 1  192.168.99.1  3.344 ms
 2  10.0.12.2  4.373 ms
 3  10.0.23.3  5.776 ms
 4  10.0.34.4  7.364 ms

After applying the policy, same command, and the second hop flips to the long link:

j@llmbits:~$ traceroute -n -q 1 -w 1 4.4.4.4
 1  192.168.99.1  5.429 ms
 2  10.0.112.2  5.323 ms
 3  10.0.23.3  5.855 ms
 4  10.0.34.4  6.753 ms

The routing table on R1 still prefers 10.0.12.2 for this destination the whole time. Only policy-selected traffic moved.

Proof 2: debug ip policy Shows the Decision

With debug ip policy running while the VM sends traffic (use this carefully in production; on a busy interface, rate-limit with an ACL-scoped debug), each packet logs the three-step verdict:

*Jul 11 16:36:53.241: IP: s=192.168.99.100 (Ethernet0/0), d=4.4.4.4, len 60, FIB policy match
*Jul 11 16:36:53.241: IP: s=192.168.99.100 (Ethernet0/0), d=4.4.4.4, len 60, PBR Counted
*Jul 11 16:36:53.241: IP: s=192.168.99.100 (Ethernet0/0), d=4.4.4.4, g=10.0.112.2, len 60, FIB policy routed
*Jul 11 16:36:53.242: IP: route map PBR-VM, item 10, permit

Read the last line as the audit trail: route-map name, sequence number, and result. Traffic that does NOT match logs "policy rejected -- normal forwarding", which is your clue when a match clause is too narrow.

Proof 3: Counters for the Long Haul

Debugs are for the moment; counters are for the change ticket. show route-map accumulates matches per sequence:

R1# show route-map PBR-VM
route-map PBR-VM, permit, sequence 10
  Match clauses:
    ip address (access-lists): VM-SOURCE
  Set clauses:
    ip next-hop 10.0.112.2
  Policy routing matches: 22 packets, 1700 bytes

A policy with zero matches after a soak period means your ACL never fires (wrong source, wrong direction, wrong interface). A policy with matches but no path change means the set clause is failing (next hop unreachable), which show ip policy plus a ping of the next hop will confirm in seconds.

Where PBR Goes Wrong

Most PBR incidents trace to one of a handful of causes. The policy is applied to the wrong interface (it must be the INGRESS interface of the interesting traffic, not the egress you are steering toward). The next hop is not on a connected subnet, so the set clause silently never resolves. Return traffic was forgotten: PBR is unidirectional, and asymmetric paths through stateful devices (firewalls, NAT) drop the reply leg. Locally generated router traffic was expected to obey the policy but needs ip local policy. And policies without verify-availability keep forwarding into a dead next hop because the policy never consults the routing table's view of liveness.

There is also an operational cost worth naming: PBR is invisible to your routing protocols. A traceroute behaves unexpectedly and nothing in show ip route explains why. Document policies aggressively, keep the route-map names descriptive (PBR-VM beats RM-101), and audit with show ip policy, which lists interface-to-route-map bindings in one screen.

Surviving a Dead Next Hop: verify-availability Done Properly

The naked set ip next-hop keeps forwarding as long as the ARP entry resolves, which on an Ethernet segment can outlive the actual path by a long time (the next hop's switchport is up, the router behind it is not). The production-grade pattern ties the policy to an IP SLA probe:

ip sla 10
 icmp-echo 10.0.112.2
 frequency 5
ip sla schedule 10 life forever start-time now
!
track 10 ip sla 10 reachability
!
route-map PBR-VM permit 10
 match ip address VM-SOURCE
 set ip next-hop verify-availability 10.0.112.2 1 track 10

When the track object goes down, the set clause is skipped and matching traffic falls through to normal routing, which is almost always the failure behavior you actually want: policy when possible, reachability always. The trailing 1 is a sequence number; you can chain multiple tracked next hops as an ordered failover list.

Policy for the Router's Own Traffic

ip policy route-map governs transit traffic only. Packets the router itself originates (pings from the CLI, syslog, SNMP traps, tunnel keepalives) consult the routing table directly unless you separately apply ip local policy route-map <name> in global configuration. This is both a gotcha and a tool: a local policy is the cleanest way to source management traffic out a dedicated path without touching the routing table, and forgetting the distinction is why "PBR works but my router's own pings ignore it" is a perennial forum thread. Test transit behavior from a host behind the router, not from the router's own CLI, or you are testing the wrong code path.

PBR or Something Else? Choosing the Right Tool

PBR

Per-source or per-application path selection at a specific ingress point. Surgical, unidirectional, invisible to protocols.

IGP metric tuning

Moves ALL traffic between paths, bidirectionally, with protocol-native failover. Prefer it when the answer is "everything should use the other link".

VRF segmentation

When "this traffic must use that path" is really "these users live in a different network", VRF-Lite gives whole routing tables instead of per-flow exceptions.

SD-WAN policies

Application-aware routing with liveness built in is PBR's spiritual descendant; if you are writing dozens of PBR entries for app steering, you are reinventing SD-WAN.

FAQ

Does PBR override static routes and routing protocols?

Yes. Policy evaluation happens before the destination lookup, so a matching permit entry wins over any routing table state, including statics and defaults. Deny entries and non-matches fall through to the table.

Can I match on port numbers or DSCP?

Anything an extended ACL expresses is matchable: ports, protocols, DSCP, packet length ranges (match length). Match on stable identifiers where possible; port-based application steering ages badly.

Is PBR bad for performance on IOS XE?

No. It is CEF-switched (the "FIB policy routed" lines in the debug are that path). The real costs are operational: an invisible forwarding exception that documentation and monitoring must carry.

Key Takeaways

PBR overrides destination routing for traffic you select by source, port, protocol, or size, evaluated at ingress before the FIB lookup. Build it as ACL, route-map, and ip policy route-map on the ingress interface, and prefer verify-availability whenever the next hop can fail independently of the link. Verify in three layers: traceroute for the path, debug ip policy for per-packet decisions, and route-map counters for sustained proof. And remember it is one-directional, so design the return path deliberately. For how policy interacts with the underlying table, the redistribution guide and the rest of the IP Routing pillar cover the destination-based machinery PBR sits on top of.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.