Commit b1bfb91799 for frr

commit b1bfb91799a02ad4e8400cc0e7d2083e93bc1750
Author: Donatas Abraitis <donatas@opensourcerouting.org>
Date:   Thu Oct 15 18:30:24 2026 +0300

    bgpd: Avoid changing interned attributes cached values

    We store X->str (aka human-readable string) for communities, as-path.

    The problem is that with the "show" commands we change X->str value while
    the attribute is already internet.

    With this change let's separate JSON and X->str caching as well, because
    if we need to update the JSON only, we shouldn't free/create X->str again.

    Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>

diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index cc9bf1ab76..a235a01a85 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -523,29 +523,15 @@ bool aspath_has_as4(struct aspath *aspath)
 }

 /* Convert aspath structure to string expression. */
-static void aspath_make_str_count(struct aspath *as, bool make_json)
+static void aspath_make_str_count(struct aspath *as)
 {
 	struct assegment *seg;
 	int str_size;
 	int len = 0;
 	char *str_buf;
-	json_object *jaspath_segments = NULL;
-	json_object *jseg = NULL;
-	json_object *jseg_list = NULL;
-
-	if (make_json) {
-		as->json = json_object_new_object();
-		jaspath_segments = json_object_new_array();
-	}

 	/* Empty aspath. */
 	if (!as->segments) {
-		if (make_json) {
-			json_object_string_add(as->json, "string", "Local");
-			json_object_object_add(as->json, "segments",
-					       jaspath_segments);
-			json_object_int_add(as->json, "length", 0);
-		}
 		as->str = XMALLOC(MTYPE_AS_STR, 1);
 		as->str[0] = '\0';
 		as->str_len = 0;
@@ -588,9 +574,6 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
 			XFREE(MTYPE_AS_STR, str_buf);
 			as->str = NULL;
 			as->str_len = 0;
-			json_object_free(as->json);
-			as->json = NULL;
-			json_object_free(jaspath_segments);
 			return;
 		}

@@ -616,14 +599,8 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
 				str_buf + len, str_size - len, "%c",
 				aspath_delimiter_char(seg->type, AS_SEG_START));

-		if (make_json)
-			jseg_list = json_object_new_array();
-
 		/* write out the ASNs, with their separators, bar the last one*/
 		for (i = 0; i < seg->length; i++) {
-			if (make_json)
-				asn_asn2json_array(jseg_list, seg->as[i],
-						   as->asnotation);
 			len += snprintfrr(str_buf + len, str_size - len,
 					  ASN_FORMAT(as->asnotation),
 					  &seg->as[i]);
@@ -633,15 +610,6 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
 						"%c", separator);
 		}

-		if (make_json) {
-			jseg = json_object_new_object();
-			json_object_string_add(
-				jseg, "type",
-				aspath_segment_type_str[seg->type]);
-			json_object_object_add(jseg, "list", jseg_list);
-			json_object_array_add(jaspath_segments, jseg);
-		}
-
 		if (seg->type != AS_SEQUENCE)
 			len += snprintf(
 				str_buf + len, str_size - len, "%c",
@@ -658,16 +626,10 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
 	as->str = str_buf;
 	as->str_len = len;

-	if (make_json) {
-		json_object_string_add(as->json, "string", str_buf);
-		json_object_object_add(as->json, "segments", jaspath_segments);
-		json_object_int_add(as->json, "length", aspath_count_hops(as));
-	}
-
 	return;
 }

-void aspath_str_update(struct aspath *as, bool make_json)
+void aspath_str_update(struct aspath *as)
 {
 	XFREE(MTYPE_AS_STR, as->str);

@@ -676,7 +638,38 @@ void aspath_str_update(struct aspath *as, bool make_json)
 		as->json = NULL;
 	}

-	aspath_make_str_count(as, make_json);
+	aspath_make_str_count(as);
+}
+
+/* Build the JSON representation, leaving as->str untouched. */
+void aspath_json_update(struct aspath *as)
+{
+	struct assegment *seg;
+	json_object *jaspath_segments;
+	json_object *jseg;
+	json_object *jseg_list;
+	int i;
+
+	if (as->json)
+		json_object_free(as->json);
+
+	as->json = json_object_new_object();
+	jaspath_segments = json_object_new_array();
+
+	for (seg = as->segments; seg; seg = seg->next) {
+		jseg_list = json_object_new_array();
+		for (i = 0; i < seg->length; i++)
+			asn_asn2json_array(jseg_list, seg->as[i], as->asnotation);
+
+		jseg = json_object_new_object();
+		json_object_string_add(jseg, "type", aspath_segment_type_str[seg->type]);
+		json_object_object_add(jseg, "list", jseg_list);
+		json_object_array_add(jaspath_segments, jseg);
+	}
+
+	json_object_string_add(as->json, "string", as->segments ? as->str : "Local");
+	json_object_object_add(as->json, "segments", jaspath_segments);
+	json_object_int_add(as->json, "length", aspath_count_hops(as));
 }

 /* Intern allocated AS path. */
@@ -930,7 +923,7 @@ static struct aspath *aspath_add_asns_rightmost(struct aspath *aspath, as_t asno
 		}
 	}

-	aspath_str_update(aspath, false);
+	aspath_str_update(aspath);
 	aspath->count = aspath_count_hops_internal(aspath);
 	return aspath;
 }
@@ -1146,7 +1139,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2)
 	}

 	assegment_normalise(aspath->segments);
-	aspath_str_update(aspath, false);
+	aspath_str_update(aspath);
 	aspath->count = aspath_count_hops_internal(aspath);

 	return aspath;
@@ -1301,7 +1294,7 @@ struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
 		cur_seg = cur_seg->next;
 	}

-	aspath_str_update(new, false);
+	aspath_str_update(new);
 	new->count = aspath_count_hops_internal(new);
 	return new;
 }
@@ -1327,7 +1320,7 @@ struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
 		seg = seg->next;
 	}

-	aspath_str_update(new, false);
+	aspath_str_update(new);
 	new->count = aspath_count_hops_internal(new);

 	return new;
@@ -1351,7 +1344,7 @@ struct aspath *aspath_replace_all_asn(struct aspath *aspath, as_t our_asn)
 		seg = seg->next;
 	}

-	aspath_str_update(new, false);
+	aspath_str_update(new);
 	new->count = aspath_count_hops_internal(new);

 	return new;
@@ -1379,7 +1372,7 @@ struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn,
 		seg = seg->next;
 	}

-	aspath_str_update(new, false);
+	aspath_str_update(new);
 	new->count = aspath_count_hops_internal(new);

 	return new;
@@ -1453,7 +1446,7 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn)
 	}
 	if (!aspath->refcnt)
 		aspath_free(aspath);
-	aspath_str_update(new, false);
+	aspath_str_update(new);
 	new->count = aspath_count_hops_internal(new);
 	return new;
 }
@@ -1510,7 +1503,7 @@ static struct aspath *aspath_merge(struct aspath *as1, struct aspath *as2)
 	if (last)
 		last->next = as2->segments;
 	as2->segments = new;
-	aspath_str_update(as2, false);
+	aspath_str_update(as2);
 	as2->count = aspath_count_hops_internal(as2);
 	return as2;
 }
@@ -1528,7 +1521,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
 	/* If as2 is empty, only need to dupe as1's chain onto as2 */
 	if (as2->segments == NULL) {
 		as2->segments = assegment_dup_all(as1->segments);
-		aspath_str_update(as2, false);
+		aspath_str_update(as2);
 		as2->count = aspath_count_hops_internal(as2);
 		return as2;
 	}
@@ -1549,7 +1542,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)

 	if (!as2->segments) {
 		as2->segments = assegment_dup_all(as1->segments);
-		aspath_str_update(as2, false);
+		aspath_str_update(as2);
 		as2->count = aspath_count_hops_internal(as2);
 		return as2;
 	}
@@ -1595,7 +1588,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
 		/* we've now prepended as1's segment chain to as2, merging
 		 * the in between AS_SEQUENCE of seg2 in the process
 		 */
-		aspath_str_update(as2, false);
+		aspath_str_update(as2);
 		as2->count = aspath_count_hops_internal(as2);
 		return as2;
 	} else {
@@ -1707,7 +1700,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source,
 			lastseg->next = newseg;
 		lastseg = newseg;
 	}
-	aspath_str_update(newpath, false);
+	aspath_str_update(newpath);
 	newpath->count = aspath_count_hops_internal(newpath);
 	/* We are happy returning even an empty AS_PATH, because the
 	 * administrator
@@ -1726,7 +1719,7 @@ struct aspath *aspath_filter_exclude_all(struct aspath *source)

 	newpath = aspath_new(source->asnotation);

-	aspath_str_update(newpath, false);
+	aspath_str_update(newpath);
 	newpath->count = aspath_count_hops_internal(newpath);
 	/* We are happy returning even an empty AS_PATH, because the
 	 * administrator
@@ -1814,7 +1807,7 @@ struct aspath *aspath_filter_exclude_acl(struct aspath *source,
 	}


-	aspath_str_update(source, false);
+	aspath_str_update(source);
 	source->count = aspath_count_hops_internal(source);
 	/* We are happy returning even an empty AS_PATH, because the
 	 * administrator
@@ -1851,7 +1844,7 @@ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno, uint8_t
 		aspath->segments = newsegment;
 	}

-	aspath_str_update(aspath, false);
+	aspath_str_update(aspath);
 	aspath->count = aspath_count_hops_internal(aspath);
 	return aspath;
 }
@@ -1940,7 +1933,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath,

 	if (!hops) {
 		newpath = aspath_dup(as4path);
-		aspath_str_update(newpath, false);
+		aspath_str_update(newpath);
 		/* dup sets the count properly */
 		return newpath;
 	}
