Skip to main content
RunBook Academy

VyOSXVII · Routing Protocol FundamentalsControl plane

Protocol metrics — cost, MED, IS-IS wide metrics, and how operators shift traffic

Intermediate⏱ ~18 minset protocols ospf interface costset protocols ospf auto-cost reference-bandwidthset protocols isis metric-styleset policy route-mapset protocols bgp neighbor address-family ipv4-unicast route-map exportshow ip ospf interfaceshow ip bgpvtysh -c 'clear ip ospf process'

What you'll learn

  • Distinguish cost (OSPF, IS-IS) from metric (BGP MED) and identify when to use each
  • Configure OSPF interface cost and reference bandwidth in VyOS 1.5 LTS
  • Configure IS-IS wide metrics on a VyOS router and recognise why narrow mode is a liability
  • Use a route-map `set metric` rule to set BGP MED on export, with the rule structure VyOS requires
  • Recognise the production failure modes when metric manipulation shifts traffic unexpectedly

Prerequisites

Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling)

Not yet marked complete on this device.

Route preference picks which source the router trusts to deliver a prefix. Cost or metric picks which path the source itself prefers when it has several to choose from. The two are independent layers, and the previous lesson established that the router never reaches the second until the first has tied.

This lesson is about that second layer — cost for OSPF and IS-IS, MED for BGP — and about how an operator uses interface costs and route-maps to move traffic on purpose. The BGP course’s XXVII-VyOS-BGPBestPath covers the full best-path algorithm and everything that outranks MED inside it.

What cost is

In OSPF and IS-IS, cost is a per-interface value. The cost of a path is the sum of the costs of the outgoing interfaces along it. A router running SPF chooses the path with the lowest total cost to each destination.

flowchart LR
  A[Router A] -->|cost 10| B[Router B]
  B -->|cost 20| D[Destination]
  A -->|cost 30| C[Router C]
  C -->|cost 5| D
  A -->|cost 15| E[Router E]
  E -->|cost 15| D

Three paths from A to D:

  • A → B → D: cost 10 + 20 = 30
  • A → C → D: cost 30 + 5 = 35
  • A → E → D: cost 15 + 15 = 30

The cheapest path is tied at 30 between A → B → D and A → E → D. OSPF installs both as equal-cost multipath and the kernel load-shares across them. Nothing had to be configured for that to happen — ECMP is what a tie produces by default, which is worth remembering the next time a deliberate cost change accidentally creates one.

The default OSPF cost on a VyOS interface is derived from bandwidth against a reference of 100 Mbit/s, with a floor of

  1. So a 100 Mbit/s interface is cost 1, and so is a 1 Gbit/s interface, and so is a 100 Gbit/s interface. Only links slower than the reference get a distinguishing cost — a 10 Mbit/s link lands at 10.
[edit]
vyos@vyos# set protocols ospf interface eth0 cost 50
[edit]
vyos@vyos# commit
[edit]
vyos@vyos# save

The valid range for OSPF cost is 1 to 65535. There is no zero: a cost of 0 would make a link free and break the shortest-path computation, so FRR does not accept it.

OSPF cost in production

Two production patterns that use OSPF cost:

  1. Turning a load-shared pair into primary and backup. Two parallel paths of equal bandwidth have equal cost, so OSPF installs both and traffic splits across them. If the design wants one of them held in reserve — because it is a metered circuit, or because it cannot carry the full load alone — raising its cost by any amount breaks the tie and makes it a backup that still fails over automatically.

  2. Making a link’s cost reflect its role rather than its speed. A 1 Gbit/s access link and a 1 Gbit/s backbone link are indistinguishable by default, so transit traffic will happily route through the access link if that is the fewest hops. Raising the access link’s cost keeps transit off it without removing it from OSPF.

IS-IS wide metrics

IS-IS has two metric encodings. The original narrow style allocates 6 bits to a single interface metric, capping it at 63, and 10 bits to an accumulated path metric, capping that at 1023. The wide style defined by RFC 5305 raises the interface metric to 24 bits (16,777,215) and the path metric to 32 bits.

A minimal IS-IS configuration on VyOS 1.5 needs a NET — the OSI address that identifies the router, since IS-IS does not use IP addressing for its own adjacencies — plus the interfaces that should participate:

[edit]
vyos@vyos# set protocols isis net 49.0001.0102.5500.0001.00
[edit]
vyos@vyos# set protocols isis metric-style wide
[edit]
vyos@vyos# set protocols isis interface eth0
[edit]
vyos@vyos# set protocols isis interface eth0 metric 1000
[edit]
vyos@vyos# commit
[edit]
vyos@vyos# save

Modern deployments use wide metrics everywhere. Narrow mode survives only for interoperation with equipment that cannot do better, and its 63-per-link ceiling is genuinely limiting: a network that wants to express a meaningful spread of link preferences runs out of numbers almost immediately.

BGP MED — the path-preference knob that crosses ASes

In BGP the MED (Multi-Exit Discriminator) is the path-preference attribute. Unlike OSPF cost, which is local information every router computes with, MED is sent to the peer as a hint: “if you have two ways into my AS, use the one with the lower MED”.

That framing is the whole of its usefulness and the whole of its unreliability. MED asks the neighbour to make a decision in your favour, and the neighbour is under no obligation.

Where BGP metric comes from

Three places a MED can be set or inherited, and one that surprises people:

  1. A route-map on export. The main deliberate mechanism. set metric in a rule attached with route-map export stamps the MED the peer will see.
  2. A route-map on import. set metric attached with route-map import rewrites the MED on routes arriving from a peer, before your own best-path selection sees them. This is a local decision about how to treat their hint.
  3. Redistribution. A route redistributed into BGP from another protocol can arrive carrying the source protocol’s metric as its MED — an OSPF cost of 20 turning up as MED 20 on the far side of the world. Do not assume either way: check what your redistribution actually produced with show ip bgp before the peer does, and attach a route-map to the redistribute statement if the value is not what you want advertised.
  4. Aggregates, which inherit nothing. A route originated by aggregate-address is a new route the local router invents; it does not carry up a MED from the more-specific routes that contributed to it. If the peer needs to see a MED on the aggregate, the export route-map has to set it like any other locally-originated route.

Route-map metric manipulation

The route-map that sets MED on export, written out in full:

[edit]
vyos@vyos# set policy prefix-list MY-NETWORKS rule 10 action permit
[edit]
vyos@vyos# set policy prefix-list MY-NETWORKS rule 10 prefix 203.0.113.0/24
[edit]
vyos@vyos# set policy route-map SET-MED-PRIMARY rule 10 action permit
[edit]
vyos@vyos# set policy route-map SET-MED-PRIMARY rule 10 match ip address prefix-list MY-NETWORKS
[edit]
vyos@vyos# set policy route-map SET-MED-PRIMARY rule 10 set metric 50
[edit]
vyos@vyos# set policy route-map SET-MED-PRIMARY rule 20 action permit
[edit]
vyos@vyos# set protocols bgp neighbor 198.51.100.2 address-family ipv4-unicast route-map export SET-MED-PRIMARY
[edit]
vyos@vyos# commit
[edit]
vyos@vyos# save

Three details in that block are where the mistakes live.

Every rule needs an action. set policy route-map NAME rule 10 set metric 50 on its own is an incomplete rule and commit rejects it. The action and the match and the set are three separate leaves of the same rule; a rule is not a rule until it says whether it permits or denies.

Rule 20 is not optional. A route-map ends in an implicit deny, not an implicit permit. Without rule 20 — a bare action permit with no match, so it matches everything that reached it — this route-map would set MED 50 on 203.0.113.0/24 and then silently refuse to advertise every other prefix to that peer. Attaching a filtering route-map when you meant a marking route-map is one of the fastest ways to withdraw your own routes from an upstream, and the configuration that does it looks entirely reasonable.

Rules are evaluated in ascending order and the first match wins. Rule 10 matches MY-NETWORKS and stops there; anything else falls through to rule 20. Numbering in tens leaves room to insert without renumbering.

Checking the peer’s view rather than your own:

Read-only / Safe
$ show ip bgp neighbors 198.51.100.2 advertised-routes
BGP table version is 5, local router ID is 10.255.0.1, vrf id 0
Default local pref 100, local AS 65001
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
             i internal, r RIB-failure, S Stale, R Removed
Origin codes:  i - IGP, e - EGP, ? - incomplete

 Network          Next Hop            Metric LocPrf Weight Path
*> 203.0.113.0/24   198.51.100.1            50             0 i

Total number of prefixes 1

Illustrative output

The Metric column carries the 50 the route-map set. Read the prefix count on the last line as carefully as the metric: if the number is 1 when you expected 40, the missing implicit permit has just cost you every other advertisement.

How the result is validated

OSPF cost, on the interface:

Read-only / Safe
$ show ip ospf interface eth0
eth0 is up
ifindex 2, MTU 1500 bytes, BW 1000 Mbit
Internet Address 10.0.0.1/30, Broadcast 10.0.0.3, Area 0.0.0.0
MTU mismatch detection: enabled
Router ID 10.255.0.1, Network Type BROADCAST, Cost: 50
Transmit Delay is 1 sec, State DR, Priority 1
Timer intervals configured, Hello 10s, Dead 40s, Wait 40s, Retransmit 5
  Hello due in 3.094s
Neighbor Count is 1, Adjacent neighbor count is 1

Illustrative output

Cost: 50 confirms the override is on the interface. It does not confirm that anything moved — that is the next check.

OSPF cost, as it lands in the routing table:

Read-only / Safe
$ show ip route 10.20.0.0/16
Routing entry for 10.20.0.0/16
Known via "ospf", distance 110, metric 51, best
Last update 00:00:41 ago
* 10.0.0.2, via eth0, weight 1

Illustrative output

metric 51 is the accumulated path cost: 50 on the local eth0 plus 1 on the next router’s outbound interface, which is at the default. If the number has not moved, either the LSA has not re-flooded yet or the path in question never traversed the interface you changed.

BGP MED, where it actually decides something:

Read-only / Safe
$ show ip bgp 203.0.113.0/24
BGP routing table entry for 203.0.113.0/24, version 5
Paths: (2 available, best #1, table default)
65002
  198.51.100.2 from 198.51.100.2 (203.0.113.9)
    Origin IGP, metric 50, valid, external, best (MED)
    Last update: Tue Aug 18 09:14:22 2026
65002
  198.51.100.6 from 198.51.100.6 (203.0.113.10)
    Origin IGP, metric 100, valid, external
    Last update: Tue Aug 18 09:14:19 2026

Illustrative output

Both paths carry the same first AS in the AS_PATH — 65002 — so they are comparable, and FRR names the deciding attribute in the annotation: best (MED). That parenthesis is the single most useful piece of BGP output on the box. If it says anything other than what you were tuning, you tuned the wrong attribute, and no amount of adjusting the number you chose will change the answer.

How it fails

The production failure modes the engineer must recognise:

  • The cost change moved the wrong direction. OSPF cost is applied on the interface a router sends out of. Raising the cost on the local eth0 moves this router’s outbound traffic; the return traffic is chosen by the far-end router using its own interface costs, which nobody changed. The result is asymmetric routing — often harmless, sometimes fatal when a stateful firewall or a NAT sits on one of the two paths and never sees the other half of the flow. A deliberate path change is a change at both ends.
  • The cost change moved more than intended. Cost is per-link, not per-prefix. Every destination reached through the interface moves together. Check what else was behind it before, not after.
  • A route-map with no terminal permit. The implicit deny at the end of a route-map turns a marking policy into a filtering policy. Symptom: the peer’s prefix count from you collapses to the number of prefixes your route-map explicitly matched, immediately on commit.
  • MED set, nothing moves. Either the two paths are not comparable, because they come from different neighbour ASes and the peer has not enabled always-compare-med, or they are comparable but something earlier in best-path — usually the peer’s own ingress LOCAL_PREF — already decided. Check the best (...) annotation on the peer’s side before adding more MED.
  • MED oscillation without deterministic-med. With multiple paths from the same neighbour AS, the comparison can depend on the order paths sit in the table, so the best path flips on unrelated churn and the prefix flaps. The peer may dampen it. set protocols bgp parameters deterministic-med is the fix and costs nothing to enable.
  • Reference bandwidth changed on one router. Because cost is advertised and summed across routers, a reference change on a subset of the area makes different routers compute different costs for the same link. Paths become inconsistent and, in the worst case, loop between routers that disagree about which direction is cheaper.

Rollback

  • Wrong OSPF interface cost: delete protocols ospf interface eth0 cost, then commit. The cost returns to the bandwidth-derived default, and the LSA re-floods again — the rollback is as much a routing event as the change was.
  • Wrong reference bandwidth: delete protocols ospf auto-cost reference-bandwidth, then commit, on every router that received the change. Rolling back a subset recreates the inconsistency the change was rolled back to avoid.
  • Wrong IS-IS interface metric: delete protocols isis interface eth0 metric, then commit.
  • Wrong export route-map on a peer: delete protocols bgp neighbor 198.51.100.2 address-family ipv4-unicast route-map export, then commit. Detach it before editing it — a half-edited route-map that is still attached is applying whatever state it is in.
  • Wrong route-map body: delete policy route-map SET-MED-PRIMARY rule 10 set metric removes one leaf; delete policy route-map SET-MED-PRIMARY removes the whole map. Deleting a map that is still referenced by a neighbour leaves a dangling reference, so unbind first.

Production discipline

Additional discipline:

  • Change OSPF cost at both ends of a path when the intent is to move a flow rather than a direction, and confirm the return path separately.
  • Set the reference bandwidth once, at design time, on every router in the area, and treat changing it as a maintenance window rather than a tweak.
  • End every route-map with an explicit terminal rule, and know whether you want it to permit or deny. Relying on the implicit deny is fine when the map is a filter and catastrophic when it is a marker.
  • Verify export policy with show ip bgp neighbors <ip> advertised-routes, never with the local show ip bgp entry, and check the prefix count as well as the attribute.
  • Treat MED as a request rather than an instruction. Where the traffic split genuinely matters, agree a community with the peer and let their ingress policy act on it.

Cross-course references

The OSPF course’s XVIII-VyOS-OSPFFund and XX-VyOS-OSPFAreas cover OSPF cost in design context. The BGP course’s XXVII-VyOS-BGPBestPath covers the full BGP best-path algorithm including how LOCAL_PREF, AS_PATH and MED are ordered against each other. The route filtering course’s XXVIII-VyOS-BGPPrefixFilters covers the route-map and prefix-list constructs in detail. The performance course’s L-VyOS-Performance covers the cost of large policy constructs on a busy control plane.

Quiz

Knowledge check · 4 questions

  1. Q1. An operator wants to shift traffic from the eth0 path to the eth1 path on an OSPF router. Both interfaces are 1 Gbps and currently have the default OSPF cost of 1. What is the minimum change to achieve the shift?

  2. Q2. MED is reliably honoured by every BGP peer that receives it; setting MED 50 on export guarantees the peer will prefer this route over another with MED 100.

  3. Q3. An operator attaches a route-map to an upstream peer to stamp MED 50 on their own /24. The route-map has one rule: rule 10, action permit, match ip address prefix-list MY-NETWORKS, set metric 50. Immediately after commit, the upstream's prefix count from this AS drops from 38 to 1, and the operator's other prefixes are unreachable from the Internet. What happened?

    A route-map ends in an implicit deny. Rule 10 matched only the prefixes in MY-NETWORKS; every other prefix the router was advertising reached the end of the map without matching a rule and was therefore denied on export. The operator wrote a marking policy and attached a filtering policy. The MED itself was applied correctly to the one prefix that matched, which is why the change looks partially successful and is easy to misread as a separate problem.

  4. Q4. R1 and R2 are connected by two parallel OSPF links, eth0 and eth1, both at the default cost 1. To take load off eth0, the operator sets cost 100 on R1's eth0 only. Afterwards, traffic from R1 to R2 leaves on eth1 as intended, but return traffic from R2 to R1 still arrives on eth0, and a stateful firewall in the eth1 path starts dropping the flows. What went wrong?

    OSPF cost is applied to the interface a router transmits out of, and each router runs SPF over its own view. R1 now sees eth1 as cheaper and sends over it. R2's interface costs were never touched, so R2 still sees its two links as tied — or still prefers the one facing R1's eth0 — and returns traffic that way. The path is now asymmetric. That is tolerable on plain routers and fatal through a stateful device, which sees only one direction of each flow and has no matching conntrack entry for the replies.

Passing score: 75%. Answers are checked in this browser.