Commit d3dfbc25b4 for frr

commit d3dfbc25b4bb85b63bd950d22bdf44325da24d23
Author: Philippe Guibert <philippe.guibert@6wind.com>
Date:   Mon Oct 12 16:11:55 2026 +0200

    bgpd: keep the aigp set by an outbound route-map

    The AIGP metric set by an outbound route-map is not advertised when the
    nexthop is changed. In bgp_aigp, r6 receives 10.0.0.71/32 from r7 with
    an AIGP metric of 71, on a connected link. Add 'set aigp-metric 100' to
    the outbound route-map of r6 towards r4:

    > route-map set-nexthop permit 10
    >  set ip next-hop peer-address
    >  set aigp-metric 100

    r4 still receives the AIGP metric of r7:

    > r4# show bgp ipv4 unicast 10.0.0.71/32
    > BGP routing table entry for 10.0.0.71/32, version 8
    > Paths: (1 available, best #1, table default)
    >   Advertised to peers:
    >   192.168.24.2
    >   Local
    >     10.0.0.6 (metric 20) from 10.0.0.6 (10.0.0.73)
    >       Origin incomplete, metric 71, localpref 100, aigp-metric 71, valid, internal, best (First path received)
    >       Originator: 10.0.0.73, Cluster list: 10.0.6.6
    >       Last update: Mon Nov  9 10:40:13 2026

    RFC 7311 section 3.4.3 requires that a speaker which changes the
    nexthop increases the AIGP metric by a non-zero distance to the previous
    nexthop, either the IGP distance or a value set by policy. On a
    connected link, the IGP metric to the nexthop is 0, so the policy is the
    only way to increase it.

    subgroup_announce_check() applies the outbound route-map, then, as the
    nexthop is changed, overwrites the AIGP metric of the advertised
    attribute with bgp_aigp_metric_total(). This function reads the AIGP
    metric of the received path (71), and not the one set by the route-map
    (100), then adds the IGP metric to the nexthop (0).

    Fix this by passing the attribute to use to bgp_aigp_metric_total().
    subgroup_announce_check() passes the attribute modified by the outbound
    route-map, and r4 receives 100. The best path selection still uses the
    attribute of the path.

    Fixes: fc82d7750f3c ("bgpd: fix AIGP calculation in route advertisement")
    Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>

diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 88e1877dd0..30093c59bf 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -752,9 +752,9 @@ static inline uint64_t bgp_attr_get_aigp_metric(const struct attr *attr)
 extern void bgp_attr_set_aigp_metric(struct attr *attr, uint64_t aigp);
 extern void bgp_attr_unset_aigp_metric(struct attr *attr);

-static inline uint64_t bgp_aigp_metric_total(struct bgp_path_info *bpi)
+static inline uint64_t bgp_aigp_metric_total(struct bgp_path_info *bpi, const struct attr *attr)
 {
-	uint64_t aigp = bgp_attr_get_aigp_metric(bpi->attr);
+	uint64_t aigp = bgp_attr_get_aigp_metric(attr);

 	/* Don't increment if it's locally sourced */
 	if (bpi->peer == bpi->peer->bgp->peer_self)
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 3a515be972..b072ce96a9 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1419,8 +1419,8 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
 	/* 4. Tie-breaker - AIGP (Metric TLV) attribute */
 	if (bgp_attr_exists(newattr, BGP_ATTR_AIGP) && bgp_attr_exists(existattr, BGP_ATTR_AIGP) &&
 	    CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) {
-		uint64_t new_aigp = bgp_aigp_metric_total(new);
-		uint64_t exist_aigp = bgp_aigp_metric_total(exist);
+		uint64_t new_aigp = bgp_aigp_metric_total(new, new->attr);
+		uint64_t exist_aigp = bgp_aigp_metric_total(exist, exist->attr);

 		if (new_aigp < exist_aigp) {
 			*reason = bgp_path_selection_aigp;
@@ -3378,7 +3378,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
 		if (AIGP_TRANSMIT_ALLOWED(peer)) {
 			if (nh_reset ||
 			    CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS)) {
-				uint64_t aigp = bgp_aigp_metric_total(pi);
+				uint64_t aigp = bgp_aigp_metric_total(pi, attr);

 				bgp_attr_set_aigp_metric(attr, aigp);
 			}