VyOSXXVII · BGP Best PathBest path
The Weight attribute — a local-only tie-breaker, per-neighbour and per-prefix
What you'll learn
- Explain why Weight is the first step in the best-path algorithm
- Configure Weight on VyOS 1.5 LTS both per-neighbour and per-prefix
- Distinguish Weight (per-router) from Local Preference (per-AS)
- Apply Weight to override an upstream's MED preference on a single router
- Recognise the production failure modes where Weight was the wrong tool
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)
Weight is the first step in the BGP best-path algorithm and the only step whose value never leaves the router. It is not a BGP protocol attribute: no UPDATE message carries a Weight field, RFC 4271 does not define one, and nothing a peer does can set it. It is a local implementation concept — one that originated on Cisco routers and that FRR implements with the same semantics — and its scope is exactly one router.
That scope is the single most important property of Weight, and the reason it is the right tool for some problems and the wrong tool for others. A Weight set on R1 has no effect whatsoever on R2, even when R1 and R2 are iBGP peers in the same AS.
Why Weight is evaluated first
The best-path algorithm evaluates the attributes the local operator controls before those a peer influences. Local Preference is the next step, but Local Preference is propagated across iBGP — it is the operator’s policy for a whole AS. Weight is more local still: the operator’s policy for this one router. The algorithm honours that hierarchy by evaluating Weight first.
The practical consequence is a trade. Setting Weight on a single router to override an upstream’s MED gets the override at the price of breaking AS-wide consistency. If the policy needs to flow to other iBGP speakers, Local Preference is the right tool. If the policy is deliberately local to one border router, Weight is.
flowchart LR
subgraph "Router R1 (the only one with Weight set)"
W1["Weight 500 on routes from peer A"]
LP1["Local Preference 100 (default)"]
end
subgraph "Router R2 (no Weight set)"
W2["Weight 0 everywhere"]
LP2["Local Preference 100 (default)"]
end
subgraph "Peer A"
A["192.0.2.2"]
end
subgraph "Peer B"
B["198.51.100.1"]
end
A --> W1
B --> LP1
A --> W2
B --> LP2
W1 -->|"decided at step 1"| R1["R1 chooses peer A"]
LP2 -->|"tie, falls through"| R2["R2 compares AS_PATH<br/>and later steps"]
R1 has a reason to prefer peer A — perhaps it is the border router for a site with a commercial preference for that upstream. R2 does not have that reason. With Weight set on R1 only, R1 decides at step 1 and R2 falls through to Local Preference, AS_PATH and the rest.
Configuring Weight on VyOS 1.5 LTS
VyOS 1.5 exposes Weight in two places, and choosing between them is the first design decision.
Per neighbour, for every route that peer sends. This is a leaf under the neighbour’s address family, and it is the simpler of the two:
configure
set protocols bgp system-as 64512
set protocols bgp neighbor 192.0.2.2 remote-as 65001
set protocols bgp neighbor 192.0.2.2 address-family ipv4-unicast weight 500
commit
save
Every prefix learned from 192.0.2.2 in the IPv4 unicast family is stored with weight 500. There is no matching, no route-map, and nothing to keep in sync. The accepted range on this leaf is 1-65535.
Per prefix, via a route-map. When only some of a peer’s routes should be weighted, the weight is set in a route-map applied on import:
set policy prefix-list CUSTOMER-ROUTES rule 10 action permit
set policy prefix-list CUSTOMER-ROUTES rule 10 prefix 198.51.100.0/24
set policy prefix-list CUSTOMER-ROUTES rule 10 description 'Customer edge routes'
set policy route-map WEIGHT-CUSTOMER rule 10 action permit
set policy route-map WEIGHT-CUSTOMER rule 10 match ip address prefix-list CUSTOMER-ROUTES
set policy route-map WEIGHT-CUSTOMER rule 10 set weight 600
set policy route-map WEIGHT-CUSTOMER rule 20 action permit
set protocols bgp neighbor 203.0.113.5 address-family ipv4-unicast route-map import WEIGHT-CUSTOMER
Only routes matching 198.51.100.0/24 from 203.0.113.5 get weight 600;
everything else from that peer keeps weight 0. The route-map leaf
accepts a wider range than the neighbour leaf does (0 to 4294967295),
which is occasionally useful and mostly a trap — see the failure modes.
Rule 20 is not optional. A route-map denies anything that matches no entry, so a map with only rule 10 would reject every prefix outside the prefix-list. The empty permit at the end is what makes this an annotating map rather than a filtering one.
Reading the weight back
$ show bgp ipv4 unicast 198.51.100.0/24BGP routing table entry for 198.51.100.0/24
Paths: (2 available, best #1, table default)
65001
192.0.2.2 from 192.0.2.2 (10.255.0.1)
Origin IGP, metric 0, localpref 100, weight 500, valid, external, best (Weight)
65002
198.51.100.1 from 198.51.100.1 (10.255.0.2)
Origin IGP, metric 0, localpref 100, valid, externalIllustrative output
Two details are worth reading carefully. The weight 500 field appears
on the first path and the best-path reason in parentheses names the step
that decided it — best (Weight) is FRR telling you the decision was
made at step 1 and nothing further was compared. The second path shows
no weight field at all, because FRR omits a weight of zero rather than
printing it.
If you need the same view without the VyOS operational wrapper,
vtysh -c 'show bgp ipv4 unicast 198.51.100.0/24' produces it directly.
The canonical Weight use cases
1. Override an upstream’s MED on a single border router. The upstream sends the same prefix with MED 50 from peer A and MED 100 from peer B. Left alone the router prefers the lower MED. If this router should egress via peer B — because it is the backup border and should not carry primary traffic — a weight on peer B settles it at step 1, long before MED is looked at.
2. Engineering a per-router egress for a specific prefix. A prefix should leave via peer A on R1 and via peer B on R2, with AS-wide Local Preference identical on both. A route-map weight on each router’s preferred peer gives exactly that asymmetry, and gives it deliberately.
3. Validating a policy before deploying it AS-wide. A new
preference is easier to test as a weight on one router than as a Local
Preference change that propagates. The blast radius is one device and
the rollback is one delete.
4. Defending against a peer you do not control. A peer sends routes with an implausibly short AS_PATH or an unhelpfully low MED and will not change. Weight on the other peer’s routes raises them above the problem without arguing about it.
5. Deliberate, documented asymmetry. Sometimes two border routers should choose differently, and the difference should be visible in the configuration rather than emerging from a metric accident. Weight makes that intent explicit.
Note what is missing from that list: anything AS-wide. “Prefer this upstream for the whole AS” is a Local Preference job, and reaching for Weight there is the root of every failure below.
The canonical “wrong tool” failures
1. AS-wide policy accidentally per-router. An operator sees
weight 500 in a configuration and reads it as an AS-wide preference.
It is not. A change made in that belief moves one router and leaves the
rest of the AS where it was.
2. Two operators, two answers. Two people in the same AS run the
same show command against the same prefix and see different winners.
One router has a weight and the other does not. The asymmetry is
invisible until somebody finds the route-map.
3. Traffic leaves by the wrong border. The router carrying the weight is not the router the operator pictured, so traffic egresses somewhere unexpected. The fix is to remove the weight and express the intent as Local Preference.
4. Drift between routers. R1 has weight 500 on peer A; R2 was given 600 during an incident and never reverted. The two borders now disagree permanently, and nothing in either configuration says why.
5. A test weight that outlived the test. A weight added to validate a change is never removed. It silently wins step 1 against every Local Preference the AS configures afterwards, so the AS-wide policy is correct and ineffective at the same time.
6. A weight above the neighbour leaf’s range. The route-map leaf accepts values up to 4294967295 while the per-neighbour leaf stops at 65535. A number chosen for one and pasted into the other is rejected at commit — the good outcome — but the same mismatch across two routers produces a preference no reviewer can explain from the numbers alone. Pick one small scale, write it down, and stay on it.
Weight vs Local Preference — the decision matrix
| Question | Use Weight | Use Local Preference |
|---|---|---|
| Should the policy apply to one router or many? | One router | Many routers in the AS |
| Should the policy reach iBGP peers? | No — it cannot | Yes |
| Will the neighbouring AS ever see it? | No | No, but iBGP does |
| Is it a per-router override of an AS-wide policy? | Yes, the canonical case | No |
| Is it scoped to one prefix? | Either works | Either works |
| Is it scoped to one peer? | Either works | Either works |
| Should a reviewer be able to see the intent on one router? | Yes | No — the intent is distributed |
Filling in the matrix before configuring is what prevents the wrong-tool failures above.
Removing Weight
Removing a per-neighbour weight is a single delete:
configure
delete protocols bgp neighbor 192.0.2.2 address-family ipv4-unicast weight
commit
save
Removing a route-map weight is two, and the order is worth keeping:
configure
delete protocols bgp neighbor 203.0.113.5 address-family ipv4-unicast route-map import
delete policy route-map WEIGHT-CUSTOMER
commit
save
Detach first, then delete the definition. compare before commit
shows both deletions; an operator who reads that diff catches the case
where the map was detached but left defined — a zombie that a future
change can silently re-attach to a different peer, complete with its
original weight.
How the result is validated
show bgp ipv4 unicast 198.51.100.0/24
show bgp ipv4 unicast summary
show policy route-map
show configuration commands | match weight
The first confirms the weight is attached to the path you meant and shows which step decided the best path. The second confirms the session is still Established — a route-map that denies everything looks very much like a peer that went down. The third confirms the map is defined as you think. The fourth is the audit line: every weight on the router, in one screen, which is the artefact to diff against the other border.
For per-rule hit counters, drop to FRR:
vtysh -c 'show route-map WEIGHT-CUSTOMER'
A rule with a zero counter after a soft-clear is a rule that is not matching, which is a different problem from a rule that matches and sets the wrong value.
How it fails
- Applied on export instead of import.
route-map exportruns on what this router advertises. Weight belongs to a stored path, so the map commits cleanly and changes nothing. - The match never fires. A prefix-list that does not cover the
prefix means
set weightnever runs.vtysh -c 'show route-map ...'shows a zero counter on the rule. - A missing trailing permit. A route-map with only the matching rule denies everything else. The symptom is not “the weight is wrong” but “most of the peer’s prefixes vanished”.
- The map denies instead of permitting.
action denyon the matching rule drops the route rather than weighting it. Again the symptom is a missing prefix, not a wrong preference. - Applied to the wrong peer. The weight lands on peer B’s routes. Traffic egresses via B, the operator looks at A’s configuration, and the two never meet.
- Detached but not deleted. The map is removed from the peer and
left in
policy. Nothing is wrong today. Something is wrong the day it is re-attached.
Rollback
Weight changes are ordinary configuration changes, so the ordinary rollback path applies:
comparebeforecommitto see exactly what is being added.commit-confirm <minutes>for any change made over the path it affects.rollback 1,commit,saveto return to the previous revision.load /config/archive/<known-good-file>,commit,saveto return to a specific archived snapshot.
The previous revision had no weight, so rollback 1 removes it
completely — including, importantly, the route-map definition, which a
hand-written delete of the attachment would have left behind.
Production discipline
Cross-course references
The Linux course’s XIX-Linux-NetFoundations covers the kernel FIB that
receives whichever path wins. The OPNsense course’s
XXX-OPNsense-DynamicRouting covers the same FRR route-map machinery on
the firewall side. vyos-xxvi-01-local-preference covers step 2 and the
AS-wide alternative to everything here; vyos-xxvii-01-best-path-algorithm
covers the full ordering; vyos-xxvii-06-best-path-troubleshoot walks
the debugging of wrong-path-wins failures.
Quiz
Knowledge check · 4 questions
Q1. What is the scope of the Weight attribute?
Q2. The Weight attribute is propagated to iBGP peers along with the route.
Q3. An operator applies a route-map with `set weight 500` on import from peer A, expecting peer A's routes to win. `show bgp ipv4 unicast <prefix>` confirms weight 500 on peer A's path, but peer B's path is still best. Diagnose it.
The route-map is defined, attached with `route-map import` on peer A's IPv4 unicast address family, and the weight is visibly attached to the path. The best-path marker is nevertheless on peer B's path, and the best-path reason in the FRR output names Weight as the deciding step.
Q4. An operator wants the whole AS to prefer peer A over peer B for outbound traffic. They set a weight on peer A on one border router. The other border router still uses peer B. What was the wrong tool, and what should they do instead?
The operator expected the preference to flow from R1 to R2 over iBGP. Weight does not flow - it is not carried in BGP at all - so R2 is unaffected and the AS now has two border routers making different egress decisions for the same prefix.
Passing score: 75%. Answers are checked in this browser.