Skip to content

bgpd: anchor parsed attr in bgp_nlri_parse_vpn to preserve srv6_l3service (backport #22492)#22620

Open
mergify[bot] wants to merge 2 commits into
stable/10.7from
mergify/bp/stable/10.7/pr-22492
Open

bgpd: anchor parsed attr in bgp_nlri_parse_vpn to preserve srv6_l3service (backport #22492)#22620
mergify[bot] wants to merge 2 commits into
stable/10.7from
mergify/bp/stable/10.7/pr-22492

Conversation

@mergify

@mergify mergify Bot commented Jul 9, 2026

Copy link
Copy Markdown

Symptom

On a PE receiving a BGP UPDATE that carries multiple VPN NLRIs sharing one parsed attribute set, only 1 of N prefixes gets installed in the importing VRF outing table. show bgp sum shows PfxRcd N (all received) but the VRF RIB ends up with a single entry. We see the following outputs:

PE3# show bgp sum
IPv4 VPN Summary:
BGP router identifier 100.1.0.31, local AS number 64602 VRF default vrf-id 0
BGP table version 0
RIB entries 1, using 160 bytes of memory
Peers 2, using 60 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
2064:100::1d    4      64600       134       134       20    0    0 00:21:28           10       20 PE1
2064:200::1e    4      64601       134       134       20    0    0 00:21:28           10       20 PE2

Total number of neighbors 2

IPv6 Unicast Summary:
BGP router identifier 100.1.0.31, local AS number 64602 VRF default vrf-id 0
BGP table version 47
RIB entries 26, using 4160 bytes of memory
Peers 2, using 60 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
fc06::2         4      65103       172       157       47    0    0 00:20:55           22       26 P4
fc08::2         4      65102       165       156       47    0    0 00:21:29           22       26 P2

Total number of neighbors 2
PE3# show ip route vrf Vrf1
Codes: K - kernel route, C - connected, L - local, S - static,
       R - RIP, O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
       f - OpenFabric, t - Table-Direct,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup
       t - trapped, o - offload failure

IPv4 unicast VRF Vrf1:
C>* 10.10.246.0/24 is directly connected, Ethernet24, weight 1, 00:21:48
L>* 10.10.246.31/32 is directly connected, Ethernet24, weight 1, 00:21:48
B> 192.100.0.1/32 [20/0] via 2064:100::1d (vrf default) (recursive), label 16, seg6 fd00:201:201:1::, weight 1, 00:07:17
                           via fc06::2, Ethernet12 (vrf default), label 16, seg6 fd00:201:201:1::, weight 1, 00:07:17
                           via fc08::2, Ethernet4 (vrf default), label 16, seg6 fd00:201:201:1::, weight 1, 00:07:17
                         via 2064:200::1e (vrf default) (recursive), label 32, seg6 fd00:202:202:2::, weight 1, 00:07:17
                           via fc06::2, Ethernet12 (vrf default), label 32, seg6 fd00:202:202:2::, weight 1, 00:07:17
                           via fc08::2, Ethernet4 (vrf default), label 32, seg6 fd00:202:202:2::, weight 1, 00:07:17

Root cause

Commit 862af5d ("bgpd: Move srv6_l3service from attr to attr_extra") moved srv6_l3service from a direct field of struct attr into the heap-allocated attr->extra container. attr->extra follows ownership-transfer semantics: bgp_attr_hash_alloc() transfers extra out of attrs that bgp_attr_owns_extra() considers transient (owned, not self-anchored).
In bgp_nlri_parse_vpn(), a single BGP UPDATE carries N NLRIs sharing one parsed parent attr. Without the self-anchor:

  • extra → non-NULL (contains srv6_l3service)
  • refcnt → 0 (not yet interned)
  • parsed_attr → NULL (never set)

so bgp_attr_owns_extra() returns true on the first prefix's bgp_attr_intern() call. bgp_attr_hash_alloc() then takes val->extra and sets it to NULL. Because val was the result of bgp_attr_dup_into(&new_attr, parent_attr) and the parent's extra was shallow-copied, the parent attr's extra is now effectively gone. Prefixes 2..N see extra == NULL → no srv6_l3service → no SRv6 SID → they either fail VPN-to-VRF leak or collapse onto the same RIB entry.

bgp_nlri_parse_ip() already sets attr->attr_intern_reuse.parsed_attr = attr, so bgp_attr_owns_extra() returns false there → the deep-copy branch runs → every NLRI gets its own extra. The VPN path was simply missing the same guard.

Fix

Mirror the self-anchor pattern from bgp_nlri_parse_ip() in bgp_nlri_parse_vpn(): seed attr->attr_intern_reuse.parsed_attr = attr before the prefix loop and reset the cache on the done: exit. if (attr) guards the withdraw path.

Topotest

tests/topotests/bgp_srv6l3vpn_multi_nlri/:
ce --- pe1 --- pe2

  • pe1 has a VRF interface connecting to ce
  • eBGP between ce and pe1; ce publishes 10 IPv4 /32 routes to pe1
  • eBGP between pe1 and pe2 (interface peering), carrying the VPN address family so pe1 redistributes the 10 routes to pe2

Two assertions on PE2 after convergence:

  • test_multi_nlri_vpn_rib — show bgp ipv4 vpn json must list all 10 prefixes as valid
  • test_multi_nlri_vrf_rib — show ip route vrf Vrf1 json must contain all 10 BGP-installed prefixes (without the fix only 192.100.0.1/32 lands here)

Without the fix: test_multi_nlri_vrf_rib FAILS, only the first prefix installed in the VRF.
With the fix: both PASS.

Commits

…vice

The MPLS VPN NLRI parser passes the same parent attr through the loop
to bgp_update() once per prefix.
After commit 862af5d("bgpd: Move srv6_l3service from attr to attr_extra")
moved srv6_l3service into attr->extra, multiple downstream paths use
bgp_attr_owns_extra() to decide whether to discard or transfer
attr->extra. That check returns true when parsed_attr != self, which
is the default (zeroed) state for the parent attr in this function.

bgp_nlri_parse_ip already self-anchors attr->attr_intern_reuse.parsed_attr
to attr at the top of the NLRI loop and clears it at the bottom.
Mirror the same pattern here so the parent attr's extra (containing srv6_l3service)
is preserved across all NLRIs in a single UPDATE.

Without this, only the first prefix in a multi-NLRI VPN UPDATE retains
the SRv6 SID; subsequent prefixes lose it and end up missing in the
importing VRF, which prevents correct VPN route install.

Signed-off-by: Yuqing Zhao <galadriel.zhao@alibaba-inc.com>
(cherry picked from commit d2a0fbc)
Reproduce the case where a single BGP UPDATE carries multiple VPN NLRIs
sharing one parsed attribute set. Without the companion fix, only the
first prefix keeps its srv6_l3service after bgp_attr_intern() and the
rest lose their SRv6 SID context, so VPN-to-VRF leak collapses 10
prefixes onto a single VRF entry on PE2.

Topology: ce -- pe1 -- pe2, eBGP between pe1/pe2 carrying vpnv4 over
IPv6 next-hop with SRv6 per-VRF SIDs. CE injects 10 IPv4 /32 prefixes;
PE2 must end up with all 10 in VRF Vrf1's RIB.

Without the fix:
- test_multi_nlri_vpn_rib  PASSES (BGP VPN table sees all 10)
- test_multi_nlri_vrf_rib  FAILS (only 192.100.0.1/32 lands in zebra)

This test verifies the correctness of the fix.

Signed-off-by: Yuqing Zhao <galadriel.zhao@alibaba-inc.com>
(cherry picked from commit af3f9cf)
@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Target branch is not in the allowed branches list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bgp size/L stable/10.7 tests Topotests, make check, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant