The routing you never think you will need until the day you do: sending one source out of one ISP and everything else out of the other, announcing a prefix only when the primary dies, draining a BGP session before you touch it, NAT between two VRFs. Each one is the problem, the trick, a config template with documentation addresses, and the trap that catches people.
Steer by sourceIOS XE
VPN out one ISP, everything else out the other
The problem
A router has two internet links. Site-to-site VPN traffic should use one and general internet the other, each should fail over to the other link, and NAT has to follow whichever link the traffic actually leaves by.
The trick
Policy-route the VPN traffic on the LAN interface with set ip next-hop verify-availability tied to an IP SLA track through ISP B. When the probe fails, the route-map stops matching and normal routing takes over.
Default route: a tracked static to ISP A, with a floating static (distance 100) to ISP B behind it.
NAT: two overload rules, each tied to a route-map that matches the inside ACL andmatch interface for one exit. Traffic gets the address of the link it really leaves by.
A deny line in the NAT ACL keeps the VPN traffic out of PAT. Without it, the traffic is translated before encryption, no longer matches the crypto ACL, and goes to the internet in clear.
Two plain overload rules that share one ACL do not split by exit: the first one wins and traffic leaving ISP B carries ISP A's address. That is why the route-maps match the interface.
After a failover, old translations stay until they time out. Clear them in the failback procedure if it matters.
Simpler alternative: a tracked static route for the remote VPN subnet via ISP B, no PBR. You lose per-source control. Cisco's own advice for this scenario is that two routers, one per ISP, is simpler still.
Config
ip sla 10
icmp-echo 192.0.2.53 source-interface GigabitEthernet0/0/1
ip sla schedule 10 life forever start-time now
track 10 ip sla 10 reachability
ip sla 20
icmp-echo 198.51.100.1 source-interface GigabitEthernet0/0/2
ip sla schedule 20 life forever start-time now
track 20 ip sla 20 reachability
!
ip access-list extended VPN-TRAFFIC
permit ip 10.1.0.0 0.0.255.255 10.2.0.0 0.0.255.255
route-map PBR-VPN permit 10
match ip address VPN-TRAFFIC
set ip next-hop verify-availability 198.51.100.1 1 track 20
interface GigabitEthernet0/0/0
description LAN
ip nat inside
ip policy route-map PBR-VPN
!
ip route 192.0.2.53 255.255.255.255 203.0.113.1
ip route 192.0.2.53 255.255.255.255 Null0 250
ip route 0.0.0.0 0.0.0.0 203.0.113.1 track 10
ip route 0.0.0.0 0.0.0.0 198.51.100.1 100
!
ip access-list extended PAT
deny ip 10.1.0.0 0.0.255.255 10.2.0.0 0.0.255.255
permit ip 10.1.0.0 0.0.255.255 any
route-map NAT-ISP-A permit 10
match ip address PAT
match interface GigabitEthernet0/0/1
route-map NAT-ISP-B permit 10
match ip address PAT
match interface GigabitEthernet0/0/2
ip nat inside source route-map NAT-ISP-A interface GigabitEthernet0/0/1 overload
ip nat inside source route-map NAT-ISP-B interface GigabitEthernet0/0/2 overload
Verify
show route-map PBR-VPN
show track brief
show ip nat translations
show crypto session
Where this comes from: Cisco TechNote "Configure PBR" (PBR and local PBR with VPN and NAT); Cisco TechNote "IOS NAT load balancing with two ISP connections".
Steer by sourceNX-OS
Pin specific flows to specific ISP links across two cores
The problem
Hosts behind a pair of Nexus cores (vPC, peer-gateway, HSRP) must reach particular remote hosts over particular ISP links, with failover. Routing protocols cannot route by source, and the ISP links don't terminate on the cores, so the cores never see a link go down.
The trick
Run IP SLA probes from both cores to the far end of each ISP link, with tracks and short up and down delays.
Put the same PBR route-map on both cores: with peer-gateway, a host's traffic can land on either one.
Each sequence matches one source and destination pair and lists two next hops in order, each with verify-availability and its track, plus force-order so the first available one in the list always wins.
Add host routes so each probe target is reached through the right link, and mirror the policy at the far site for return traffic.
Config
feature sla sender
feature pbr
ip sla 1
icmp-echo 192.0.2.2 source-ip 192.0.2.1
ip sla schedule 1 life forever start-time now
track 1 ip sla 1 reachability
delay up 1 down 1
ip sla 2
icmp-echo 198.51.100.2 source-ip 198.51.100.1
ip sla schedule 2 life forever start-time now
track 2 ip sla 2 reachability
delay up 1 down 1
!
ip access-list A-TO-C
10 permit ip 10.10.1.0/24 10.20.1.0/24
route-map PBR permit 10
match ip address A-TO-C
set ip next-hop verify-availability 192.0.2.2 track 1
set ip next-hop verify-availability 198.51.100.2 track 2 force-order
interface Vlan10
ip policy route-map PBR
Verify
show track
show ip sla statistics
show route-map PBR
show ip policy
Where this comes from: Cisco TechNote "Configure policy-based redirect and IP SLA" (Nexus cores with vPC).
Steer by sourceIOS XE
Choose the VRF by who sent the packet
The problem
Traffic arriving on one interface belongs to different VRFs depending on its source, for example several departments or customers sharing one access link.
The trick
Use Multi-VRF selection with policy routing: a route-map matches the source and sets the VRF and next hop with set ip vrf, or sends it back to the global table with set ip global next-hop.
Define each VRF before the route-map that names it.
Add ip vrf receive for each VRF on the interface. Without it, packets selected into a VRF and addressed to the router itself are dropped.
Config
ip access-list standard FROM-FINANCE
permit 10.50.0.0 0.0.255.255
route-map VRF-SELECT permit 10
match ip address FROM-FINANCE
set ip vrf FINANCE next-hop 192.0.2.10
route-map VRF-SELECT permit 20
set ip global next-hop 192.0.2.1
!
interface GigabitEthernet0/0/0
ip policy route-map VRF-SELECT
ip vrf receive FINANCE
Verify
show route-map VRF-SELECT
show ip vrf
show ip route vrf FINANCE
Where this comes from: Cisco configuration guide "Multi-VRF Selection Using Policy-Based Routing".
Steer by sourceIOS XE
When every PBR next hop is down, traffic does not stop
The problem
Engineers expect a policy to drop traffic when its next hops are unavailable. It doesn't: the packet quietly falls back to the routing table and leaves by a path the policy was meant to avoid.
The trick
Know the order IOS works through set clauses: next hop, recursive next hop, interface, default next hop, default interface.
A recursive next hop can be any address reachable through the routing table, not just a connected one. Only one is allowed per route-map entry.
If nothing in the list is available, the packet is routed normally. To force a drop, finish with set interface Null0.
Config
ip access-list extended GUEST
permit ip 10.30.0.0 0.0.255.255 any
route-map GUEST-ONLY-ISP-B permit 10
match ip address GUEST
set ip next-hop recursive 198.51.100.1
set interface Null0
Verify
show route-map GUEST-ONLY-ISP-B
show ip cef 198.51.100.1
Where this comes from: Cisco configuration guide "PBR Recursive Next Hop".
Router-generated trafficIOS XE
Steering the router's own VPN traffic: the encryption trap
The problem
A router with two ISPs must bring up a VPN, or send its own pings and management traffic, over one specific link. You add local policy routing, the tunnel comes up, and nothing passes through it.
The trick
Router-generated traffic: local PBR chooses the exit for the packet before encryption (and for IKE), but the encrypted ESP packet is routed by the normal routing table. If that points the other way, encapsulation fails.
Transit traffic: interface PBR is applied twice, before and after encryption, so no extra route is needed.
Interface PBR is CEF-switched; local PBR is process-switched. Keep local policies narrow.
Practical fix: local PBR for IKE and test traffic, plus a matching tracked host route to the VPN peer so the encrypted packets follow.
Watch for uRPF at the provider: traffic sourced from the "other" link's address may be dropped. Cisco mentions a loopback bounce that makes local traffic look like transit, and does not advise it.
Config
ip access-list extended IKE-LOCAL
permit udp any any eq isakmp
permit udp any any eq non500-isakmp
route-map LOCAL-PBR permit 10
match ip address IKE-LOCAL
set ip next-hop verify-availability 198.51.100.1 1 track 20
ip local policy route-map LOCAL-PBR
!
! ESP follows the routing table, so point the peer the same way
ip route 192.0.2.50 255.255.255.255 198.51.100.1 track 20
Verify
show ip local policy
show crypto session
debug ip policy (lab only)
Where this comes from: Cisco TechNote "Configure PBR" (local PBR and IPsec behaviour).
Inbound BGPIOS XE
Announce a prefix only when another one disappears
The problem
A site should announce its prefix to a backup provider only when the primary path has failed. A permanent backup announcement attracts traffic all the time, even with prepending.
The trick
BGP conditional advertisement: neighbor x advertise-map ADVERTISE non-exist-map WATCH. The prefixes in ADVERTISE go to the backup only while the prefix in WATCH is missing from the BGP table. exist-map does the opposite.
The trap: if the watched prefix can still be learned another way, for example through the backup provider from the wider internet, it never disappears and the backup announcement never happens.
Fix it with a match as-path in the watch map anchored on the primary peer's AS, so it only counts the prefix when it comes from that peer. The AS-path match narrows a prefix match; it can't replace it.
Check the state with show ip bgp neighbors: it shows the condition map, the advertise map and whether it is advertising or withdrawn.
Config
ip prefix-list OUR-BLOCK seq 5 permit 203.0.113.0/24
ip prefix-list WATCH seq 5 permit 198.51.100.0/24
ip as-path access-list 30 permit ^64500_
!
route-map ADVERTISE permit 10
match ip address prefix-list OUR-BLOCK
route-map WATCH-PRIMARY permit 10
match ip address prefix-list WATCH
match as-path 30
!
router bgp 64512
network 203.0.113.0 mask 255.255.255.0
neighbor 192.0.2.9 remote-as 64501
neighbor 192.0.2.9 advertise-map ADVERTISE non-exist-map WATCH-PRIMARY
Verify
show ip bgp neighbors 192.0.2.9 | include Condition-map
show ip bgp neighbors 192.0.2.9 advertised-routes
Where this comes from: Cisco TechNote "Configure and verify BGP conditional advertisement".
Inbound BGPIOS XE
Steer inbound traffic with tags and the origin attribute
The problem
Prepending is blunt, and some providers strip or ignore it. You want to choose which upstream announces a prefix, or tilt traffic between two upstreams, without a config change on every border router each time.
The trick
Tag at the origin: where a prefix enters BGP, give it an internal community meaning "upstream 1 only", "upstream 2 only" or none for both. Each upstream session's route-map announces, drops or alters by community. Moving a prefix becomes one tag change.
The soft version: announce to both, but set origin incomplete towards the less preferred upstream. Origin is compared after local preference and AS-path length, so it tilts traffic without removing the backup path.
Provider action communities: many providers accept communities that prepend or lower local preference further from you, which is often more effective than prepending yourself. Check each provider's community guide; not all support them.
Tag peering and transit routes differently inside your network, and decide deliberately whether a broken peering link should fail over to transit.
Config
ip community-list standard PREFER-UP1 permit 64512:101
ip community-list standard INTERNAL-TAGS permit 64512:101
ip community-list standard INTERNAL-TAGS permit 64512:102
!
route-map TO-UPSTREAM-2 permit 10
match community PREFER-UP1
set origin incomplete
set comm-list INTERNAL-TAGS delete
route-map TO-UPSTREAM-2 permit 20
set comm-list INTERNAL-TAGS delete
!
router bgp 64512
neighbor 192.0.2.17 remote-as 64502
neighbor 192.0.2.17 send-community
neighbor 192.0.2.17 route-map TO-UPSTREAM-2 out
Verify
show ip bgp neighbors 192.0.2.17 advertised-routes
show ip bgp 203.0.113.0 (from a looking glass)
Where this comes from: ESNOG GORE 16 "BGP hacks" (practitioner write-up); Philip Smith, BGP multihoming techniques, NANOG 35; Cisco PfR inbound optimisation guide.
Inbound BGPIOS (legacy PfR)
Automated inbound steering, and why it moved on
The problem
Inbound load across several internet links shifts through the day, and manual prepending can't keep up with it.
The trick
Cisco Performance Routing did this automatically: it learned the busiest inside prefixes and measured each entrance link every 20 seconds.
When a link passed its threshold (75 percent by default) or went out of policy, it prepended, or added prepend communities, on the other entrances: one at a time up to six for delay, or all six at once for loss or unreachability.
PfR is legacy now (it lived on in IWAN and gave way to SD-WAN), but the method still works by hand: watch inbound utilisation per link, act on a few large prefixes, and prefer provider communities to raw prepends.
Where this comes from: Cisco configuration guide "BGP Inbound Optimization Using Performance Routing". Legacy feature, included for the technique.
Outbound BGPIOS XE
Load share in proportion to link speed
The problem
Two exits towards the same provider have different bandwidths. Once BGP multipath is on, traffic splits 1:1 and the smaller link fills first.
The trick
Turn on multipath first: maximum-paths is 1 by default on IOS.
Add DMZ link bandwidth: neighbor x dmzlink-bw on each eBGP session attaches a link-bandwidth extended community, and bgp dmzlink-bw uses it to set traffic shares. The routing table then shows unequal "traffic share count" values.
Across iBGP, an edge router with several links advertises the sum of its link bandwidths, so routers further in balance across edges in proportion to their capacity. Send extended communities to iBGP peers and enable iBGP multipath.
show ip bgp 198.51.100.0 | include DMZ
show ip route 198.51.100.0 | include share
Where this comes from: ipSpace, "Unequal-Cost Multipath with BGP DMZ Link Bandwidth" (practitioner write-up with a Cisco IOS lab).
Outbound BGPIOS XE
Shift outbound traffic in predictable amounts
The problem
An edge link is congested. Changing local preference prefix by prefix moves unpredictable amounts of traffic, and what worked on Monday is wrong by Friday.
The trick
Move groups, not single prefixes: match an origin AS or an AS-path pattern and lower local preference for that group on the congested exit. Big, popular origin ASes carry stable volumes; single prefixes swing week to week.
Use NetFlow to find which destinations fill the link, and pick large, stable ones with one best path across all exits.
Make path selection deterministic with bgp deterministic-med. Use bgp always-compare-med only if you understand what it does across different neighbour ASes.
Change policy without resetting sessions: route refresh (clear ip bgp x soft in), or inbound soft reconfiguration if you also want to inspect what was received.
Config
ip as-path access-list 40 permit _64496$
route-map FROM-ISP-A permit 10
match as-path 40
set local-preference 90
route-map FROM-ISP-A permit 20
!
router bgp 64512
bgp deterministic-med
neighbor 192.0.2.1 route-map FROM-ISP-A in
!
clear ip bgp 192.0.2.1 soft in
Verify
show ip bgp regexp _64496$
show ip bgp neighbors 192.0.2.1 | include refresh
Where this comes from: Feamster, Borkenhagen and Rexford, "Guidelines for Interdomain Traffic Engineering" (research paper with operator data).
Maintenance and migrationIOS XE, IOS XR
Drain an eBGP session before you shut it
The problem
Shutting a BGP session for maintenance drops traffic while the internet reconverges, because the routes are withdrawn before alternatives have spread.
The trick
Use the well-known GRACEFUL_SHUTDOWN community, 65535:0 (RFC 8326). Before the work, tag routes you send on that session with it and set local preference 0 on routes you receive over it. Wait for convergence, then shut the session.
Receiving side: an inbound policy on every eBGP session that sets local preference 0 for routes carrying 65535:0, so neighbours can drain you the same way.
Many large networks honour it, including NTT, Telia, GTT, Cogent and PCCW on the NLNOG list. Check your provider.
Raising MED or the IGP metric only works when the alternatives are otherwise equal, which is why the community is preferred.
Check whether your IOS XE release has a built-in graceful-shutdown command; otherwise use route-maps as shown.
Config
ip community-list standard GSHUT permit 65535:0
route-map FROM-PEER permit 5
match community GSHUT
set local-preference 0
route-map FROM-PEER permit 10
!
! at maintenance time, on the session being drained
route-map TO-PEER-DRAIN permit 10
set community 65535:0 additive
router bgp 64512
neighbor 192.0.2.1 send-community
neighbor 192.0.2.1 route-map TO-PEER-DRAIN out
!
clear ip bgp 192.0.2.1 soft out
Verify
show ip bgp neighbors 192.0.2.1 advertised-routes
show ip bgp community 65535:0
Where this comes from: RFC 8326; NLNOG BGP filter guide.
Maintenance and migrationIOS XE
Change AS number without touching the neighbours
The problem
After a merger or a redesign, a router must move to a new AS number, but its eBGP neighbours can't all change their configuration on the same night.
The trick
Configure neighbor x local-as <old AS> on each eBGP session. Neighbours keep peering with the old AS while the router runs the new one.
The options no-prepend, replace-as and dual-as control whether the old AS appears in paths and whether the neighbour can use either AS during the cutover.
Plan the inside separately: in the worked example, the old eBGP link between the merging networks becomes iBGP between loopbacks, with the IGP extended over it and next-hop-self on the edge.
show ip bgp neighbors 192.0.2.13 | include local AS
show ip bgp neighbors 192.0.2.13 advertised-routes
Where this comes from: ipSpace, "Network Migration with BGP Local-AS" (practitioner write-up with Cisco IOS configs).
Dropping on purposeIOS XE
Black-hole an attack by destination or by source
The problem
A DDoS attack needs dropping at every edge router within a minute, faster than an access list can be written and pushed.
The trick
Destination RTBH: every edge router routes a reserved address to Null0. A trigger router announces the victim /32 by iBGP with a black-hole community and that reserved next hop, so every edge router forwards it into Null0.
Source RTBH: announce the attacker's source address the same way, and enable uRPF loose mode on the edge interfaces. The source now resolves to Null0, so uRPF drops its packets.
Lock down acceptance on the edge routers: /32s only, empty AS path (^$), add no-export, and drop anything else carrying the black-hole community.
Customer-triggered RTBH is possible, but only for prefixes each customer is authorised to announce.
Know uRPF: strict mode drops legitimate traffic on asymmetric paths; loose mode drops traffic from sources with no route at all.
History: uRPF loose mode was created for this in 2000, to drop by source on 60 routers in 60 seconds.
Config
! every edge router
ip route 192.0.2.1 255.255.255.255 Null0
interface GigabitEthernet0/0/0
description Internet-facing
ip verify unicast source reachable-via any
!
! trigger router
route-map RTBH-TRIGGER permit 10
match tag 666
set ip next-hop 192.0.2.1
set community 64512:666 no-export
set origin igp
router bgp 64512
redistribute static route-map RTBH-TRIGGER
!
! to drop an attacker by source:
ip route 198.51.100.66 255.255.255.255 Null0 tag 666
Verify
show ip bgp 198.51.100.66
show ip route 198.51.100.66
show ip interface Gi0/0/0 | include verify
Where this comes from: RFC 5635 (includes a Cisco IOS configuration).
VRFs and NATIOS XE (ASR 1000, Catalyst 8000)
NAT between two VRFs
The problem
Traffic must pass from one VRF to another with NAT in between, for example a customer VRF with overlapping addresses reaching a shared WAN or services VRF.
The trick
Create a VASI pair: vasileft1 in one VRF and vasiright1 in the other, on a small shared subnet. Whatever leaves vasileft enters vasiright, so each VRF routes into the other with a static route via its VASI interface.
Apply NAT where it fits: inside on vasiright with the WAN interface as outside (the usual case), or entirely in the left VRF with vasileft as outside.
The TTL drops by two: once in each VRF.
The trap: don't translate to the WAN interface's own address. The router treats the return traffic as its own and never forwards it back through the VASI pair. Use a separate address or pool.
Config
interface vasileft1
vrf forwarding CUST
ip address 10.255.0.1 255.255.255.252
interface vasiright1
vrf forwarding WAN
ip address 10.255.0.2 255.255.255.252
ip nat inside
interface GigabitEthernet0/0/1
vrf forwarding WAN
ip address 203.0.113.2 255.255.255.0
ip nat outside
!
ip route vrf CUST 0.0.0.0 0.0.0.0 vasileft1 10.255.0.2
ip route vrf WAN 10.60.0.0 255.255.0.0 vasiright1 10.255.0.1
!
ip access-list standard CUST-NETS
permit 10.60.0.0 0.0.255.255
ip nat pool CUST-OUT 203.0.113.10 203.0.113.10 prefix-length 24
ip nat inside source list CUST-NETS pool CUST-OUT vrf WAN overload
Verify
show ip nat translations vrf WAN
show ip route vrf CUST
show interfaces vasileft1
Where this comes from: Cisco TechNote "Configure VRF-Aware Software Infrastructure (VASI) NAT" (recertified 2026).
VRFs and NATIOS XE SD-WAN
Let a public server out of a VRF without NAT
The problem
A server with a public address sits in a service VRF (an SD-WAN service VPN, say) but must be reachable from the internet in the global table, bypassing the NAT everything else uses.
The trick
Replicate routes both ways: in the VRF, route-replicate from vrf global unicast connected; in the global address family, route-replicate from vrf <VRF> unicast connected. Each table then holds the other's connected routes.
Pair it with a data policy that matches the server's address and sets NAT bypass, so it keeps its real address.
In the same policy, other groups can use different internet NAT methods: a pool shared across both ISPs for one group, one ISP's interface address for another.
Config
vrf definition WEB
rd 64512:30
address-family ipv4
route-replicate from vrf global unicast connected
route-target export 64512:30
route-target import 64512:30
!
global-address-family ipv4
route-replicate from vrf WEB unicast connected
Verify
show ip route vrf WEB
show ip route 203.0.113.80
Where this comes from: Cisco Live APJC 2024 BRKENS-2720.
Bad linksCatalyst SD-WAN
Make a lossy link usable for real-time traffic
The problem
Critical traffic has to run over a lossy transport such as satellite, where waiting for retransmission makes voice and video unusable.
The trick
Packet duplication: a data policy matches the critical application and sends each packet over two tunnels; the far edge discards the duplicate. Either path can lose packets without the application noticing.
Forward error correction: one parity packet (XOR) for every four, so the far end can rebuild any single lost packet in the group. Adaptive FEC switches on only when loss appears.
TCP optimisation: the WAN edge proxies TCP using BBR2; Cisco measured about three times the throughput over Starlink.
Combine with adaptive QoS on the satellite tunnel, so the shaper follows the real capacity.
Where this comes from: Cisco Live APJC 2024 BRKENS-2720.