Skip to content

bgpd: fix BGP_ATTR_NEXT_HOP flag handling in bgp_attr_default_set() - #21166

Merged
riw777 merged 1 commit into
FRRouting:masterfrom
enkechen-panw:network-nexthop
Mar 18, 2026
Merged

bgpd: fix BGP_ATTR_NEXT_HOP flag handling in bgp_attr_default_set()#21166
riw777 merged 1 commit into
FRRouting:masterfrom
enkechen-panw:network-nexthop

Conversation

@enkechen-panw

@enkechen-panw enkechen-panw commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

bgp_attr_default_set() unconditionally set the BGP_ATTR_NEXT_HOP flag
on every call, even though attr.nexthop (the IPv4 address field) is
all-zeros and not yet assigned. This flag is used by
BGP_ATTR_NEXTHOP_AFI_IP6 to distinguish IPv4 vs IPv6 nexthops, so
having it always set caused non-IPv4 routes to be misidentified.
Callers were working around this by manually calling UNSET_FLAG for
non-IPv4 cases, which was fragile and error-prone.

Remove the unconditional flag from bgp_attr_default_set() and enforce
the invariant that BGP_ATTR_NEXT_HOP is set where and only where
attr.nexthop is assigned as an actual IPv4 nexthop.

@greptile-apps

greptile-apps Bot commented Mar 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a bug in bgp_static_update() where the BGP_ATTR_NEXT_HOP flag was not being cleared for non-IPv4 address families. The bgp_attr_default_set() function unconditionally sets this flag, but the BGP_ATTR_NEXTHOP_AFI_IP6 macro relies on it being unset to correctly identify IPv6 nexthops. Without this fix, IPv6 static routes could be misidentified as having IPv4 nexthops, affecting downstream consumers like SNMP, EVPN nexthop conversion, and MPLS VPN route import/export.

  • Adds an else branch to clear BGP_ATTR_NEXT_HOP flag when afi != AFI_IP in bgp_static_update(), consistent with the existing pattern in bgp_redistribute_add() at line 10595-10596 of the same file
  • The fix is minimal and targeted β€” only the necessary flag is cleared for non-IPv4 address families

Confidence Score: 5/5

  • This PR is safe to merge β€” it is a small, well-understood bug fix that follows an existing pattern in the same file.
  • The change is a 2-line addition that mirrors an identical pattern already present in bgp_redistribute_add(). The BGP_ATTR_NEXT_HOP flag semantics are well-established in the codebase and the fix correctly addresses the documented issue with BGP_ATTR_NEXTHOP_AFI_IP6 macro behavior.
  • No files require special attention.

Important Files Changed

Filename Overview
bgpd/bgp_route.c Adds UNSET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) for non-IPv4 AFIs in bgp_static_update(), fixing incorrect nexthop AFI detection via BGP_ATTR_NEXTHOP_AFI_IP6 macro. Mirrors the existing pattern in bgp_redistribute_add().

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["bgp_static_update()"] --> B["bgp_attr_default_set()"]
    B --> C["Sets BGP_ATTR_NEXT_HOP flag"]
    C --> D{"afi == AFI_IP?"}
    D -->|Yes| E["nh_length = IPV4_MAX_BYTELEN\n(flag remains set)"]
    D -->|No| F["UNSET BGP_ATTR_NEXT_HOP flag\n(NEW: this PR)"]
    E --> G["Build NHC and set attributes"]
    F --> G
    G --> H["BGP_ATTR_NEXTHOP_AFI_IP6 macro"]
    H --> I{"flag unset AND\nmp_nexthop_len is IPv6?"}
    I -->|Yes| J["Treat as IPv6 nexthop"]
    I -->|No| K["Treat as IPv4 nexthop"]
Loading

Last reviewed commit: 93458ff

@donaldsharp

Copy link
Copy Markdown
Member

This immediately implies that the usage of bgp_attr_default_set is a problem throughout our code base. I personally would like to have a way that this function can be used properly without having to figure out how to properly set this flag after the call. Otherwise we are just setting ourselves up for more bugs in the future( and we probably already have something wrong somewhere with all the other usages ). Why are we not fixing that instead of just hoping we get it right everywhere else?

