Skip to main content
RunBook Academy

VyOSXXXIV · Route RedistributionRedistribution anti-patterns

Redistribution anti-patterns — bidirectional loops, missing tags, suboptimal paths

Advanced⏱ ~26 minvyosvtyshshow ip routeshow ip bgpshow ip ospf database externalshow route-mapshow ip bgp communityclear ip bgp <peer> soft in

What you'll learn

  • Recognise the four anti-patterns: bidirectional loops, missing route-maps, missing tags, suboptimal paths
  • Diagnose each anti-pattern from the routing table and BGP/OSPF database evidence
  • Apply the four remediations: tag-based feedback prevention, route-map filtering, administrative-distance tuning, redistribute-only-the-intended-prefixes
  • Build a redistribution design that survives operational change

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.

Redistribution is the source of the most common production routing failures: route leaks, suboptimal paths, and routing loops. The four anti-patterns are well-known but the operator who does not internalise them will encounter them in every multi-protocol network.

This lesson covers the four anti-patterns, their evidence, and their remediations. The pattern is consistent: every anti-pattern is a missing control, and every control is a tag, route-map, or administrative-distance tuning.

Anti-pattern 1: bidirectional redistribution loop

Two routers running OSPF and BGP, each redistributing the other protocol into its own, is the textbook loop. R1 redistributes OSPF into BGP; R2 redistributes BGP into OSPF; R1 sees its own route come back through OSPF as an external route.

flowchart LR
  O1["10.10.0.0/16\nOSPF internal on R1, cost 50"]
  R1["R1\nBGP AS 64512\nredistribute ospf into bgp"]
  R2["R2\nBGP AS 64513\nredistribute bgp into ospf"]

  O1 --> R1
  R1 -- "eBGP UPDATE\n10.10.0.0/16, MED 50" --> R2
  R2 -- "OSPF Type 5 LSA\n10.10.0.0/16, metric 100" --> O1

The evidence:

  • show ip ospf database external self-originate on R1 shows the redistributed-back route as Type 5.
  • show ip bgp on R1 shows the route with R2’s community tag.
  • The metric oscillates as BGP refreshes.

The remediation: tag-based feedback prevention. R1 stamps a community tag on the OSPF-to-BGP redistribution; R2 denies the tag on the BGP-to-OSPF redistribution.

# On R1: stamp a unique community on what it redistributes
set policy route-map OSPF-TO-BGP rule 10 action 'permit'
set policy route-map OSPF-TO-BGP rule 10 set community add '64512:400'

set protocols bgp system-as 64512
set protocols bgp address-family ipv4-unicast redistribute ospf route-map 'OSPF-TO-BGP'
# On R2: classify R1's tag, then refuse to send it back into OSPF
set policy community-list FROM-R1 rule 10 action 'permit'
set policy community-list FROM-R1 rule 10 regex '64512:400'

set policy route-map BGP-FILTER rule 10 action 'deny'
set policy route-map BGP-FILTER rule 10 match community community-list 'FROM-R1'

set policy route-map BGP-FILTER rule 20 action 'permit'

set protocols ospf redistribute bgp route-map 'BGP-FILTER'

After the fix, R2 denies any BGP route carrying 64512:400. R1’s redistributed routes are not re-injected into OSPF. The loop is broken.

Three details in that configuration are 1.5-specific and each one breaks a 1.3-era runbook silently or loudly:

  • set community add '64512:400'. On 1.3 the clause was a single string with a trailing keyword — set community '64512:400 additive'. On 1.5 add and replace are nodes in the tree, not words inside the value, and you must choose. add is what you want here: replace would discard whatever communities the route already carried, including tags a different pair of routers is relying on.
  • The community-list rule’s value leaf is called regex, not community. It is one leaf and it is matched as a regular expression, which matters when you graduate from a single tag to 64512:.*.
  • The route-map matches with match community community-list 'FROM-R1' — the community-list is named through its own node, not passed as a bare argument.

And rule 20 is not optional. A route-map ends in an implicit deny, so BGP-FILTER without the bare permit at rule 20 would stop redistributing anything into OSPF at all — which looks, from the routing table, exactly like the loop being fixed very thoroughly.

Anti-pattern 2: missing route-map filter

The redistribution statement is configured without a route-map. Every route from the source protocol is redistributed. Internal prefixes leak.

# Anti-pattern: unfiltered redistribution
set protocols bgp system-as 64512
set protocols bgp address-family ipv4-unicast redistribute ospf

Note where redistribute lives on 1.5: under the address-family, not directly under the BGP node. A redistribution is a statement about one address family, and the tree says so.

The evidence:

  • show ip bgp neighbors <peer> advertised-routes shows internal prefixes (10.0.0.0/8, 192.168.0.0/16, etc.).
  • The upstream provider’s received-routes shows the same prefixes.

The remediation: add a route-map that enumerates the prefixes the operator intends to redistribute.

