Commit edc3b7cdc9 for frr

commit edc3b7cdc913a2d652553f41fae75554b2356ef0
Author: Yuqing Zhao <galadriel.zyq@alibaba-inc.com>
Date:   Mon Sep 14 19:04:13 2026 +0800

    zebra: Add resolved-via attribute to fpm_listener dump

    FPM nexthop-group messages can carry the NHA_FPM_RESOLVED_VIA
    attribute, the id of the nexthop group that resolved a nexthop.
    The fpm_listener's attribute table only covered the kernel's NHA_*
    values, so the attribute was treated as unknown and dropped.

    Decode and store the attribute, and print it in the SIGUSR1 dump as
    ", ResolvedVia: %u", so the FPM topotests can verify the values zebra
    sends over the FPM pipe.

    Signed-off-by: Yuqing Zhao <galadriel.zyq@alibaba-inc.com>

diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c
index fa802b2007..ceb219ace1 100644
--- a/zebra/fpm_listener.c
+++ b/zebra/fpm_listener.c
@@ -85,6 +85,8 @@ struct fpm_nhg {
 	uint8_t scope;	      /* Scope */
 	bool is_blackhole;    /* Is this a blackhole nexthop? */
 	uint8_t num_nexthops; /* Number of nexthops in the group */
+	/* Resolving nexthop group ID, if the resolved-via attr was received */
+	uint32_t resolved_via;

 	/* Individual nexthops in the group */
 	struct {
@@ -657,7 +659,7 @@ static void handle_nexthop_update(struct nlmsghdr *hdr, struct nhmsg *nhmsg, str
 static int parse_nexthop_msg(struct nlmsghdr *hdr)
 {
 	struct nhmsg *nhmsg;
-	struct rtattr *tb[NHA_MAX + 1] = {};
+	struct rtattr *tb[NHA_FPM_MAX] = {};
 	int len;
 	uint32_t nhgid = 0;
 	uint16_t nhg_count = 0;
@@ -936,6 +938,7 @@ static void handle_nexthop_update(struct nlmsghdr *hdr, struct nhmsg *nhmsg, str
 	struct fpm_nhg *existing;
 	struct fpm_nhg lookup = { 0 };
 	uint32_t nhgid = 0;
+	uint32_t resolved_via = 0;
 	uint16_t nhg_count = 0;

 	/* Get Nexthop Group ID */
@@ -944,6 +947,11 @@ static void handle_nexthop_update(struct nlmsghdr *hdr, struct nhmsg *nhmsg, str
 	else
 		return; /* Can't process without an ID */

+	/* Get resolving nexthop group ID, if present */
+	if (tb[NHA_FPM_RESOLVED_VIA] &&
+	    RTA_PAYLOAD(tb[NHA_FPM_RESOLVED_VIA]) >= sizeof(resolved_via))
+		resolved_via = *(uint32_t *)RTA_DATA(tb[NHA_FPM_RESOLVED_VIA]);
+
 	/* Count nexthops in the group */
 	if (tb[NHA_GROUP]) {
 		size_t count = (RTA_PAYLOAD(tb[NHA_GROUP]) / sizeof(struct nexthop_group));
@@ -971,6 +979,7 @@ static void handle_nexthop_update(struct nlmsghdr *hdr, struct nhmsg *nhmsg, str
 			existing->scope = nhmsg->nh_scope;
 			existing->is_blackhole = tb[NHA_BLACKHOLE] ? true : false;
 			existing->num_nexthops = nhg_count;
+			existing->resolved_via = resolved_via;

 			/* Update individual nexthop IDs and weights */
 			if (tb[NHA_GROUP]) {
@@ -999,6 +1008,7 @@ static void handle_nexthop_update(struct nlmsghdr *hdr, struct nhmsg *nhmsg, str
 			nhg->scope = nhmsg->nh_scope;
 			nhg->is_blackhole = tb[NHA_BLACKHOLE] ? true : false;
 			nhg->num_nexthops = nhg_count;
+			nhg->resolved_via = resolved_via;

 			/* Store individual nexthop IDs and weights */
 			if (tb[NHA_GROUP]) {
@@ -1159,6 +1169,9 @@ static void sigusr1_handler(int signum)
 			nhg->num_nexthops ? nhg->num_nexthops : 1,
 			nhg->is_blackhole ? "BLACKHOLE" : "");

+		if (nhg->resolved_via)
+			fprintf(out, ", ResolvedVia: %u", nhg->resolved_via);
+
 		/* Display individual nexthops if any */
 		if (nhg->num_nexthops > 0 && !nhg->is_blackhole) {
 			if (nhg->nexthops[0].id == 0) {