@@ -2002,7 +1995,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath,
 	mergedpath = aspath_merge(newpath, aspath_dup(as4path));
 	aspath_free(newpath);
 	mergedpath->segments = assegment_normalise(mergedpath->segments);
-	aspath_str_update(mergedpath, false);
+	aspath_str_update(mergedpath);
 	mergedpath->count = aspath_count_hops_internal(mergedpath);

 	if (BGP_DEBUG(as4, AS4))
@@ -2075,7 +2068,7 @@ struct aspath *aspath_delete_confed_seq(struct aspath *aspath)
 	}

 	if (removed_confed_segment) {
-		aspath_str_update(aspath, false);
+		aspath_str_update(aspath);
 		aspath->count = aspath_count_hops_internal(aspath);
 	}

@@ -2128,7 +2121,7 @@ struct aspath *aspath_empty_get(void)
 	struct aspath *aspath;

 	aspath = aspath_new(bgp_get_asnotation(NULL));
-	aspath_make_str_count(aspath, false);
+	aspath_make_str_count(aspath);
 	return aspath;
 }

@@ -2269,7 +2262,7 @@ struct aspath *aspath_str2aspath(const char *str,
 		}
 	}

-	aspath_make_str_count(aspath, false);
+	aspath_make_str_count(aspath);

 	return aspath;
 }
@@ -2281,7 +2274,7 @@ unsigned int aspath_key_make(const void *p)
 	unsigned int key = 0;

 	if (!aspath->str)
-		aspath_str_update((struct aspath *)aspath, false);
+		aspath_str_update((struct aspath *)aspath);

 	key = jhash(aspath->str, aspath->str_len, 2334325);

@@ -2558,7 +2551,7 @@ struct aspath *aspath_delete_as_set_seq(struct aspath *aspath)
 	}

 	if (removed) {
-		aspath_str_update(aspath, false);
+		aspath_str_update(aspath);
 		aspath->count = aspath_count_hops_internal(aspath);
 	}

diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index 295837b137..ce4df29bdd 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -110,7 +110,8 @@ extern struct aspath *aspath_empty(enum asnotation_mode asnotation);
 extern struct aspath *aspath_empty_get(void);
 extern struct aspath *aspath_str2aspath(const char *str,
 					enum asnotation_mode asnotation);
-extern void aspath_str_update(struct aspath *as, bool make_json);
+extern void aspath_str_update(struct aspath *as);
+extern void aspath_json_update(struct aspath *as);
 extern void aspath_free(struct aspath *aspath);
 extern struct aspath *aspath_intern(struct aspath *aspath);
 extern void aspath_unintern(struct aspath **aspath);
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index bb596e54d5..e8fd2d8953 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1377,8 +1377,7 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, void *args[])
 		attr->nh_ifindex, attr->nh_flags, attr->distance, &attr->mp_nexthop_global,
 		&attr->mp_nexthop_local, attr->nh_lla_ifindex);
 	vty_out(vty, "\taspath: %s Community: %s Large Community: %s\n", aspath_print(attr->aspath),
-		community_str(attr->community, false, false),
-		lcommunity_str(attr->lcommunity, false, false));
+		community_str(attr->community, false), lcommunity_str(attr->lcommunity, false));
 	vty_out(vty, "\tExtended Community: %s Extended IPv6 Community: %s\n",
 		ecommunity_str(attr->ecommunity),
 		ecommunity_str(bgp_attr_get_ipv6_ecommunity(attr)));
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 8b244bd703..f9f4ca9ed9 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -18,7 +18,6 @@
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_ecommunity.h"
 #include "bgpd/bgp_lcommunity.h"
-#include "bgpd/bgp_community_alias.h"
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_regex.h"
 #include "bgpd/bgp_clist.h"
