BGP merge paths uses AS_PATH length, not AS_PATH equality or same remote-AS, for ECMP equivalence
Hello BIRD maintainers, I would like to ask for clarification about BIRD's BGP `merge paths` equivalence rules. BIRD 2.19.0+ appears to use an AS_PATH-length-based equivalence check for BGP routes when kernel `merge paths` is enabled. In the reproduced cases, BIRD exported one ECMP route from two BGP paths even though: 1. The two eBGP paths came from different neighboring ASes, and their AS_PATH contents were different. 2. The two iBGP paths had different AS_PATH contents. The common condition was that both paths had the same AS_PATH length and otherwise equal attributes. This looks similar to AS-path multipath-relax behavior in implementations such as FRR, but in FRR this relaxed behavior is enabled by an explicit `bgp bestpath as-path multipath-relax` option. FRR's default multipath behavior is stricter: eBGP multipath requires the same remote AS, and iBGP multipath requires matching AS_PATH contents. I am reporting this as a route-merging behavior / correctness concern and documentation clarification request, not as a security vulnerability or a strict RFC 4271 violation. ## Version Tested I reproduced this on: ```text BIRD version 2.19.0+branch.master.880200b1f94c ``` The PoC intentionally refuses to run against versions older than 2.19.0. ## Background BIRD's documentation for the kernel protocol says that `merge paths` merges best routes and "equivalent non-best routes" to generate one ECMP route: ```text With path merging enabled, both best routes and equivalent non-best routes are merged during export to generate one ECMP (equal-cost multipath) route for each network. ``` The question is what counts as BGP-equivalent for this purpose. The current documentation does not appear to make clear that equivalence can mean equal AS_PATH length rather than equal AS_PATH contents or same eBGP neighboring AS. For comparison, FRR's default behavior is: - iBGP paths are multipath-equivalent only when AS_PATH contents match. - eBGP paths are multipath-equivalent only when the remote AS is the same. - Different AS_PATHs of equal length are allowed only when the explicit `bgp bestpath as-path multipath-relax` knob is enabled. ## Expected Behavior / Documentation I expected BIRD either to avoid merging such paths by default, or to document that `merge paths` intentionally uses an AS_PATH-length-based relaxed equivalence rule. For example, I did not expect these two eBGP routes to be treated as ECMP-equivalent unless this relaxed rule is intentional and documented: ```text Route A: remote AS 65100, AS_PATH 65100 65200 Route B: remote AS 65300, AS_PATH 65300 65400 ``` Likewise, I did not expect these two iBGP routes to be treated as ECMP-equivalent unless AS_PATH-length-based equivalence is the intended behavior: ```text Route A: AS_PATH 65010 65020 Route B: AS_PATH 65030 65040 ``` If this relaxed behavior is intentional, it may be helpful to document it explicitly or expose a configuration knob similar to FRR's AS-path multipath relax option. ## Actual Behavior With `merge paths yes` enabled, BIRD exported a merged ECMP route in both cases. ### Case 1: eBGP paths from different neighboring ASes The PoC announced the same prefix from two synthetic eBGP peers: ```text Peer A: source: 127.0.0.11 remote AS: 65100 AS_PATH: 65100 65200 next-hop: 10.10.0.11 Peer B: source: 127.0.0.12 remote AS: 65300 AS_PATH: 65300 65400 next-hop: 10.10.0.12 ``` BIRD showed both BGP routes: ```text 203.0.113.0/24 unicast [poc_peer1 ... from 127.0.0.11] ! (100/?) [AS65200i] via 192.0.2.11 on lo onlink BGP.origin: IGP BGP.as_path: 65100 65200 BGP.next_hop: 10.10.0.11 BGP.local_pref: 100 unicast [poc_peer2 ... from 127.0.0.12] (100/?) [AS65400i] via 192.0.2.12 on lo onlink BGP.origin: IGP BGP.as_path: 65300 65400 BGP.next_hop: 10.10.0.12 BGP.local_pref: 100 ``` The merged export view for the kernel protocol contained both nexthops: ```text 203.0.113.0/24 unicast [poc_peer1 ... from 127.0.0.11] ! (100/?) [AS65200i] via 192.0.2.11 on lo onlink weight 1 via 192.0.2.12 on lo onlink weight 1 BGP.origin: IGP BGP.as_path: 65100 65200 BGP.next_hop: 10.10.0.11 BGP.local_pref: 100 ``` ### Case 2: iBGP paths with different AS_PATH contents The PoC also reproduced the behavior with two iBGP peers: ```text Peer A: source: 127.0.0.11 AS_PATH: 65010 65020 next-hop: 10.10.0.11 Peer B: source: 127.0.0.12 AS_PATH: 65030 65040 next-hop: 10.10.0.12 ``` BIRD again exported one merged route: ```text 203.0.113.0/24 unicast [poc_peer1 ... from 127.0.0.11] ! (100/?) [AS65020i] via 192.0.2.11 on lo onlink weight 1 via 192.0.2.12 on lo onlink weight 1 BGP.origin: IGP BGP.as_path: 65010 65020 BGP.next_hop: 10.10.0.11 BGP.local_pref: 100 ``` ## Steps to Reproduce The attached PoC starts a private BIRD instance, creates two synthetic BGP peers, advertises the same IPv4 prefix twice, and enables kernel `merge paths`. Run the eBGP case: ```sh python3 poc_bird_bgp_multipath_aspath.py --keep-workdir ``` Run the iBGP case: ```sh python3 poc_bird_bgp_multipath_aspath.py --case ibgp --keep-workdir ``` Expected reproduced signal: ```text RESULT: BIRD treated the routes as merge-paths equivalent and produced ECMP export view. ``` The script exits with status `2` when the behavior is reproduced. On an unprivileged account, BIRD may log netlink permission errors and the Linux kernel table may remain empty. The important evidence is the `birdc show route export krt_poc 203.0.113.0/24 all` output, because BIRD's CLI uses the same `rt_export_merged()` path to construct the merged export route. ## Source-Level Analysis BIRD's BGP merge predicate checks AS_PATH length but not full AS_PATH equality or same eBGP neighboring AS. In `proto/bgp/attrs.c`, `bgp_rte_mergable()` checks local preference, resolvability, LLGR stale state, AS_PATH length, origin, MED, peer type, and IGP metric: ```c /* RFC 4271 9.1.2.2. a) Use AS path lengths */ if (pri_bgp->cf->compare_path_lengths || sec_bgp->cf->compare_path_lengths) { x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN; s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN; if (p != s) return 0; } ``` It prevents mixing iBGP and eBGP paths: ```c /* RFC 4271 9.1.2.2. d) Prefer external peers */ if (pri_bgp->is_interior != sec_bgp->is_interior) return 0; ``` But after checking IGP metric, the remaining criteria are ignored: ```c /* Remaining criteria are ignored */ return 1; ``` I did not find a check equivalent to: - for iBGP: full AS_PATH contents must match, or - for eBGP: neighboring AS must match. ## Comparison with FRR FRR explicitly distinguishes relaxed and non-relaxed multipath behavior. In its default mode: ```c } else if (peer_new->sort == BGP_PEER_IBGP) { if (aspath_cmp(new->attr->aspath, exist->attr->aspath)) { *paths_eq = 1; } } else if (peer_new->as == peer_exist->as) { *paths_eq = 1; } ``` FRR only allows different AS_PATH contents of the same length when the explicit AS-path multipath relax flag is configured: ```c } else if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { *paths_eq = 1; } ``` This makes the relaxed behavior visible to operators as an intentional configuration choice. ## Impact When `merge paths` is enabled, BIRD may export ECMP routes whose contributing BGP paths would not be considered multipath-equivalent by implementations such as FRR unless an explicit AS-path multipath-relax option is enabled. Potential consequences include: 1. Traffic sharing across different upstream ASes when the operator expected same-AS eBGP multipath only. 2. Traffic sharing across iBGP paths that have different AS_PATH contents. 3. Policy surprises when one AS_PATH would normally be considered non-equivalent to another even though their lengths match. 4. Interoperability differences compared with implementations such as FRR that require explicit AS-path multipath relaxation. This impact is configuration-dependent. It requires `merge paths` or equivalent multipath export behavior to be enabled. It is not a session-reset issue and is not being reported as a security vulnerability. ## Suggested Fix or Clarification If the current behavior is unintentional, BIRD could make `bgp_rte_mergable()` stricter by default: 1. For iBGP routes, require full AS_PATH equality before merging. 2. For eBGP routes, require the same neighboring AS before merging. 3. Optionally add a configuration knob to allow the current relaxed behavior explicitly, similar to FRR's `bgp bestpath as-path multipath-relax`. If the current behavior is intentional, documenting that `merge paths` may merge BGP routes with different AS_PATH contents or different eBGP neighboring ASes when AS_PATH lengths are equal would help operators understand the policy impact. In other words, this may simply be intentional relaxed multipath behavior. I am reporting it because the documentation says "equivalent non-best routes" but does not appear to define that equivalence as AS_PATH-length-based rather than AS_PATH-content-based or same-neighboring-AS-based. Best regards.
On Tue, Jul 07, 2026 at 07:03:38PM +0800, xinzhe liu via Bird-users wrote:
Hello BIRD maintainers,
I would like to ask for clarification about BIRD's BGP `merge paths` equivalence rules.
BIRD 2.19.0+ appears to use an AS_PATH-length-based equivalence check for BGP routes when kernel `merge paths` is enabled. In the reproduced cases, BIRD exported one ECMP route from two BGP paths even though:
1. The two eBGP paths came from different neighboring ASes, and their AS_PATH contents were different. 2. The two iBGP paths had different AS_PATH contents.
The common condition was that both paths had the same AS_PATH length and otherwise equal attributes. This looks similar to AS-path multipath-relax behavior in implementations such as FRR, but in FRR this relaxed behavior is enabled by an explicit `bgp bestpath as-path multipath-relax` option. FRR's default multipath behavior is stricter: eBGP multipath requires the same remote AS, and iBGP multipath requires matching AS_PATH contents.
Hello Yes, this is expected behavior. The 'merge paths' option merges paths where one is not clearly better than other, regardless of specifics of their AS paths. It would make sense to have better configuration granularity for this, but it is low on our todo list. -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) "To err is human -- to blame it on a computer is even more so."
participants (2)
-
Ondrej Zajicek -
xinzhe liu