Dual-Transit Multihoming: Inbound and Outbound Traffic Engineering

Two transit providers gives you redundancy the moment both sessions are up. Using that redundancy well — sending traffic out the cheaper or shorter path, pulling inbound traffic toward the link you want — is a separate skill, and it splits cleanly into two problems with very different difficulty.

Outbound is easy: you decide, locally, which exit to use. Inbound is hard: you are trying to influence other people’s routers, and you only get to ask politely.

Outbound: You Are in Control

Outbound path selection is a local decision, and local-preference is the biggest lever in BGP best-path — higher wins, before AS-path length even matters.

Prefer Transit A for everything, fall back to B:

Terminal window
# FRR
route-map FROM-TRANSIT-A permit 10
set local-preference 200
route-map FROM-TRANSIT-B permit 10
set local-preference 100
router bgp 64500
neighbor 192.0.2.1 route-map FROM-TRANSIT-A in
neighbor 198.51.100.1 route-map FROM-TRANSIT-B in

Per-destination steering is just more specific matching. Send traffic to a particular network out B while keeping the default out A:

Terminal window
bgp as-path access-list TO-CONTENT permit _15169$
route-map FROM-TRANSIT-B permit 5
match as-path TO-CONTENT
set local-preference 300
route-map FROM-TRANSIT-B permit 10
set local-preference 100

Want to balance by destination popularity? Accept full tables from both, set equal local-pref, and let AS-path length pick the shorter path per prefix — the closest thing to “natural” load sharing without per-flow hashing across providers.

Inbound: You Can Only Ask

Inbound traffic follows the path other networks choose. You influence that choice with three increasingly blunt tools.

AS-Path Prepend

Make a path look longer so it loses on tie-breaks. Prepend your own AS on announcements out the link you want to de-prioritize:

Terminal window
route-map TO-TRANSIT-B-OUT permit 10
set as-path prepend 64500 64500
router bgp 64500
neighbor 198.51.100.1 route-map TO-TRANSIT-B-OUT out

Prepending is coarse. One prepend is often ignored (AS-path is far down the decision list, after local-pref the remote network sets for its own reasons). Two or three is typical. More than three rarely helps and signals you do not understand the problem.

MED

MED only influences a network you peer with on multiple links, telling it which of your links to prefer. It is comparable only between routes from the same neighbor AS, so it does nothing for choosing between two different transits:

Terminal window
route-map TO-TRANSIT-A-LINK2 permit 10
set metric 100 # higher MED = less preferred (lower wins)

Useful for two links to the same provider; useless for steering between A and B.

Provider Communities — The Real Tool

The effective inbound lever is the BGP communities your upstream publishes. They let you reach past your provider to influence how their peers see you: prepend to specific upstreams, set local-pref inside the provider, or selectively not announce.

Terminal window
# Examples (each provider documents their own values):
# 64500:70 -> provider sets local-pref 70 (lower) on this prefix
# 64500:1:174 -> prepend once toward AS174
# 64500:0:3356 -> do NOT announce to AS3356
route-map TO-TRANSIT-A-OUT permit 10
set large-community 64500:1:174 64500:1:174 additive

This is far more surgical than prepending, because it acts deep inside the provider’s network where the path decision is actually made. Read each upstream’s community guide — they differ, and this is where the real inbound control lives.

Splitting Inbound by Prefix

The bluntest but most reliable inbound control: announce more-specifics out the preferred link and the aggregate everywhere. Longest-prefix-match beats every BGP attribute, so the more-specific wins regardless of what remote networks prefer.

Terminal window
# Announce the /24 halves split across links, aggregate /23 out both
# Out Transit A: 203.0.113.0/24 + 203.0.113.0/23
# Out Transit B: 203.0.113.0/23 only

Use sparingly. Deaggregation adds to the global table, and many networks filter long prefixes. It is the hammer for when prepend and communities are not enough — not the default.

The Decision Process, in Order

Every steering trick maps to one step of the BGP best-path algorithm. Knowing the order is the difference between a knob that works and one that gets silently overridden:

StepAttributeDirectionWho sets it
1Weight (Cisco-only)OutboundYou, locally
2Local-preferenceOutboundYou, locally
3AS-path lengthBothYou influence via prepend
4MEDInbound (same AS)You, via metric
5eBGP over iBGPTopology
6IGP metric to next-hopOutbound tie-breakHot-potato

Local-pref (step 2) beats AS-path (step 3), which is why a remote network’s local-pref policy quietly eats your prepends — their step 2 runs before your prepend reaches their step 3. Provider communities work because they reach in and change the provider’s own step 2, ahead of where prepend lives. That single fact explains why communities are surgical and prepend is a blunt instrument.

When a Transit Drops: Convergence and the Failover Trap

Redundancy is only real if failover behaves the way you assumed. Pull Transit A and watch outbound move to B:

Terminal window
# Simulate the session loss
router bgp 64500
neighbor 192.0.2.1 shutdown
# Outbound should reconverge onto Transit B
show bgp ipv4 unicast 8.8.8.0/24
# best path now via 198.51.100.1
# How long did the RIB take to settle?
show bgp ipv4 unicast summary | include 192.0.2.1

Two production gotchas surface here. First, if you set Transit A to local-pref 200 and B to 100, outbound fails over cleanly — best-path just re-runs. But inbound does not move on your command at all: remote networks only stop using A when their session via A goes away and they reconverge on their own timescale, which can be tens of seconds with default timers. Enable BFD on the transit links so a dark-fibre cut is detected in milliseconds rather than waiting on the BGP hold timer:

Terminal window
# FRR — fast failure detection on the transit session
neighbor 192.0.2.1 bfd

Second, watch the return path during single-homed operation. If you were splitting inbound with more-specifics out A only, those more-specifics vanish when A drops — inbound for that block falls back to the aggregate announced out B, which is correct, provided you actually announced the aggregate everywhere. The classic outage is announcing more-specifics out the preferred link and forgetting the aggregate on the backup; lose the preferred link and that traffic blackholes. Verify both links carry the covering aggregate before you rely on the split:

Terminal window
show bgp ipv4 unicast neighbors 198.51.100.1 advertised-routes | include 203.0.113.0/23

Verifying It Does What You Think

Terminal window
# Which exit is chosen for a destination?
show bgp ipv4 unicast 8.8.8.0/24
# ">"-marked best path should be via the intended neighbor
# Confirm prepend/communities are actually applied outbound
show bgp ipv4 unicast neighbors 198.51.100.1 advertised-routes
show bgp ipv4 unicast 203.0.113.0/24

For inbound, config is not proof — measure. Look at interface counters and flow data per provider, and use looking glasses in the regions you care about to see which path remote networks actually take:

Terminal window
# From a third-party looking glass, check the AS-path back to you
# AS-path should traverse your preferred upstream

Watch for the Communities Being Stripped

A community-based inbound plan fails silently if the upstream strips the communities you set before they take effect. Confirm they survive onto the wire, and confirm the provider documents the exact action — a transit that resets local-preference on ingress will ignore a community telling it to lower local-pref:

Terminal window
# Are the large-communities actually attached to what we advertise?
show bgp ipv4 unicast neighbors 192.0.2.1 advertised-routes 203.0.113.0/24
show bgp ipv4 unicast 203.0.113.0/24
# Large Community: 64500:1:174 64500:1:174 <- must be present here

If the community is present on your side but the provider’s looking glass shows the path unchanged, you are either using the wrong value or hitting a provider that does not honor that action on your prefix class. Read their guide, not a generic one — 64500: here stands in for the provider’s own ASN, and the function-to-value mapping is theirs. There is no portable community standard for this beyond the informational well-known ranges; everything surgical is per-provider.

The Honest Limitation

You will never get inbound perfectly balanced. Remote networks weight their own decisions — peering relationships, cost, hot-potato routing — far above anything you signal. Aim for “good enough”: shift the bulk of inbound off a congested or expensive link, accept that some will not move, and re-measure. Operators who chase a perfect 50/50 inbound split with ever-more prepends are fighting physics. Set local-pref outbound, use provider communities inbound, measure, and stop when it is good enough.