@enkechen-panw

Copy link
Copy Markdown
Contributor Author

This immediately implies that the usage of bgp_attr_default_set is a problem throughout our code base. I personally would like to have a way that this function can be used properly without having to figure out how to properly set this flag after the call. Otherwise we are just setting ourselves up for more bugs in the future( and we probably already have something wrong somewhere with all the other usages ). Why are we not fixing that instead of just hoping we get it right everywhere else?

That's a good point, thanks. Let me investigate.

bgp_attr_default_set() unconditionally set the BGP_ATTR_NEXT_HOP flag
on every call, even though attr.nexthop (the IPv4 address field) is
all-zeros and not yet assigned. This flag is used by
BGP_ATTR_NEXTHOP_AFI_IP6 to distinguish IPv4 vs IPv6 nexthops, so
having it always set caused non-IPv4 routes to be misidentified.
Callers were working around this by manually calling UNSET_FLAG for
non-IPv4 cases, which was fragile and error-prone.

Remove the unconditional flag from bgp_attr_default_set() and enforce
the invariant that BGP_ATTR_NEXT_HOP is set where and only where
attr.nexthop is assigned as an actual IPv4 nexthop:

- bgp_evpn_vtep_ip_to_attr_nh(): set the flag alongside attr->nexthop
  for IPv4 VTEPs, covering all EVPN call sites through this helper.
- bgp_evpn_fill_rmac_nh_to_attr(): set the flag in both IPv4 nexthop
  assignment paths (anycast-IP and PIP).
- bgp_static_update(): set the flag explicitly for AFI_IP; remove the
  UNSET_FLAG workaround from the else branch.
- bgp_redistribute_add(): set the flag in all three IPv4 nexthop cases
  (NEXTHOP_TYPE_IFINDEX/IPv4, NEXTHOP_TYPE_IPV4[_IFINDEX],
  NEXTHOP_TYPE_BLACKHOLE/IPv4); remove the blanket UNSET_FLAG workaround.
- subgroup_default_originate(): set the flag for the IPv4
  default-originate path.

Signed-off-by: Enke Chen <enchen@paloaltonetworks.com>
@github-actions github-actions Bot added size/S and removed size/XS labels Mar 16, 2026
@enkechen-panw enkechen-panw changed the title bgpd: fix attribute flag BGP_ATTR_NEXT_HOP in bgp_static_update() bgpd: fix BGP_ATTR_NEXT_HOP flag handling in bgp_attr_default_set() Mar 16, 2026

@ton31337 ton31337 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@riw777 riw777 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@riw777
riw777 merged commit 10498c0 into FRRouting:master Mar 18, 2026
19 checks passed
@enkechen-panw
enkechen-panw deleted the network-nexthop branch March 18, 2026 16:58
kaffarell added a commit to kaffarell/frr that referenced this pull request May 15, 2026
When importing an EVPN route into a VRF unicast table,
install_evpn_route_entry_in_vrf() converted every imported IPv4 route
into a route with the legacy IPv4 NEXT_HOP attribute set:

    attr.nexthop = attr.mp_nexthop_global_in;
    SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP));

This is only valid when the imported EVPN nexthop is IPv4. With IPv6
VTEPs we can get IPv4 prefixes with IPv6 nexthops and the route already
has the real nexthop encoded in the MP nexthop fields. In that case
setting BGP_ATTR_NEXT_HOP creates an inconsistent attribute: the route
has an IPv6 MP nexthop, but is also marked as having a classic IPv4
NEXT_HOP.

This breaks code that uses BGP_ATTR_NEXTHOP_AFI_IP6() to determine
the nexthop address family. BGP_ATTR_NEXTHOP_AFI_IP6() sees
BGP_ATTR_NEXT_HOP and thinks this is a IPv4 route with a IPv4 nexthop
even though mp_nexthop_len indicates an IPv6 nexthop. The result is that
VRF import/leak drops the IPv6 nexthop and sends a 0.0.0.0 nexthop to
zebra.

