> ## Content Index
> Fetch the complete content index at: https://www.pinglabz.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Policy-Based Routing (PBR): Configuration and Verification
- URL: https://www.pinglabz.com/policy-based-routing-pbr-cisco/
- Published: 2026-07-11T17:10:26.000Z
- Updated: 2026-08-24T09:43:30.000Z
- Description: PBR routes on source address, protocol, port, or size instead of destination alone. This post configures it on Cisco IOS XE, proves it with debug ip policy and route-map counters, and covers the failure modes.
- Author: Jaime
- Tags: IP Routing, Labs, #Import 2026-08-01 19:54

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](https://www.pinglabz.com/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](https://www.pinglabz.com/vrf-lite-configuration-cisco-ios-xe/) 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](https://www.pinglabz.com/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](https://www.pinglabz.com/route-redistribution-cisco-ios-xe/) and the rest of the [IP Routing pillar](https://www.pinglabz.com/ip-routing/) cover the destination-based machinery PBR sits on top of.