Commit 5e95e2dba0 for frr

commit 5e95e2dba0196f4baf0f562dc0cbdcdf56b745f5
Author: Johannes Gezachew <yohannis995@gmail.com>
Date:   Sat Sep 12 14:27:43 2026 +0300

    zebra: keep numMacs out of macs in evpn mac json output

    `show evpn mac vni all [detail] json` builds each VNI object with the
    "macs" container, held open for incremental output, followed by the
    "numMacs" scalar. The MAC walk flushes the top-level object each time
    more than 50 MACs have been added. When that flush happens in the
    middle of a VNI, frr_json_obj_to_vty() prints the open "macs"
    container without closing it and then prints its "numMacs" sibling,
    so "numMacs" ends up inside "macs" in the output.

    Add "numMacs" to the VNI object before "macs", so it is printed ahead
    of the open container regardless of where the flush happens.

    Reported in: #23273

    Signed-off-by: Johannes Gezachew <yohannis995@gmail.com>

diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 07935eeff2..dba381ca0b 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -465,20 +465,21 @@ static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt
 		frr_json_set_open(json_evpn);
 		frr_json_set_open(json_mac);

+		/* Add numMacs before the open "macs" container: an incremental
+		 * flush during the MAC walk would otherwise print it inside "macs".
+		 */
+		if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP))
+			json_object_int_add(json_evpn, "numMacs", num_macs);
 		json_object_object_add(json_evpn, "macs", json_mac);
 		json_object_object_add(json, vni_str, json_evpn);
 	}

-	if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
-		if (json == NULL) {
-			vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
-				zevpn->vni, num_macs);
-			vty_out(vty,
-				"Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n");
-			vty_out(vty, "%-17s %-6s %-5s %-39s %-5s %s\n", "MAC", "Type", "Flags",
-				"Intf/Remote ES/VTEP", "VLAN", "Seq #'s");
-		} else
-			json_object_int_add(json_evpn, "numMacs", num_macs);
+	if (json == NULL && !CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
+		vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", zevpn->vni, num_macs);
+		vty_out(vty,
+			"Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n");
+		vty_out(vty, "%-17s %-6s %-5s %-39s %-5s %s\n", "MAC", "Type", "Flags",
+			"Intf/Remote ES/VTEP", "VLAN", "Seq #'s");
 	}

 	if (!num_macs) {
@@ -559,17 +560,17 @@ static void zevpn_print_mac_hash_all_evpn_detail(struct hash_bucket *bucket,
 		frr_json_set_open(json_evpn);
 		frr_json_set_open(json_mac);

+		/* Add numMacs before the open "macs" container: an incremental
+		 * flush during the MAC walk would otherwise print it inside "macs".
+		 */
+		if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP))
+			json_object_int_add(json_evpn, "numMacs", num_macs);
 		json_object_object_add(json_evpn, "macs", json_mac);
 		json_object_object_add(json, vni_str, json_evpn);
 	}

-	if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
-		if (json == NULL) {
-			vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
-				zevpn->vni, num_macs);
-		} else
-			json_object_int_add(json_evpn, "numMacs", num_macs);
-	}
+	if (json == NULL && !CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP))
+		vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", zevpn->vni, num_macs);
 	/* assign per-evpn to wctx->json object to fill macs
 	 * under the evpn. Re-assign primary json object to fill
 	 * next evpn information.