bgpd: fix BGP_ATTR_NEXT_HOP flag handling in bgp_attr_default_set() - #21166
Conversation
Greptile SummaryThis PR fixes a bug in
Confidence Score: 5/5
Important Files Changed
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"]
Last reviewed commit: 93458ff |
|
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>
93458ff to
d33a1dd
Compare
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>
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>
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>
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.