# Fix: route-map with explicit prefix-list
set policy prefix-list ADVERTISE-OUT rule 10 action 'permit'
set policy prefix-list ADVERTISE-OUT rule 10 prefix '192.168.0.0/16'
set policy prefix-list ADVERTISE-OUT rule 10 le '24'

set policy route-map OSPF-TO-BGP rule 10 action 'permit'
set policy route-map OSPF-TO-BGP rule 10 match ip address prefix-list 'ADVERTISE-OUT'

set protocols bgp system-as 64512
set protocols bgp address-family ipv4-unicast redistribute ospf route-map 'OSPF-TO-BGP'

prefix, ge and le are three separate leaves under the rule; they do not combine onto one set line. And there is no closing action deny rule to add: a prefix matching no rule is denied by the list, and a route matching no rule is denied by the route-map. Both denies are structural, and a rule carrying an action with no prefix under it is rejected at commit rather than accepted as a catch-all.

The fix is straightforward but the leak may have already propagated. The operator must also notify the upstream provider to withdraw the leaked prefixes.

Anti-pattern 3: missing feedback-prevention tag

The redistribution has a route-map but no tag is stamped on the outbound direction. The downstream peer cannot filter based on the redistribution source; the loop is possible.

The evidence:

  • show ip bgp <prefix> shows the route has no community tag identifying the redistribution source.
  • The downstream peer’s show ip bgp community <value> returns nothing for the redistribution source.

The remediation: stamp a community tag on the outbound redistribution.

# On R1: stamp the community tag
set policy route-map OSPF-TO-BGP rule 10 set community add '64512:400'

The tag is the contract. The downstream peer filters based on the tag.

add rather than replace, for the reason above: the route may already be carrying tags that belong to somebody else’s control loop, and replacing them removes a control you cannot see from here.

Anti-pattern 4: suboptimal path from AD collision

When two protocols learn the same prefix, the AD decides which wins in the kernel FIB. A redistribution can create a situation where the wrong protocol wins.

The classic example is a redistribution feeding a prefix back to the router that already had it. R1 learns 10.10.0.0/16 by iBGP, which FRR gives an administrative distance of 200. R2 redistributes BGP into OSPF, so the same prefix arrives back at R1 as an OSPF external — and FRR gives OSPF an administrative distance of 110 whether the route is internal or external. 110 beats 200, so the FIB installs the OSPF external path.

Look at what that means. R1 is forwarding via a path it learned from a re-injection of its own BGP information, and the shorter BGP path it already had is sitting unused in the Loc-RIB. The routing is not wrong in the sense of being broken; it is wrong in the sense of being derived from a longer loop than the one the operator designed.

The evidence:

  • show ip route 10.10.0.0/16 shows the OSPF external route as the selected path. FRR’s output marks the selected candidate and lists the others, so this one command shows both the winner and what it beat.
  • vtysh -c 'show ip bgp 10.10.0.0/16' shows the iBGP path present and best within BGP — BGP chose it; zebra then preferred another protocol’s candidate.
  • show ip ospf database external self-originate on R2 confirms R2 is the one originating the Type 5 LSA.

The remediation options, in order of preference:

  • Do not redistribute the prefix back. If R1’s BGP already carries 10.10.0.0/16, R2 injecting it into OSPF adds nothing and creates the collision. A prefix-list on R2’s BGP-into-OSPF redistribution that excludes what BGP already distributes is the fix that removes the problem rather than outranking it.
  • Raise the redistributed metric. Redistributing with a high metric keeps the external route as a genuine last resort. This helps only against other OSPF paths; it does nothing about the OSPF-versus-iBGP distance comparison, which is decided before metrics are considered.
  • Tune the administrative distance. set protocols ospf distance ospf external 210 makes OSPF external routes less preferred than iBGP’s 200. Note the tree: distance ospf external, with ospf as its own node — distance global next to it changes every OSPF route at once, which is almost never what you want here.

Diagnostic discipline

The four anti-patterns have overlapping symptoms but distinct evidence. The diagnostic discipline:

  1. Identify the winner and what it beat. show ip route 10.10.0.0/16 is FRR’s view: it lists every candidate for the prefix, from every protocol, and marks the one zebra selected. This is the command that shows the collision.
  2. Confirm what the kernel actually holds. ip route show 10.10.0.0/16 is the kernel’s view, and it contains only the installed route. If FRR’s selected candidate and the kernel’s entry disagree, the problem is between zebra and the kernel, not between two protocols.
  3. Identify the redistribution sources. There is no per-protocol show ... redistribute operational command; read the configuration: show configuration commands | match redistribute lists every redistribution statement on the router in one output, across BGP, OSPF and the rest.
  4. Identify the route-maps. vtysh -c 'show route-map' shows what FRR compiled, and show configuration commands | match route-map shows what you asked for. Read both; a name present in one and absent from the other is itself a finding.
  5. Identify the tags. show ip bgp community 64512:400 lists the routes carrying a specific community, which tells you whether the feedback-prevention contract is being honoured in practice.
  6. Walk the chain. For each redistribution, name the source, the route-map, the tag, and the destination. Write it down; the loop is usually obvious once the chain is on paper and invisible while it is spread across two routers.
flowchart TB
  S1["OSPF internal\ncost 50 - distance 110"]
  S2["OSPF external\nredistributed from BGP\nmetric 100 - distance 110"]
  S3["iBGP\nMED 50 - distance 200"]

  S1 --> D{"Step 1: lowest\nadministrative distance"}
  S2 --> D
  S3 --> D

  D -- "110 beats 200" --> M{"Step 2: within OSPF,\nlowest cost"}
  D -- "iBGP eliminated here,\nits MED never considered" --> X["iBGP path unused\nstill best inside BGP"]
  M --> W1["OSPF internal installed"]

Read the two steps in order, because the order is the whole point. Distance is compared first and it is a comparison between protocols: iBGP at 200 loses to anything OSPF offers at 110, and it loses before its MED, its local-preference or its AS-path length are looked at. Only among the survivors — here, the two OSPF candidates, which share a distance — does a metric decide anything.

This is why “the BGP path was clearly better” is not an argument zebra will ever hear, and why the fix for a collision is to remove one of the candidates rather than to improve the one that is losing.

The four remediations

The four anti-patterns have four remediations:

  • Loop → tag-based feedback prevention (community on outbound, deny on inbound).
  • Missing filter → route-map with prefix-list enumeration on the redistribution.
  • Missing tag → community stamping on the outbound redistribution.
  • Suboptimal path → route-map filtering (preferred) or AD tuning (escape hatch).

Every redistribution in production should have:

  • A route-map filter that enumerates the prefixes.
  • A community tag on the outbound direction.
  • A coordinated deny on the inbound direction at every peer that redistributes the other way.

The defensive design is symmetric: R1 stamps a tag; R2 denies the tag; R2 stamps a different tag; R1 denies R2’s tag. Both sides honour the contract; the loop is broken on both sides.

Operational commands

# FRR's view: every candidate for the prefix, winner marked
show ip route 10.10.0.0/16

# The kernel's view: only what is installed
ip route show 10.10.0.0/16

# Every redistribution statement on this router, in one output
show configuration commands | match redistribute

# The route-maps: what FRR compiled, and what you asked for
vtysh -c 'show route-map'
show configuration commands | match route-map

# The tags
show ip bgp community 64512:400
show ip bgp community 64512:500

# Walk the chain
show ip bgp neighbors 10.0.0.2 advertised-routes
show ip ospf database external self-originate

# Re-evaluate inbound policy without dropping the session
clear ip bgp 10.0.0.2 soft in

clear ip bgp 10.0.0.2 soft in asks the peer for a route refresh and re-runs inbound policy against what comes back. The session stays up throughout, which is what makes it safe to use while you are still forming a hypothesis — the hard clear ip bgp 10.0.0.2 tears the session down and reconverges, and is not a diagnostic.

There is no operational command that evicts one prefix to force a re-decision. Changing the inputs is what changes the outcome: fix the redistribution or the policy, and zebra re-evaluates.

Production discipline

Cross-course references

  • XXXIV-VyOS-Redistribution (the previous lessons in this part) covers each redistribution source (static, OSPF, connected, kernel) and their specific configuration patterns.
  • XXV-VyOS-BGPAdvertise covers redistribution in the BGP context.
  • XXXIII-VyOS-RoutePolicy covers the route-map filtering primitives used here.
  • XXVI-VyOS-BGPAttributes covers the MED attribute and the community attribute that are the building blocks of the redistribution design.

Quiz

Knowledge check · 4 questions

  1. Q1. Which is the canonical remediation for a bidirectional redistribution loop?

  2. Q2. An unfiltered `redistribute ospf` is a route leak.

  3. Q3. R1 and R2 run OSPF area 0 and iBGP. R1 redistributes OSPF into BGP with metric 50. R2 redistributes the iBGP back into OSPF with metric 100. R1 sees its own OSPF route come back as Type 5 with metric 100. The MED in BGP oscillates between 50 and 100. What is the canonical remediation?

    R1 and R2 run OSPF and iBGP. Both redistribute bidirectionally. The OSPF LSDB shows the route as Type 5 from R2. The BGP MED oscillates.

  4. Q4. R1 learns 10.10.0.0/16 by iBGP. R2 redistributes BGP into OSPF, so R1 also learns the same prefix as an OSPF external. R1's `show ip route 10.10.0.0/16` selects the OSPF external path, and R1 now forwards using a re-injection of information it already had. Why does OSPF win, and what should be fixed?

    R1 has an iBGP path to 10.10.0.0/16 and an OSPF external path for the same prefix, the latter produced by R2 redistributing BGP into OSPF. Zebra selects the OSPF external path.

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