@@ -532,33 +531,16 @@ static bool community_regexp_include(struct frregex *reg, struct community *com,
 static bool community_regexp_match(struct community *com, struct frregex *reg)
 {
 	const char *str;
-	char *regstr;
-	int rv;
-	bool translate_alias = !!bgp_ca_alias_hash->count;

 	/* When there is no communities attribute it is treated as empty
 	   string.  */
 	if (com == NULL || com->size == 0)
 		return false;

-	str = community_str(com, false, translate_alias);
-
-	/* If at least one community alias is configured, then let's
-	 * do the work, otherwise we don't need to spend time on splitting
-	 * stuff and creating a new string.
-	 */
-	regstr = translate_alias ? bgp_alias2community_str(str) : (char *)str;
+	str = community_str(com, false);

 	/* Regular expression match.  */
-	rv = regexec(&reg->real, regstr, 0, NULL, 0);
-
-	/* This is allocated by frrstr_join(), and needs to be freed
-	 * only if it was created.
-	 */
-	if (translate_alias)
-		XFREE(MTYPE_TMP, regstr);
-
-	return rv == 0;
+	return regexec(&reg->real, str, 0, NULL, 0) == 0;
 }

 static char *ecommunity_str_get(struct ecommunity *ecom, int i)
@@ -642,33 +624,16 @@ static bool ecommunity_regexp_include(struct frregex *reg, struct ecommunity *ec
 static bool lcommunity_regexp_match(struct lcommunity *com, struct frregex *reg)
 {
 	const char *str;
-	char *regstr;
-	int rv;
-	bool translate_alias = !!bgp_ca_alias_hash->count;

 	/* When there is no communities attribute it is treated as empty
 	   string.  */
 	if (com == NULL || com->size == 0)
 		return false;

-	str = lcommunity_str(com, false, translate_alias);
-
-	/* If at least one community alias is configured, then let's
-	 * do the work, otherwise we don't need to spend time on splitting
-	 * stuff and creating a new string.
-	 */
-	regstr = translate_alias ? bgp_alias2community_str(str) : (char *)str;
+	str = lcommunity_str(com, false);

 	/* Regular expression match.  */
-	rv = regexec(&reg->real, regstr, 0, NULL, 0);
-
-	/* This is allocated by frrstr_join(), and needs to be freed
-	 * only if it was created.
-	 */
-	if (translate_alias)
-		XFREE(MTYPE_TMP, regstr);
-
-	return rv == 0;
+	return regexec(&reg->real, str, 0, NULL, 0) == 0;
 }


diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
index 7bc64f634f..dd5304e456 100644
--- a/bgpd/bgp_community.c
+++ b/bgpd/bgp_community.c
@@ -184,8 +184,7 @@ struct community *community_uniq_sort(struct community *com)
    0xFFFFFF04      "no-peer"

    For other values, "AS:VAL" format is used.  */
-static void set_community_string(struct community *com, bool make_json,
-				 bool translate_alias)
+static void set_community_string(struct community *com, bool make_json)
 {
 	int i;
 	char *str;
@@ -217,8 +216,7 @@ static void set_community_string(struct community *com, bool make_json,
 			json_object_object_add(com->json, "list",
 					       json_community_list);
 		}
-		com->str = str;
-		return;
+		goto out;
 	}

 	/* Memory allocation is time consuming work.  So we calculate
@@ -279,7 +277,7 @@ static void set_community_string(struct community *com, bool make_json,
 			val = CHECK_FLAG(comval, 0xFFFF);

 			snprintf(buf, sizeof(buf), "%u:%d", as, val);
-			const char *com2alias = translate_alias ? bgp_community2alias(buf) : buf;
+			const char *com2alias = make_json ? bgp_community2alias(buf) : buf;

 			/* Plus one for the space separator. */
 			len += strlen(com2alias) + 1;
@@ -432,8 +430,7 @@ static void set_community_string(struct community *com, bool make_json,
 			val = CHECK_FLAG(comval, 0xFFFF);
 			char buf[32];
 			snprintf(buf, sizeof(buf), "%u:%d", as, val);
-			const char *com2alias =
-				translate_alias ? bgp_community2alias(buf) : buf;
+			const char *com2alias = make_json ? bgp_community2alias(buf) : buf;

 			strlcat(str, com2alias, len);
 			if (make_json) {
@@ -449,7 +446,12 @@ static void set_community_string(struct community *com, bool make_json,
 		json_object_string_add(com->json, "string", str);
 		json_object_object_add(com->json, "list", json_community_list);
 	}
-	com->str = str;
+
+out:
+	if (!com->str && !make_json)
+		com->str = str;
+	else
+		XFREE(MTYPE_COMMUNITY_STR, str);
 }

 /* Intern communities attribute.  */
@@ -473,7 +475,7 @@ struct community *community_intern(struct community *com)

 	/* Make string.  */
 	if (!find->str)
-		set_community_string(find, false, true);
+		set_community_string(find, false);

 	return find;
 }
@@ -534,16 +536,15 @@ struct community *community_dup(struct community *com)
 }

 /* Return string representation of communities attribute. */
-char *community_str(struct community *com, bool make_json, bool translate_alias)
+char *community_str(struct community *com, bool make_json)
 {
 	if (!com)
 		return NULL;

-	if (make_json && !com->json && com->str)
-		XFREE(MTYPE_COMMUNITY_STR, com->str);
-
 	if (!com->str)
-		set_community_string(com, make_json, translate_alias);
+		set_community_string(com, false);
+	if (make_json && !com->json)
+		set_community_string(com, true);
 	return com->str;
 }

diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
index b172207144..9dd7df18c6 100644
--- a/bgpd/bgp_community.h
+++ b/bgpd/bgp_community.h
@@ -62,8 +62,7 @@ extern struct community *community_uniq_sort(struct community *com);
 extern struct community *community_parse(uint32_t *pnt, unsigned short length);
 extern struct community *community_intern(struct community *com);
 extern void community_unintern(struct community **com);
-extern char *community_str(struct community *com, bool make_json,
-			   bool translate_alias);
+extern char *community_str(struct community *com, bool make_json);
 extern unsigned int community_hash_make(const struct community *com);
 extern struct community *community_str2com(const char *str);
 extern bool community_match(const struct community *com1,
diff --git a/bgpd/bgp_community_alias.c b/bgpd/bgp_community_alias.c
index d7567fcdd4..c10e1ac513 100644
--- a/bgpd/bgp_community_alias.c
+++ b/bgpd/bgp_community_alias.c
@@ -159,31 +159,30 @@ const char *bgp_alias2community(char *alias)
 	return alias;
 }

-/* Communities structs have `->str` which is used
- * for vty outputs and extended BGP community lists
- * with regexp.
- * This is a helper to convert already aliased version
- * of communities into numerical-only format.
+/* Communities structs cache `->str` without aliases (AS:VAL and
+ * well-known names only), which is used for matching.
+ * This is a helper to convert it into the aliased version
+ * for outputs. The result must be freed with MTYPE_TMP.
  */
-char *bgp_alias2community_str(const char *str)
+char *bgp_community2alias_str(const char *str)
 {
-	char **aliases;
-	char *comstr;
+	char **communities;
+	char *aliasstr;
 	int num, i;

-	frrstr_split(str, " ", &aliases, &num);
-	const char *communities[num];
+	frrstr_split(str, " ", &communities, &num);
+	const char *aliases[num];

 	for (i = 0; i < num; i++)
-		communities[i] = bgp_alias2community(aliases[i]);
+		aliases[i] = bgp_community2alias(communities[i]);

-	comstr = frrstr_join(communities, num, " ");
+	aliasstr = frrstr_join(aliases, num, " ");

 	for (i = 0; i < num; i++)
-		XFREE(MTYPE_TMP, aliases[i]);
-	XFREE(MTYPE_TMP, aliases);
+		XFREE(MTYPE_TMP, communities[i]);
+	XFREE(MTYPE_TMP, communities);

-	return comstr;
+	return aliasstr;
 }

 static int bgp_community_alias_vector_walker(struct hash_bucket *bucket,
diff --git a/bgpd/bgp_community_alias.h b/bgpd/bgp_community_alias.h
index 9dcab6fe30..a9466fdebc 100644
--- a/bgpd/bgp_community_alias.h
+++ b/bgpd/bgp_community_alias.h
@@ -30,7 +30,7 @@ extern void bgp_ca_alias_delete(struct community_alias *ca);
 extern int bgp_community_alias_write(struct vty *vty);
 extern const char *bgp_community2alias(char *community);
 extern const char *bgp_alias2community(char *alias);
-extern char *bgp_alias2community_str(const char *str);
+extern char *bgp_community2alias_str(const char *str);
 extern void bgp_community_alias_command_completion_setup(void);

 #endif /* FRR_BGP_COMMUNITY_ALIAS_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index f165335c9a..da27833d19 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -25,6 +25,7 @@
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_community_alias.h"
 #include "bgpd/bgp_updgrp.h"
 #include "bgpd/bgp_mplsvpn.h"
 #include "bgpd/bgp_ecommunity.h"
@@ -445,13 +446,21 @@ bool bgp_dump_attr(struct attr *attr, char *buf, size_t size)
 	if (bgp_attr_exists(attr, BGP_ATTR_MULTI_EXIT_DISC))
 		snprintfrr(buf + strlen(buf), size - strlen(buf), ", metric %u", attr->med);

-	if (bgp_attr_exists(attr, BGP_ATTR_COMMUNITIES))
-		snprintfrr(buf + strlen(buf), size - strlen(buf), ", community %s",
-			   community_str(bgp_attr_get_community(attr), false, true));
+	if (bgp_attr_exists(attr, BGP_ATTR_COMMUNITIES)) {
+		char *str = bgp_community2alias_str(
+			community_str(bgp_attr_get_community(attr), false));

-	if (bgp_attr_exists(attr, BGP_ATTR_LARGE_COMMUNITIES))
-		snprintfrr(buf + strlen(buf), size - strlen(buf), ", large-community %s",
-			   lcommunity_str(bgp_attr_get_lcommunity(attr), false, true));
+		snprintfrr(buf + strlen(buf), size - strlen(buf), ", community %s", str);
+		XFREE(MTYPE_TMP, str);
+	}
+
+	if (bgp_attr_exists(attr, BGP_ATTR_LARGE_COMMUNITIES)) {
+		char *str = bgp_community2alias_str(
+			lcommunity_str(bgp_attr_get_lcommunity(attr), false));
+
+		snprintfrr(buf + strlen(buf), size - strlen(buf), ", large-community %s", str);
+		XFREE(MTYPE_TMP, str);
+	}

 	if (bgp_attr_exists(attr, BGP_ATTR_EXT_COMMUNITIES))
 		snprintfrr(buf + strlen(buf), size - strlen(buf), ", extcommunity %s",
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
index 57a7981e87..5bc516c2d6 100644
--- a/bgpd/bgp_lcommunity.c
+++ b/bgpd/bgp_lcommunity.c
@@ -161,8 +161,7 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
 	return lcom1;
 }

-static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
-				  bool translate_alias)
+static void set_lcommunity_string(struct lcommunity *lcom, bool make_json)
 {
 	int i;
 	int len;
@@ -194,8 +193,7 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
 					       json_lcommunity_list);
 		}

-		lcom->str = str_buf;
-		return;
+		goto out;
 	}

 	/* 1 space + lcom->size lcom strings + null terminator */
@@ -224,8 +222,7 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
 		 * then we need to realloc the memory associated
 		 * with the string so that it can fit
 		 */
-		const char *com2alias =
-			translate_alias ? bgp_community2alias(lcsb) : lcsb;
+		const char *com2alias = make_json ? bgp_community2alias(lcsb) : lcsb;
 		size_t individual_len = strlen(com2alias);
 		if (individual_len + len > str_buf_sz) {
 			str_buf_sz = individual_len + len + 1;
@@ -248,7 +245,11 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
 				       json_lcommunity_list);
 	}

-	lcom->str = str_buf;
+out:
+	if (!lcom->str && !make_json)
+		lcom->str = str_buf;
+	else
+		XFREE(MTYPE_LCOMMUNITY_STR, str_buf);
 }

 /* Intern Large Communities Attribute.  */
@@ -266,7 +267,7 @@ struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
 	find->refcnt++;

 	if (!find->str)
-		set_lcommunity_string(find, false, true);
+		set_lcommunity_string(find, false);

 	return find;
 }
@@ -293,17 +294,15 @@ void lcommunity_unintern(struct lcommunity **lcom)
 }

 /* Return string representation of lcommunities attribute. */
