bgpd: fix IPv6 nexthop for IPv4 peers to use IPv4-mapped address#22168
bgpd: fix IPv6 nexthop for IPv4 peers to use IPv4-mapped address#22168Jay779 wants to merge 1 commit into
Conversation
Greptile SummaryThis PR fixes a send-side bug in RFC 5549 (IPv4 BGP peer advertising IPv6 prefixes) where an interface with both a global IPv6 address and an IPv4 address would produce UPDATE messages carrying the wrong nexthop (
Confidence Score: 3/5The fix correctly addresses the described nexthop mismatch, but calls The EBGP multiaccess-check and route-map NH_PEER_ADDRESS branches redirect bgpd/bgp_updgrp_packet.c — the interaction between the new branch and the preceding Important Files Changed
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
bgpd/bgp_updgrp_packet.c:534-540
When the EBGP multiaccess-check branch (lines 517-527) or the route-map `NH_PEER_ADDRESS` branch (lines 508-512) fires before this point, `mod_v6nhg` has already been redirected to `&peer->nexthop.v6_global`. Calling `ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, ...)` then permanently overwrites that per-session field in place. `peer->nexthop.v6_global` is written once at session establishment by `bgp_zebra_nexthop_set()` and is later read by the update-group hash (`bgp_updgrp.c:473`) and update-group equality check (`bgp_updgrp.c:695`). Mutating it mid-send silently corrupts those lookups for the rest of the session. The fix should always target the stack-local `v6nhglobal` and reset the pointer, keeping the peer structure untouched.
```suggestion
if (peer->su_local &&
peer->su_local->sa.sa_family == AF_INET &&
peer->nexthop.v4.s_addr != INADDR_ANY) {
ipv4_to_ipv4_mapped_ipv6(&v6nhglobal,
peer->nexthop.v4);
mod_v6nhg = &v6nhglobal;
gnh_modified = 1;
} else if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) {
```
Reviews (1): Last reviewed commit: "bgpd: fix IPv6 nexthop for IPv4 peers to..." | Re-trigger Greptile |
| if (peer->su_local && | ||
| peer->su_local->sa.sa_family == AF_INET && | ||
| peer->nexthop.v4.s_addr != INADDR_ANY) { | ||
| ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, | ||
| peer->nexthop.v4); | ||
| gnh_modified = 1; | ||
| } else if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) { |
There was a problem hiding this comment.
When the EBGP multiaccess-check branch (lines 517-527) or the route-map
NH_PEER_ADDRESS branch (lines 508-512) fires before this point, mod_v6nhg has already been redirected to &peer->nexthop.v6_global. Calling ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, ...) then permanently overwrites that per-session field in place. peer->nexthop.v6_global is written once at session establishment by bgp_zebra_nexthop_set() and is later read by the update-group hash (bgp_updgrp.c:473) and update-group equality check (bgp_updgrp.c:695). Mutating it mid-send silently corrupts those lookups for the rest of the session. The fix should always target the stack-local v6nhglobal and reset the pointer, keeping the peer structure untouched.
| if (peer->su_local && | |
| peer->su_local->sa.sa_family == AF_INET && | |
| peer->nexthop.v4.s_addr != INADDR_ANY) { | |
| ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, | |
| peer->nexthop.v4); | |
| gnh_modified = 1; | |
| } else if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) { | |
| if (peer->su_local && | |
| peer->su_local->sa.sa_family == AF_INET && | |
| peer->nexthop.v4.s_addr != INADDR_ANY) { | |
| ipv4_to_ipv4_mapped_ipv6(&v6nhglobal, | |
| peer->nexthop.v4); | |
| mod_v6nhg = &v6nhglobal; | |
| gnh_modified = 1; | |
| } else if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) { |
Prompt To Fix With AI
This is a comment left during a code review.
Path: bgpd/bgp_updgrp_packet.c
Line: 534-540
Comment:
When the EBGP multiaccess-check branch (lines 517-527) or the route-map `NH_PEER_ADDRESS` branch (lines 508-512) fires before this point, `mod_v6nhg` has already been redirected to `&peer->nexthop.v6_global`. Calling `ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, ...)` then permanently overwrites that per-session field in place. `peer->nexthop.v6_global` is written once at session establishment by `bgp_zebra_nexthop_set()` and is later read by the update-group hash (`bgp_updgrp.c:473`) and update-group equality check (`bgp_updgrp.c:695`). Mutating it mid-send silently corrupts those lookups for the rest of the session. The fix should always target the stack-local `v6nhglobal` and reset the pointer, keeping the peer structure untouched.
```suggestion
if (peer->su_local &&
peer->su_local->sa.sa_family == AF_INET &&
peer->nexthop.v4.s_addr != INADDR_ANY) {
ipv4_to_ipv4_mapped_ipv6(&v6nhglobal,
peer->nexthop.v4);
mod_v6nhg = &v6nhglobal;
gnh_modified = 1;
} else if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) {
```
How can I resolve this? If you propose a fix, please make it concise.When an IPv4 BGP peer advertises IPv6 prefixes (RFC 5549), the nexthop in UPDATE messages must be an IPv4-mapped IPv6 address (::ffff:x.x.x.x). Currently, bgp_zebra_nexthop_set() calls if_get_ipv6_global() which stores the first non-link-local IPv6 address found on the interface into peer->nexthop.v6_global. In bpacket_reformat_for_peer(), this value is used as the nexthop. The existing synthesis path (ipv4_to_ipv4_mapped_ipv6) only fires when mod_v6nhg is all-zeros, and the IS_MAPPED_IPV6 override only fires when v6_global is already IPv4-mapped. Neither triggers when v6_global contains a real global IPv6 address from the interface. This causes incorrect nexthops when the outgoing interface has both a global IPv6 address and an IPv4 address (e.g., a loopback with 2001:db8::1/128 and 172.32.0.13/32). The UPDATE is sent with the global IPv6 address instead of ::ffff:172.32.0.13, causing the receiver to fail recursive nexthop resolution in the IPv4 RIB. Fix this by always synthesizing the IPv4-mapped IPv6 nexthop from peer->nexthop.v4 when the peer connection is AF_INET, regardless of what v6_global contains. The existing fallback path for IPv6 peers is preserved as an else-if branch. Signed-off-by: Jia Chen <jchen1@paloaltonetworks.com>
fbe0c00 to
1a421c6
Compare
Summary
When an IPv4 BGP peer advertises IPv6 prefixes (RFC 5549), the nexthop in UPDATE messages must be an IPv4-mapped IPv6 address (
::ffff:x.x.x.x).Currently,
bgp_zebra_nexthop_set()callsif_get_ipv6_global()which stores the first non-link-local IPv6 address found on the interface intopeer->nexthop.v6_global. Inbpacket_reformat_for_peer(), the existing synthesis path (ipv4_to_ipv4_mapped_ipv6) only fires whenmod_v6nhgis all-zeros, and theIS_MAPPED_IPV6override only fires whenv6_globalis already IPv4-mapped. Neither triggers whenv6_globalcontains a real global IPv6 address from the interface.Result: When the outgoing interface has both a global IPv6 address and an IPv4 address (e.g., a loopback with
2001:db8::1/128and172.32.0.13/32), the UPDATE is sent with the wrong nexthop (the global IPv6 address instead of::ffff:172.32.0.13), causing the receiver to fail recursive nexthop resolution in the IPv4 RIB.Fix
Always synthesize the IPv4-mapped IPv6 nexthop from
peer->nexthop.v4when the peer connection isAF_INET, regardless of whatv6_globalcontains. The existing fallback path for IPv6 peers is preserved as an else-if branch.Receive side
Note that the receive side already correctly handles IPv4-mapped IPv6 nexthops:
bgp_find_or_add_nexthop()inbgp_nht.cdetectsIS_MAPPED_IPV6()and forcesAFI_IPfor resolutionmake_prefix()extracts the IPv4 address viaipv4_mapped_ipv6_to_ipv4()nexthop_active()also handles v4-mapped addresses correctlyOnly the send side was broken.
Signed-off-by: Jia Chen jchen1@paloaltonetworks.com