Skip to main content
RunBook Academy

VyOSXXVI · BGP AttributesAttributes

BGP attribute anti-patterns — missing local-preference, MED oscillation, attribute loss on aggregation

Advanced⏱ ~24 minshow bgp ipv4 unicastshow bgp ipv4 unicast communityshow configuration commandsshow policy route-mapvtysh -c show route-mapvtysh -c show bgp ipv4 unicast <prefix> json

What you'll learn

  • Recognise the missing-local-preference anti-pattern and fix it
  • Detect MED oscillation in production BGP tables
  • Use `as-set` to prevent AS_PATH loss on aggregation, and know what it costs
  • Recognise that a route-map ends in an implicit deny, and what that does to a map written only to set an attribute
  • Apply the discipline that prevents attribute anti-patterns from being deployed

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.

This lesson is the cross-cutting summary of the BGP attribute mistakes that surface in production. Each anti-pattern is a configuration that appears correct but causes subtle failures — wrong best-path selection, route flaps, attribute loss, or unintended advertisement behaviour. On VyOS 1.5 LTS / FRR 10.x, the configuration syntax for the fix is covered in the prior lessons; this lesson focuses on the diagnostic and the discipline.

flowchart TB
  subgraph "Anti-pattern map"
    A1["AP1: missing local-preference"]
    A2["AP2: MED oscillation"]
    A3["AP3: aggregation attribute loss"]
    A4["AP4: well-known community misuse"]
    A5["AP5: forgetting next-hop-self"]
    A6["AP6: LOCAL_PREF on outbound"]
    A7["AP7: aggregate inheritance"]
    A8["AP8: route-map no deny-all"]
  end
  DISC["Diagnostic discipline:\nattribute, direction, rule, match, set"]
  A1 --> DISC
  A2 --> DISC
  A3 --> DISC
  A4 --> DISC
  A5 --> DISC
  A6 --> DISC
  A7 --> DISC
  A8 --> DISC

Anti-pattern 1: missing local-preference on outbound routes

The most common attribute anti-pattern. The operator has two upstream providers and configures eBGP peers for both. The operator expects traffic to prefer Provider A, but Provider B is being used because no LOCAL_PREF has been configured.

The scenario:

# R1 (AS 64512) has two eBGP peers:
set protocols bgp system-as 64512
set protocols bgp neighbor 10.0.0.1 remote-as '64513'
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast
set protocols bgp neighbor 10.0.0.2 remote-as '64514'
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast

# Both peers' routes enter the local AS with LOCAL_PREF 100,
# which is the FRR default for a route learned from an eBGP
# peer with no inbound policy. When an iBGP router (R3) has
# one path via Provider A and one via Provider B, both carry
# LOCAL_PREF 100, so best-path selection falls through to the
# next tie-breakers: AS_PATH length, then origin, then MED.

The result: the iBGP router picks the path based on the default tie-breaker (typically AS_PATH length), not based on the operator’s intent.

The fix:

# Set LOCAL_PREF on the inbound from Provider A
set policy route-map FROM-PROVIDER-A rule 10 action 'permit'
set policy route-map FROM-PROVIDER-A rule 10 set local-preference '200'

set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import 'FROM-PROVIDER-A'

On VyOS 1.5 the binding lives under the neighbour’s address-family and the direction is a value of the route-map node — import for received routes, export for advertised ones — rather than a trailing in/out keyword.

With LOCAL_PREF 200 on Provider A’s routes, every iBGP router in AS 64512 prefers Provider A’s path.

Diagnostic

Run this on an iBGP router — R3, not R1 — because the whole point of LOCAL_PREF is what the rest of the AS sees:

show bgp ipv4 unicast 198.51.100.0/24

FRR prints localpref 100 on a path only when it carries the attribute. Two paths both showing 100, or neither showing a localpref at all, means no inbound policy set it and the selection is being made by AS_PATH length. That is a decision the upstreams are making on your behalf, and it changes when they change their peering.

Fix

Apply the inbound route-map with set local-preference on each eBGP peer whose path should be preferred — and set it on every upstream, including the intended-backup one. A single configured preference is an accident waiting for the day somebody adds a third provider.

Anti-pattern 2: MED oscillation

MED is the one step of the best-path algorithm that is not a total ordering. By default a speaker compares MED only between paths learned from the same neighbouring AS, which means “is path X better than path Y” can depend on which pairs got compared first — and therefore on the order the paths arrived. Two routers with identical configuration and identical paths can settle on different bests, and a router that re-runs selection after a withdrawal can settle on a different best than it had a moment ago. RFC 3345 is the write-up of the persistent version of this, which route reflectors make worse because a reflector only ever advertises its own best path and so hides the alternatives that would have made the comparison stable.

Diagnostic

The signal is not a MED value that changes; it is a best-path selection that changes without an underlying event.

show bgp ipv4 unicast 198.51.100.0/24

Look at which path is flagged best and whether the paths come from different neighbouring ASes. If the best path moves and show bgp ipv4 unicast 198.51.100.0/24 shows no change in which paths exist, MED comparison order is the first suspect.

For a programmatic watch, take the JSON and record the chosen next-hop rather than the metric — the metric is stable, it is the choice that moves:

PREFIX=198.51.100.0/24
while true; do
  date -Is
  vtysh -c "show bgp ipv4 unicast $PREFIX json" \
    | jq -r '.paths[] | select(.bestpath) | .nexthops[0].ip'
  sleep 60
done

Fix

Two settings, in this order of preference.

deterministic-med makes the comparison order-independent by grouping paths by neighbouring AS before comparing, so every router reaches the same answer from the same inputs. It is the setting that removes the non-determinism rather than papering over it, and it should be on estate-wide or not at all — a mixed estate is the worst of both:

set protocols bgp parameters deterministic-med

always-compare-med compares MED across ASes as well. It gives a total ordering, but it also means one upstream’s MED scale is being weighed against another’s, which those two upstreams never agreed on. Turn it on only when you know both upstreams’ MED semantics:

set protocols bgp parameters always-compare-med

If the goal is simply “prefer this provider”, do not reach for MED at all — set a static metric outbound so your own MED is predictable to the peer, and make the local decision with LOCAL_PREF:

set policy route-map TO-PROVIDER-A rule 10 action 'permit'
set policy route-map TO-PROVIDER-A rule 10 set metric '50'
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map export 'TO-PROVIDER-A'

Anti-pattern 3: attribute loss on aggregation

The aggregate-address configuration loses the AS_PATH information unless as-set is configured. The anti-pattern: the operator aggregates prefixes from multiple ASes and the downstream peers cannot determine the originating ASes.

The scenario:

# R1 (AS 64512) aggregates 192.0.2.0/22 from three ASes
set protocols bgp address-family ipv4-unicast aggregate-address 192.0.2.0/22 summary-only

# Without as-set, the aggregate carries only the local AS in
# its AS_PATH. Provider Z cannot tell that the contributing
# prefixes originated in 64513, 64514 and 64515 - and neither
# can anyone downstream of Provider Z.

The consequence: Provider Z’s policy that prefers routes from certain ASes cannot be applied to the aggregate.

Fix

# Use as-set to preserve the contributing AS-path information
set protocols bgp address-family ipv4-unicast aggregate-address 192.0.2.0/22 as-set
set protocols bgp address-family ipv4-unicast aggregate-address 192.0.2.0/22 summary-only

With as-set, the aggregate’s AS_PATH carries an AS_SET segment, printed by FRR in braces: {64513,64514,64515}. Provider Z can now see which ASes originated the contributing prefixes, and loop prevention works again — a speaker whose own AS appears in the set will reject the aggregate, which without as-set it could not detect.

The cost is real and worth stating before you enable it. An AS_SET counts as length 1 in the AS_PATH-length tie-break no matter how many ASes it holds, so the aggregate always looks like a one-hop path. And because the set changes whenever a contributing prefix appears or withdraws, the aggregate’s AS_PATH changes with it — an aggregate that was meant to damp churn from the more-specifics now re-advertises every time their membership moves.

Anti-pattern 4: well-known community on routes that need to be advertised

The well-known communities (no-export, no-advertise, no-export-subconfed) are enforced by every BGP implementation. There is no override.

The anti-pattern: the operator tags a route with no-export intending to prevent advertisement to one specific peer, but the well-known community prevents advertisement to all eBGP peers. The route does not leave the local AS.

Diagnostic

# Check if the route has a well-known community
show ip bgp <prefix>
# Look for "no-export", "no-advertise", "no-export-subconfed"

# Or use a filtered view
show bgp community no-export

Fix

Use a selective community (e.g. 64512:no-export-to-X) and apply the filter at the specific peer’s outbound route-map, rather than using the well-known community.

# Use a selective community instead of no-export
set policy route-map SET-SELECTIVE-NO-EXPORT rule 10 action permit
set policy route-map SET-SELECTIVE-NO-EXPORT rule 10 set community 64512:no-export-to-X

# Filter at the specific peer's outbound route-map
set policy route-map TO-PROVIDER-X rule 10 action deny
set policy route-map TO-PROVIDER-X rule 10 match community COMMUNITY-LIST-NO-EXPORT-TO-X

set protocols bgp 64512 neighbor 10.0.0.99 route-map TO-PROVIDER-X export

This pattern gives the operator precise control: the route is not advertised to Provider X, but is advertised to other eBGP peers.

Anti-pattern 5: forgetting next-hop-self on iBGP

The next-hop attribute is the IP address used to reach the route. For eBGP, the next-hop is the advertising router’s interface address. For iBGP, the next-hop is preserved from the original eBGP advertisement.

The anti-pattern: the operator has an iBGP peering between two routers in different subnets (e.g. the peering is on a transit network, not a /30). The iBGP peer’s IGP does not have a route to the original next-hop. The iBGP peer receives the route but cannot install it (RIB-failure).

Diagnostic

# On the iBGP peer, check the route's status
show ip bgp <prefix>
# Look for "r" (RIB-failure) or missing entry

# Check if the iBGP peer's IGP has the next-hop
show ip route <next-hop>

Fix

# On the originating router, use next-hop-self
set protocols bgp 64512 neighbor <ibgp-peer> next-hop-self

next-hop-self rewrites the next-hop to the local router-id for all advertisements to the iBGP peer. The iBGP peer’s IGP should have a route to the router-id (via the loopback).

Anti-pattern 6: setting LOCAL_PREF on the outbound route-map

The LOCAL_PREF attribute is set on the inbound route-map (received from a peer), not on the outbound route-map (sent to a peer). The anti-pattern: the operator configures set local-preference on the outbound route-map and expects the LOCAL_PREF to influence the peer’s selection.

The reality: LOCAL_PREF is stripped on eBGP boundaries regardless. The outbound set local-preference has no effect on the peer (because the attribute is stripped before sending). It also has no effect on the local router’s selection (the route is locally originated with the default LOCAL_PREF 100).

Diagnostic

# Check the LOCAL_PREF on the received routes
show ip bgp <prefix>
# If the LOCAL_PREF is 100 (default), the inbound route-map
# is not setting it

# Check the route-map direction
show ip bgp neighbors <peer> route-map
# Confirm "import" not "export"

Fix

Apply the LOCAL_PREF on the inbound route-map from the peer:

set policy route-map FROM-PROVIDER-A rule 10 action permit
set policy route-map FROM-PROVIDER-A rule 10 set local-preference 200

set protocols bgp 64512 neighbor 10.0.0.1 route-map FROM-PROVIDER-A import

Anti-pattern 7: assuming aggregates inherit attributes

The aggregate is a new BGP entry created by the local router. Its attributes are local defaults (or whatever the route-map explicitly sets). The aggregate does not inherit attributes from the contributing more-specifics.

The anti-pattern: the operator configures an aggregate and expects it to carry the communities or LOCAL_PREF of the more-specifics. It does not.

Fix

Apply a route-map to the aggregate that explicitly sets the desired attributes:

set policy route-map AGGREGATE-POLICY rule 10 action permit
set policy route-map AGGREGATE-POLICY rule 10 set community 64512:200 additive
set policy route-map AGGREGATE-POLICY rule 10 set metric 50

set protocols bgp 64512 aggregate-address 192.0.2.0/22 route-map AGGREGATE-POLICY

Anti-pattern 8: route-map with no explicit deny-all

The route-map has only permit rules. Without an explicit deny rule (or a deny all in the prefix-list), the route-map’s default behaviour on no-match is permit. Routes that should be blocked are silently passed through.

Diagnostic

# Audit each route-map
show policy route-map <map>

# Look for a trailing deny rule, or a deny-all in the
# prefix-list

Fix

Add a trailing deny all to the prefix-list:

set policy prefix-list MY-PREFIXES rule 1000 action deny

Or add a trailing deny rule to the route-map:

set policy route-map MY-MAP rule 1000 action deny

The diagnostic discipline

For every BGP attribute problem in production:

  1. Identify the attribute — which attribute is at issue (LOCAL_PREF, AS_PATH, MED, ORIGIN, community, next-hop)?
  2. Identify the direction — is the attribute set on inbound or outbound? Is the route-map direction correct?
  3. Identify the rule — which rule in the route-map matches? Is the rule on a permit action?
  4. Identify the match clause — does the match clause (prefix-list, community-list, as-path access-list) match the route?
  5. Identify the set clause — does the set clause (set local-preference, set metric, set community) take effect?

The five-question discipline covers most attribute problems in production.

Production discipline

Cross-course references

  • Part XXVI-01 through XXVI-05 (XXVI-VyOS-BGPAttributes) cover the individual attributes in detail.
  • Part XXVII (XXVII-VyOS-BGPBestPath) covers the best-path algorithm that the attributes influence.
  • Part XXVIII (XXVIII-VyOS-BGPPrefixFilter) covers the filtering primitives that interact with attributes.
  • Part XXX (XXX-VyOS-BGPReflector) covers the route reflector use case where attribute handling has special considerations.

Quiz

Knowledge check · 4 questions

  1. Q1. An operator has two eBGP peers (Provider A and Provider B). The operator expected traffic to prefer Provider A but Provider B is being used. The operator never configured LOCAL_PREF. What is the most likely cause?

  2. Q2. BGP aggregates inherit attributes (LOCAL_PREF, MED, communities) from the contributing more-specifics by default.

  3. Q3. R1 (AS 64512) aggregates 192.0.2.0/22 from three ASes (64513, 64514, 64515) and advertises to Provider Z. Provider Z's policy is 'prefer routes from AS 64513 over routes from AS 64514 over routes from AS 64515'. Provider Z's engineer sees the aggregate with AS_PATH `64512` (the local AS) and cannot apply the preference policy. What is the fix?

    R1: set protocols bgp 64512 aggregate-address 192.0.2.0/22 summary-only set protocols bgp 64512 neighbor 10.0.0.99 remote-as 64999 Provider Z's policy: - if AS_PATH contains 64513: set LOCAL_PREF 200 - if AS_PATH contains 64514: set LOCAL_PREF 100 - if AS_PATH contains 64515: set LOCAL_PREF 50 Provider Z sees the aggregate's AS_PATH as `64512` and cannot apply the policy.

  4. Q4. R1 sets the well-known community `no-export` on routes advertised to Provider A, intending to prevent advertisement to Provider A only. But Provider B also stops receiving the routes. Why?

    R1's outbound route-map: set policy route-map TO-PROVIDER-A rule 10 action permit set policy route-map TO-PROVIDER-A rule 10 set community no-export set protocols bgp 64512 neighbor 10.0.0.1 route-map TO-PROVIDER-A export Provider B is at 10.0.0.2.

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