-char *lcommunity_str(struct lcommunity *lcom, bool make_json,
-		     bool translate_alias)
+char *lcommunity_str(struct lcommunity *lcom, bool make_json)
 {
 	if (!lcom)
 		return NULL;

-	if (make_json && !lcom->json && lcom->str)
-		XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
-
 	if (!lcom->str)
-		set_lcommunity_string(lcom, make_json, translate_alias);
+		set_lcommunity_string(lcom, false);
+	if (make_json && !lcom->json)
+		set_lcommunity_string(lcom, true);

 	return lcom->str;
 }
diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h
index 0c8140e779..03fc2905cd 100644
--- a/bgpd/bgp_lcommunity.h
+++ b/bgpd/bgp_lcommunity.h
@@ -54,8 +54,7 @@ extern unsigned int lcommunity_hash_make(const void *arg);
 extern struct hash *lcommunity_hash(void);
 extern struct lcommunity *lcommunity_str2com(const char *str);
 extern bool lcommunity_match(const struct lcommunity *lcom1, const struct lcommunity *lcom2);
-extern char *lcommunity_str(struct lcommunity *lcom, bool make_json,
-			    bool translate_alias);
+extern char *lcommunity_str(struct lcommunity *lcom, bool make_json);
 extern bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr);
 extern void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr);

diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 9e8749ea9d..0a2021ad90 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -13872,7 +13872,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
 	if (attr->aspath) {
 		if (json_paths) {
 			if (!attr->aspath->json)
-				aspath_str_update(attr->aspath, true);
+				aspath_json_update(attr->aspath);
 			json_object_lock(attr->aspath->json);
 			json_object_object_add(json_path, "aspath",
 					       attr->aspath->json);
@@ -14476,17 +14476,17 @@ skip_nexthop:
 	if (bgp_attr_exists(attr, BGP_ATTR_COMMUNITIES)) {
 		if (json_paths) {
 			if (!bgp_attr_get_community(attr)->json)
-				community_str(bgp_attr_get_community(attr),
-					      true, true);
+				community_str(bgp_attr_get_community(attr), true);
 			json_object_lock(bgp_attr_get_community(attr)->json);
 			json_object_object_add(
 				json_path, "community",
 				bgp_attr_get_community(attr)->json);
 		} else {
-			if (!bgp_attr_get_community(attr)->str)
-				community_str(bgp_attr_get_community(attr), true, true);
-			vty_out(vty, "      Community: %s\n",
-				bgp_attr_get_community(attr)->str);
+			char *str = bgp_community2alias_str(
+				community_str(bgp_attr_get_community(attr), false));
+
+			vty_out(vty, "      Community: %s\n", str);
+			XFREE(MTYPE_TMP, str);
 		}
 	}

@@ -14530,17 +14530,17 @@ skip_nexthop:
 	if (bgp_attr_exists(attr, BGP_ATTR_LARGE_COMMUNITIES)) {
 		if (json_paths) {
 			if (!bgp_attr_get_lcommunity(attr)->json)
-				lcommunity_str(bgp_attr_get_lcommunity(attr),
-					       true, true);
+				lcommunity_str(bgp_attr_get_lcommunity(attr), true);
 			json_object_lock(bgp_attr_get_lcommunity(attr)->json);
 			json_object_object_add(
 				json_path, "largeCommunity",
 				bgp_attr_get_lcommunity(attr)->json);
 		} else {
-			if (!bgp_attr_get_lcommunity(attr)->str)
-				lcommunity_str(bgp_attr_get_lcommunity(attr), true, true);
-			vty_out(vty, "      Large Community: %s\n",
-				bgp_attr_get_lcommunity(attr)->str);
+			char *str = bgp_community2alias_str(
+				lcommunity_str(bgp_attr_get_lcommunity(attr), false));
+
+			vty_out(vty, "      Large Community: %s\n", str);
+			XFREE(MTYPE_TMP, str);
 		}
 	}

diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 7460332dd8..35a10e3dc3 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -7168,7 +7168,7 @@ DEFUN_YANG (set_community,
 	XFREE(MTYPE_TMP, str);

 	/* Set communities attribute string.  */
-	str = community_str(com, false, false);
+	str = community_str(com, false);

 	if (additive) {
 		size_t argstr_sz = strlen(str) + strlen(" additive") + 1;
diff --git a/bgpd/bgp_unreach.c b/bgpd/bgp_unreach.c
index 9b56f01028..cb4ca33e79 100644
--- a/bgpd/bgp_unreach.c
+++ b/bgpd/bgp_unreach.c
@@ -1074,7 +1074,7 @@ static void bgp_unreach_path_detail_json(json_object *json_path,

 		if (comm) {
 			if (!comm->json)
-				community_str(comm, true, true);
+				community_str(comm, true);
 			json_object_lock(comm->json);
 			json_object_object_add(json_path, "community",
 					       comm->json);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index bb37bae3f1..f42134670a 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -13786,13 +13786,13 @@ static void bgp_show_aggregate_one(struct vty *vty, const struct prefix *p,
 					       aggregate->suppress_map_name);
 		if (aggregate->community)
 			json_object_string_add(json_aggr, "community",
-					       community_str(aggregate->community, false, false));
+					       community_str(aggregate->community, false));
 		if (aggregate->ecommunity)
 			json_object_string_add(json_aggr, "extendedCommunity",
 					       ecommunity_str(aggregate->ecommunity));
 		if (aggregate->lcommunity)
 			json_object_string_add(json_aggr, "largeCommunity",
-					       lcommunity_str(aggregate->lcommunity, false, false));
+					       lcommunity_str(aggregate->lcommunity, false));
 		if (aggregate->aspath)
 			json_object_string_add(json_aggr, "asPath",
 					       aspath_print(aggregate->aspath));
@@ -13822,13 +13822,13 @@ static void bgp_show_aggregate_one(struct vty *vty, const struct prefix *p,
 			vty_out(vty, "  Suppress-map: %s\n", aggregate->suppress_map_name);
 		if (aggregate->community)
 			vty_out(vty, "  Community: %s\n",
-				community_str(aggregate->community, false, false));
+				community_str(aggregate->community, false));
 		if (aggregate->ecommunity)
 			vty_out(vty, "  Extended community: %s\n",
 				ecommunity_str(aggregate->ecommunity));
 		if (aggregate->lcommunity)
 			vty_out(vty, "  Large community: %s\n",
-				lcommunity_str(aggregate->lcommunity, false, false));
+				lcommunity_str(aggregate->lcommunity, false));
 		if (aggregate->aspath)
 			vty_out(vty, "  AS path: %s\n", aspath_print(aggregate->aspath));
 	}
@@ -19085,8 +19085,7 @@ static void community_show_all_iterator(struct hash_bucket *bucket,
 	struct community *com;

 	com = (struct community *)bucket->data;
-	vty_out(vty, "[%p] (%ld) %s\n", (void *)com, com->refcnt,
-		community_str(com, false, false));
+	vty_out(vty, "[%p] (%ld) %s\n", (void *)com, com->refcnt, community_str(com, false));
 }

 /* Show BGP's community internal data. */
@@ -19114,8 +19113,7 @@ static void lcommunity_show_all_iterator(struct hash_bucket *bucket,
 	struct lcommunity *lcom;

 	lcom = (struct lcommunity *)bucket->data;
-	vty_out(vty, "[%p] (%ld) %s\n", (void *)lcom, lcom->refcnt,
-		lcommunity_str(lcom, false, false));
+	vty_out(vty, "[%p] (%ld) %s\n", (void *)lcom, lcom->refcnt, lcommunity_str(lcom, false));
 }

 /* Show BGP's community internal data. */
@@ -25613,9 +25611,9 @@ static const char *community_list_config_str(struct community_entry *entry)
 	const char *str;

 	if (entry->style == COMMUNITY_LIST_STANDARD)
-		str = community_str(entry->u.com, false, false);
+		str = community_str(entry->u.com, false);
 	else if (entry->style == LARGE_COMMUNITY_LIST_STANDARD)
-		str = lcommunity_str(entry->u.lcom, false, false);
+		str = lcommunity_str(entry->u.lcom, false);
 	else
 		str = entry->config;

diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 22dc36d622..5aac3a911a 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -55,6 +55,7 @@
 #include "bgpd/bgp_trace.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_community_alias.h"
 #include "bgpd/bgp_srv6.h"
 #include "bgpd/bgp_ls.h"
 #include "bgpd/bgp_ls_ted.h"
@@ -1664,15 +1665,21 @@ enum zclient_send_status bgp_zebra_announce_actual(struct bgp_dest *dest,
 		strlcpy(bzo.aspath, info->attr->aspath->str,
 			sizeof(bzo.aspath));

-		if (bgp_attr_exists(info->attr, BGP_ATTR_COMMUNITIES))
-			strlcpy(bzo.community,
-				bgp_attr_get_community(info->attr)->str,
-				sizeof(bzo.community));
+		if (bgp_attr_exists(info->attr, BGP_ATTR_COMMUNITIES)) {
+			char *str =
+				bgp_community2alias_str(bgp_attr_get_community(info->attr)->str);

-		if (bgp_attr_exists(info->attr, BGP_ATTR_LARGE_COMMUNITIES))
-			strlcpy(bzo.lcommunity,
-				bgp_attr_get_lcommunity(info->attr)->str,
-				sizeof(bzo.lcommunity));
+			strlcpy(bzo.community, str, sizeof(bzo.community));
+			XFREE(MTYPE_TMP, str);
+		}
+
+		if (bgp_attr_exists(info->attr, BGP_ATTR_LARGE_COMMUNITIES)) {
+			char *str =
+				bgp_community2alias_str(bgp_attr_get_lcommunity(info->attr)->str);
+
+			strlcpy(bzo.lcommunity, str, sizeof(bzo.lcommunity));
+			XFREE(MTYPE_TMP, str);
+		}

 		strlcpy(bzo.selection_reason, reason,
 			sizeof(bzo.selection_reason));
diff --git a/tests/bgpd/test_community.c b/tests/bgpd/test_community.c
index 7aed4f237a..164cf964f1 100644
--- a/tests/bgpd/test_community.c
+++ b/tests/bgpd/test_community.c
@@ -56,7 +56,7 @@ static void test_large_community_not_truncated(void)
 	com = community_str2com(input);
 	assert(com);

-	str = community_str(com, false, false);
+	str = community_str(com, false);

 	printf("rendered string length: %zu (%d communities)\n", strlen(str),
 	       com->size);