Fix this by only assigning attr.nexthop and setting BGP_ATTR_NEXT_HOP
when the imported EVPN route does not have an IPv6 MP nexthop. EVPN IPv4
routes with IPv6 nexthops are left as MP-nexthop routes.

This is related to the previous BGP_ATTR_NEXT_HOP cleanup (FRRouting#21166) and
was probably missed there.

Also make the nexthop-change detection handle this case by comparing the
MP IPv6 nexthop for IPv4 routes that carry one.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
kaffarell added a commit to kaffarell/frr that referenced this pull request May 15, 2026
When importing an EVPN route into a VRF unicast table,
install_evpn_route_entry_in_vrf() converted every imported IPv4 route
into a route with the legacy IPv4 NEXT_HOP attribute set:

    attr.nexthop = attr.mp_nexthop_global_in;
    SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP));

This is only valid when the imported EVPN nexthop is IPv4. With IPv6
VTEPs we can get IPv4 prefixes with IPv6 nexthops and the route already
has the real nexthop encoded in the MP nexthop fields. In that case
setting BGP_ATTR_NEXT_HOP creates an inconsistent attribute: the route
has an IPv6 MP nexthop, but is also marked as having a classic IPv4
NEXT_HOP.

This breaks code that uses BGP_ATTR_NEXTHOP_AFI_IP6() to determine
the nexthop address family. BGP_ATTR_NEXTHOP_AFI_IP6() sees
BGP_ATTR_NEXT_HOP and thinks this is a IPv4 route with a IPv4 nexthop
even though mp_nexthop_len indicates an IPv6 nexthop. The result is that
VRF import/leak drops the IPv6 nexthop and sends a 0.0.0.0 nexthop to
zebra.

Fix this by only assigning attr.nexthop and setting BGP_ATTR_NEXT_HOP
when the imported EVPN route does not have an IPv6 MP nexthop. EVPN IPv4
routes with IPv6 nexthops are left as MP-nexthop routes.

This is related to the previous BGP_ATTR_NEXT_HOP cleanup (FRRouting#21166) and
was probably missed there.

Also make the nexthop-change detection handle this case by comparing the
MP IPv6 nexthop for IPv4 routes that carry one.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
kaffarell added a commit to kaffarell/frr that referenced this pull request May 18, 2026
When importing an EVPN route into a VRF unicast table,
install_evpn_route_entry_in_vrf() converted every imported IPv4 route
into a route with the legacy IPv4 NEXT_HOP attribute set:

    attr.nexthop = attr.mp_nexthop_global_in;
    SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP));

This is only valid when the imported EVPN nexthop is IPv4. With IPv6
VTEPs we can get IPv4 prefixes with IPv6 nexthops and the route already
has the real nexthop encoded in the MP nexthop fields. In that case
setting BGP_ATTR_NEXT_HOP creates an inconsistent attribute: the route
has an IPv6 MP nexthop, but is also marked as having a classic IPv4
NEXT_HOP.

This breaks code that uses BGP_ATTR_NEXTHOP_AFI_IP6() to determine
the nexthop address family. BGP_ATTR_NEXTHOP_AFI_IP6() sees
BGP_ATTR_NEXT_HOP and thinks this is a IPv4 route with a IPv4 nexthop
even though mp_nexthop_len indicates an IPv6 nexthop. The result is that
VRF import/leak drops the IPv6 nexthop and sends a 0.0.0.0 nexthop to
zebra.

Fix this by only assigning attr.nexthop and setting BGP_ATTR_NEXT_HOP
when the imported EVPN route does not have an IPv6 MP nexthop. EVPN IPv4
routes with IPv6 nexthops are left as MP-nexthop routes.

This is related to the previous BGP_ATTR_NEXT_HOP cleanup (FRRouting#21166) and
was probably missed there.

Also make the nexthop-change detection handle this case by comparing the
MP IPv6 nexthop for IPv4 routes that carry one.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants