From 06bfab5ec7cb07f5ea498769a8c15dd053e1845e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 10 Sep 2024 18:36:07 +0200 Subject: [PATCH 1/9] bgpd, lib: generate aspath json string on demand When using the 'show bgp ipv4 json detail' against a full route, the BGP memory consumed increases a lot, and is never restored. Before the show: > root@dut-orwell-nianticvf:~# ps -aux | grep bgpd > root 12263 10.8 10.1 1213920 1033452 ? Ssl 10:43 1:42 /usr/bin/bgpd -A 127.0.0.1 -M snmp -M rpki -M bmp After the show: > root@dut-orwell-nianticvf:~# ps -aux | grep bgpd > root 11772 4.9 19.3 2150668 1970548 ? Ssl 08:59 2:09 /usr/bin/bgpd -A 127.0.0.1 -M snmp -M rpki -M bmp This is not a memory leak, since applying multiple times the command does not increase the memory. What happens is that some internal structures of BGP store a json structure: AsPath, Communities, Large Communities. This series of commits intends to do the following for the following objects: ASPaths, communities and large communities: - replace the usage of the internal json structure by a dynamically built json object. - remove the no more used json object referenced in the BGP internals. That first commit addresses the case with ASpaths: instead of relying on the internal json structure, the json aspath objects are allocated and freed at usage. Signed-off-by: Philippe Guibert --- bgpd/bgp_aspath.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++- bgpd/bgp_aspath.h | 1 + bgpd/bgp_route.c | 7 +++--- lib/asn.c | 35 ++++++++++++++++++++--------- lib/asn.h | 3 ++- 5 files changed, 86 insertions(+), 16 deletions(-) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 4c1615a5c625..d095d65742d3 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -606,7 +606,7 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) for (i = 0; i < seg->length; i++) { if (make_json) asn_asn2json_array(jseg_list, seg->as[i], - as->asnotation); + as->asnotation, NULL, false); len += snprintfrr(str_buf + len, str_size - len, ASN_FORMAT(as->asnotation), &seg->as[i]); @@ -662,6 +662,60 @@ void aspath_str_update(struct aspath *as, bool make_json) aspath_make_str_count(as, make_json); } +static int aspath_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + struct aspath *as; + struct assegment *seg; + int count_seg = 0, i; + char num_str[12]; + const char *seg_type; + + + as = json_object_get_userdata(jso); + assert(as); + seg = as->segments; + if (seg == NULL) + printbuf_strappend(pb, "{\"string\":\"Local\",\"segments\":["); + else { + printbuf_strappend(pb, "{\"string\":\""); + printbuf_memappend(pb, as->str, strlen(as->str)); + printbuf_strappend(pb, "\",\"segments\":["); + } + while (seg) { + if (count_seg) + printbuf_strappend(pb, ","); + printbuf_strappend(pb, "{\"type\":\""); + seg_type = aspath_segment_type_str[seg->type]; + printbuf_memappend(pb, seg_type, strlen(seg_type)); + printbuf_strappend(pb, "\",\"list\":["); + + for (i = 0; i < seg->length; i++) { + asn_asn2json_array(NULL, seg->as[i], as->asnotation, pb, + true); + if (i < (seg->length - 1)) + printbuf_strappend(pb, ","); + } + printbuf_strappend(pb, "]}"); + seg = seg->next; + count_seg++; + } + printbuf_strappend(pb, "],\"length\":"); + snprintf(num_str, sizeof(num_str), "%d}", aspath_count_hops(as)); + return printbuf_memappend(pb, num_str, strlen(num_str)); +} + +json_object *aspath_get_json(struct aspath *aspath) +{ + json_object *json_aspath = NULL; + + if (aspath->json) + return aspath->json; + json_aspath = json_object_new_object(); + json_object_set_serializer(json_aspath, aspath_json_string, aspath, + NULL); + return json_aspath; +} /* Intern allocated AS path. */ struct aspath *aspath_intern(struct aspath *aspath) { diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index f7e57fd66dda..cf2b66cfb3e8 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -112,6 +112,7 @@ extern struct aspath *aspath_str2aspath(const char *str, extern void aspath_str_update(struct aspath *as, bool make_json); extern void aspath_free(struct aspath *aspath); extern struct aspath *aspath_intern(struct aspath *aspath); +extern json_object *aspath_get_json(struct aspath *aspath); extern void aspath_unintern(struct aspath **aspath); extern const char *aspath_print(struct aspath *aspath); extern void aspath_print_vty(struct vty *vty, struct aspath *aspath); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 3c0f0c8b53b1..7be8f1e202fd 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10515,6 +10515,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_object *json_peer = NULL; json_object *json_string = NULL; json_object *json_adv_to = NULL; + json_object *json_aspath = NULL; int first = 0; struct listnode *node, *nnode; struct peer *peer; @@ -10654,11 +10655,9 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, /* Line1 display AS-path, Aggregator */ if (attr->aspath) { if (json_paths) { - if (!attr->aspath->json) - aspath_str_update(attr->aspath, true); - json_object_lock(attr->aspath->json); + json_aspath = aspath_get_json(attr->aspath); json_object_object_add(json_path, "aspath", - attr->aspath->json); + json_aspath); } else { if (attr->aspath->segments) vty_out(vty, " %s", attr->aspath->str); diff --git a/lib/asn.c b/lib/asn.c index 4042f5846cc1..ad1b78885727 100644 --- a/lib/asn.c +++ b/lib/asn.c @@ -128,13 +128,17 @@ static bool asn_str2asn_internal(const char *asstring, as_t *asn, return ret; } -static void asn_asn2asdot(as_t asn, char *asstring, size_t len) +static void asn_asn2asdot(as_t asn, char *asstring, size_t len, + bool quotation_mark) { uint16_t low, high; high = (asn >> 16) & 0xffff; low = asn & 0xffff; - snprintf(asstring, len, "%hu.%hu", high, low); + if (quotation_mark) + snprintf(asstring, len, "\"%hu.%hu\"", high, low); + else + snprintf(asstring, len, "%hu.%hu", high, low); } bool asn_str2asn(const char *asstring, as_t *asn) @@ -202,23 +206,34 @@ void asn_asn2json(json_object *json, const char *attr, ((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX)) json_object_int_add(json, attr, asn); else { - asn_asn2asdot(asn, as_str, sizeof(as_str)); + asn_asn2asdot(asn, as_str, sizeof(as_str), false); json_object_string_add(json, attr, as_str); } } void asn_asn2json_array(json_object *jseg_list, as_t asn, - enum asnotation_mode asnotation) + enum asnotation_mode asnotation, struct printbuf *pb, + bool incremental_print) { - static char as_str[ASN_STRING_MAX_SIZE]; + static char as_str[ASN_STRING_MAX_SIZE + 2]; + const char *const_str = as_str; if ((asnotation == ASNOTATION_PLAIN) || ((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX)) - json_object_array_add(jseg_list, - json_object_new_int64(asn)); + if (incremental_print) { + snprintf(as_str, sizeof(as_str), "%u", asn); + printbuf_memappend(pb, const_str, strlen(const_str)); + } else + json_object_array_add(jseg_list, + json_object_new_int64(asn)); else { - asn_asn2asdot(asn, as_str, sizeof(as_str)); - json_array_string_add(jseg_list, as_str); + if (incremental_print) { + asn_asn2asdot(asn, as_str, sizeof(as_str), true); + printbuf_memappend(pb, const_str, strlen(const_str)); + } else { + asn_asn2asdot(asn, as_str, sizeof(as_str), false); + json_array_string_add(jseg_list, as_str); + } } } @@ -229,7 +244,7 @@ char *asn_asn2string(const as_t *asn, char *buf, size_t len, ((asnotation == ASNOTATION_DOT) && *asn < UINT16_MAX)) snprintf(buf, len, "%u", *asn); else - asn_asn2asdot(*asn, buf, len); + asn_asn2asdot(*asn, buf, len, false); return buf; } diff --git a/lib/asn.h b/lib/asn.h index a7394fa52bc6..f66d8a9ef60b 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -50,7 +50,8 @@ extern bool asn_str2asn_notation(const char *asstring, as_t *asn, enum asnotation_mode *asnotation); extern const char *asn_mode2str(enum asnotation_mode asnotation); void asn_asn2json_array(json_object *jseg_list, as_t asn, - enum asnotation_mode asnotation); + enum asnotation_mode asnotation, struct printbuf *pb, + bool incremental_print); void asn_asn2json(json_object *jseg_list, const char *attr, as_t asn, enum asnotation_mode asnotation); extern char *asn_asn2string(const as_t *as, char *buf, size_t len, From 5bfc84d6f6b7c960b6664b825a54a7a629cb1015 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 11 Sep 2024 13:27:55 +0200 Subject: [PATCH 2/9] bgpd: remove make_json option from as_path The aspath library stores a json pointer that is no more used. Remove the 'make_json' argument of the associated functions. Remove the 'json' attribute from the bgp_aspath structure. Signed-off-by: Philippe Guibert --- bgpd/bgp_aspath.c | 102 +++++++++++----------------------------------- bgpd/bgp_aspath.h | 5 +-- 2 files changed, 24 insertions(+), 83 deletions(-) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index d095d65742d3..64df4d0aa918 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -309,11 +309,6 @@ void aspath_free(struct aspath *aspath) assegment_free_all(aspath->segments); XFREE(MTYPE_AS_STR, aspath->str); - if (aspath->json) { - json_object_free(aspath->json); - aspath->json = NULL; - } - XFREE(MTYPE_AS_PATH, aspath); } @@ -506,29 +501,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; @@ -571,8 +552,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; return; } @@ -599,14 +578,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, NULL, false); len += snprintfrr(str_buf + len, str_size - len, ASN_FORMAT(as->asnotation), &seg->as[i]); @@ -616,15 +589,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", @@ -641,25 +605,14 @@ 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); - if (as->json) { - json_object_free(as->json); - as->json = NULL; - } - - aspath_make_str_count(as, make_json); + aspath_make_str_count(as); } static int aspath_json_string(struct json_object *jso, struct printbuf *pb, @@ -709,8 +662,6 @@ json_object *aspath_get_json(struct aspath *aspath) { json_object *json_aspath = NULL; - if (aspath->json) - return aspath->json; json_aspath = json_object_new_object(); json_object_set_serializer(json_aspath, aspath_json_string, aspath, NULL); @@ -744,7 +695,6 @@ struct aspath *aspath_dup(struct aspath *aspath) struct aspath *new; new = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); - new->json = NULL; if (aspath->segments) new->segments = assegment_dup_all(aspath->segments); @@ -781,7 +731,6 @@ static void *aspath_hash_alloc(void *arg) new->segments = aspath->segments; new->str = aspath->str; new->str_len = aspath->str_len; - new->json = aspath->json; new->asnotation = aspath->asnotation; return new; @@ -918,10 +867,6 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit, assegment_free_all(as.segments); /* aspath_key_make() always updates the string */ XFREE(MTYPE_AS_STR, as.str); - if (as.json) { - json_object_free(as.json); - as.json = NULL; - } } find->refcnt++; @@ -1166,7 +1111,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) } assegment_normalise(aspath->segments); - aspath_str_update(aspath, false); + aspath_str_update(aspath); return aspath; } @@ -1321,7 +1266,7 @@ struct aspath *aspath_replace_regex_asn(struct aspath *aspath, cur_seg = cur_seg->next; } - aspath_str_update(new, false); + aspath_str_update(new); return new; } @@ -1346,7 +1291,7 @@ struct aspath *aspath_replace_specific_asn(struct aspath *aspath, seg = seg->next; } - aspath_str_update(new, false); + aspath_str_update(new); return new; } @@ -1368,7 +1313,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); return new; } @@ -1394,7 +1339,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); return new; } @@ -1412,7 +1357,6 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn) new = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); - new->json = NULL; new_seg = NULL; last_new_seg = NULL; seg = aspath->segments; @@ -1466,7 +1410,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); return new; } @@ -1522,7 +1466,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); return as2; } @@ -1539,7 +1483,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); return as2; } @@ -1559,7 +1503,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); return as2; } @@ -1604,7 +1548,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) /* we've now prepended as1's segment chain to as2, merging * the inbetween AS_SEQUENCE of seg2 in the process */ - aspath_str_update(as2, false); + aspath_str_update(as2); return as2; } else { /* AS_SET merge code is needed at here. */ @@ -1715,7 +1659,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source, lastseg->next = newseg; lastseg = newseg; } - aspath_str_update(newpath, false); + aspath_str_update(newpath); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1733,7 +1677,7 @@ struct aspath *aspath_filter_exclude_all(struct aspath *source) newpath = aspath_new(source->asnotation); - aspath_str_update(newpath, false); + aspath_str_update(newpath); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1820,7 +1764,7 @@ struct aspath *aspath_filter_exclude_acl(struct aspath *source, } - aspath_str_update(source, false); + aspath_str_update(source); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1858,7 +1802,7 @@ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno, aspath->segments = newsegment; } - aspath_str_update(aspath, false); + aspath_str_update(aspath); return aspath; } @@ -1949,7 +1893,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, if (!hops) { newpath = aspath_dup(as4path); - aspath_str_update(newpath, false); + aspath_str_update(newpath); return newpath; } @@ -2010,7 +1954,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); if (BGP_DEBUG(as4, AS4)) zlog_debug("[AS4] result of synthesizing is %s", @@ -2082,7 +2026,7 @@ struct aspath *aspath_delete_confed_seq(struct aspath *aspath) } if (removed_confed_segment) - aspath_str_update(aspath, false); + aspath_str_update(aspath); return aspath; } @@ -2133,7 +2077,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; } @@ -2274,7 +2218,7 @@ struct aspath *aspath_str2aspath(const char *str, } } - aspath_make_str_count(aspath, false); + aspath_make_str_count(aspath); return aspath; } @@ -2286,7 +2230,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); diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index cf2b66cfb3e8..33c457b6806a 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -52,9 +52,6 @@ struct aspath { /* segment data */ struct assegment *segments; - /* AS path as a json object */ - json_object *json; - /* String expression of AS path. This string is used by vty output and AS path regular expression match. */ char *str; @@ -109,7 +106,7 @@ 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_free(struct aspath *aspath); extern struct aspath *aspath_intern(struct aspath *aspath); extern json_object *aspath_get_json(struct aspath *aspath); From 5377aad3676972c177be4d2bd19a6220dc338e96 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 3 Sep 2024 15:05:41 +0200 Subject: [PATCH 3/9] bgpd: move community list build in a separate function Create the community_json_list() function, that stores the community list output in a passed buffer, and optionally in the json community list passed array. This commit does not have any functional impact. Signed-off-by: Philippe Guibert --- bgpd/bgp_community.c | 262 +++++++++++++++++++++++-------------------- 1 file changed, 138 insertions(+), 124 deletions(-) diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 602c1437af11..f804c9b0b511 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -164,121 +164,20 @@ struct community *community_uniq_sort(struct community *com) return new; } -/* Convert communities attribute to string. - - For Well-known communities value, below keyword is used. - - 0xFFFF0000 "graceful-shutdown" - 0xFFFF0001 "accept-own" - 0xFFFF0002 "route-filter-translated-v4" - 0xFFFF0003 "route-filter-v4" - 0xFFFF0004 "route-filter-translated-v6" - 0xFFFF0005 "route-filter-v6" - 0xFFFF0006 "llgr-stale" - 0xFFFF0007 "no-llgr" - 0xFFFF0008 "accept-own-nexthop" - 0xFFFF029A "blackhole" - 0xFFFFFF01 "no-export" - 0xFFFFFF02 "no-advertise" - 0xFFFFFF03 "local-AS" - 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) +/* Convert communities attribute to string and optionally add json objects + * into the pased json_community_list string + */ +static void community_json_list(struct community *com, + json_object *json_community_list, char *str, + int len, bool translate_alias) { int i; - char *str; - int len; int first; + first = 1; uint32_t comval; + json_object *json_string = NULL; uint16_t as; uint16_t val; - json_object *json_community_list = NULL; - json_object *json_string = NULL; - - if (!com) - return; - - if (make_json) { - com->json = json_object_new_object(); - json_community_list = json_object_new_array(); - } - - /* When communities attribute is empty. */ - if (com->size == 0) { - str = XMALLOC(MTYPE_COMMUNITY_STR, 1); - str[0] = '\0'; - - if (make_json) { - json_object_string_add(com->json, "string", ""); - json_object_object_add(com->json, "list", - json_community_list); - } - com->str = str; - return; - } - - /* Memory allocation is time consuming work. So we calculate - required string length first. */ - len = 0; - - for (i = 0; i < com->size; i++) { - memcpy(&comval, com_nthval(com, i), sizeof(uint32_t)); - comval = ntohl(comval); - - switch (comval) { - case COMMUNITY_GSHUT: - len += strlen(" graceful-shutdown"); - break; - case COMMUNITY_ACCEPT_OWN: - len += strlen(" accept-own"); - break; - case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: - len += strlen(" route-filter-translated-v4"); - break; - case COMMUNITY_ROUTE_FILTER_v4: - len += strlen(" route-filter-v4"); - break; - case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: - len += strlen(" route-filter-translated-v6"); - break; - case COMMUNITY_ROUTE_FILTER_v6: - len += strlen(" route-filter-v6"); - break; - case COMMUNITY_LLGR_STALE: - len += strlen(" llgr-stale"); - break; - case COMMUNITY_NO_LLGR: - len += strlen(" no-llgr"); - break; - case COMMUNITY_ACCEPT_OWN_NEXTHOP: - len += strlen(" accept-own-nexthop"); - break; - case COMMUNITY_BLACKHOLE: - len += strlen(" blackhole"); - break; - case COMMUNITY_NO_EXPORT: - len += strlen(" no-export"); - break; - case COMMUNITY_NO_ADVERTISE: - len += strlen(" no-advertise"); - break; - case COMMUNITY_LOCAL_AS: - len += strlen(" local-AS"); - break; - case COMMUNITY_NO_PEER: - len += strlen(" no-peer"); - break; - default: - len = BUFSIZ; - break; - } - } - - /* Allocate memory. */ - str = XCALLOC(MTYPE_COMMUNITY_STR, len); - first = 1; /* Fill in string. */ for (i = 0; i < com->size; i++) { @@ -293,7 +192,7 @@ static void set_community_string(struct community *com, bool make_json, switch (comval) { case COMMUNITY_GSHUT: strlcat(str, "graceful-shutdown", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "gracefulShutdown"); json_object_array_add(json_community_list, @@ -302,7 +201,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_ACCEPT_OWN: strlcat(str, "accept-own", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "acceptown"); json_object_array_add(json_community_list, @@ -311,7 +210,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: strlcat(str, "route-filter-translated-v4", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "routeFilterTranslatedV4"); json_object_array_add(json_community_list, @@ -320,7 +219,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_ROUTE_FILTER_v4: strlcat(str, "route-filter-v4", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "routeFilterV4"); json_object_array_add(json_community_list, @@ -329,7 +228,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: strlcat(str, "route-filter-translated-v6", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "routeFilterTranslatedV6"); json_object_array_add(json_community_list, @@ -338,7 +237,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_ROUTE_FILTER_v6: strlcat(str, "route-filter-v6", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "routeFilterV6"); json_object_array_add(json_community_list, @@ -347,7 +246,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_LLGR_STALE: strlcat(str, "llgr-stale", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "llgrStale"); json_object_array_add(json_community_list, @@ -356,7 +255,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_NO_LLGR: strlcat(str, "no-llgr", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "noLlgr"); json_object_array_add(json_community_list, @@ -365,7 +264,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_ACCEPT_OWN_NEXTHOP: strlcat(str, "accept-own-nexthop", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "acceptownnexthop"); json_object_array_add(json_community_list, @@ -374,7 +273,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_BLACKHOLE: strlcat(str, "blackhole", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string( "blackhole"); json_object_array_add(json_community_list, @@ -383,7 +282,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_NO_EXPORT: strlcat(str, "no-export", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string("noExport"); json_object_array_add(json_community_list, @@ -392,7 +291,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_NO_ADVERTISE: strlcat(str, "no-advertise", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string("noAdvertise"); json_object_array_add(json_community_list, @@ -401,7 +300,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_LOCAL_AS: strlcat(str, "local-AS", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string("localAs"); json_object_array_add(json_community_list, json_string); @@ -409,7 +308,7 @@ static void set_community_string(struct community *com, bool make_json, break; case COMMUNITY_NO_PEER: strlcat(str, "no-peer", len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string("noPeer"); json_object_array_add(json_community_list, json_string); @@ -424,7 +323,7 @@ static void set_community_string(struct community *com, bool make_json, translate_alias ? bgp_community2alias(buf) : buf; strlcat(str, com2alias, len); - if (make_json) { + if (json_community_list) { json_string = json_object_new_string(com2alias); json_object_array_add(json_community_list, json_string); @@ -432,6 +331,121 @@ static void set_community_string(struct community *com, bool make_json, break; } } +} + +/* Convert communities attribute to string. + + For Well-known communities value, below keyword is used. + + 0xFFFF0000 "graceful-shutdown" + 0xFFFF0001 "accept-own" + 0xFFFF0002 "route-filter-translated-v4" + 0xFFFF0003 "route-filter-v4" + 0xFFFF0004 "route-filter-translated-v6" + 0xFFFF0005 "route-filter-v6" + 0xFFFF0006 "llgr-stale" + 0xFFFF0007 "no-llgr" + 0xFFFF0008 "accept-own-nexthop" + 0xFFFF029A "blackhole" + 0xFFFFFF01 "no-export" + 0xFFFFFF02 "no-advertise" + 0xFFFFFF03 "local-AS" + 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) +{ + int i; + char *str; + int len; + uint32_t comval; + json_object *json_community_list = NULL; + + if (!com) + return; + + if (make_json) { + com->json = json_object_new_object(); + json_community_list = json_object_new_array(); + } + + /* When communities attribute is empty. */ + if (com->size == 0) { + str = XMALLOC(MTYPE_COMMUNITY_STR, 1); + str[0] = '\0'; + + if (make_json) { + json_object_string_add(com->json, "string", ""); + json_object_object_add(com->json, "list", + json_community_list); + } + com->str = str; + return; + } + + /* Memory allocation is time consuming work. So we calculate + required string length first. */ + len = 0; + + for (i = 0; i < com->size; i++) { + memcpy(&comval, com_nthval(com, i), sizeof(uint32_t)); + comval = ntohl(comval); + + switch (comval) { + case COMMUNITY_GSHUT: + len += strlen(" graceful-shutdown"); + break; + case COMMUNITY_ACCEPT_OWN: + len += strlen(" accept-own"); + break; + case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: + len += strlen(" route-filter-translated-v4"); + break; + case COMMUNITY_ROUTE_FILTER_v4: + len += strlen(" route-filter-v4"); + break; + case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: + len += strlen(" route-filter-translated-v6"); + break; + case COMMUNITY_ROUTE_FILTER_v6: + len += strlen(" route-filter-v6"); + break; + case COMMUNITY_LLGR_STALE: + len += strlen(" llgr-stale"); + break; + case COMMUNITY_NO_LLGR: + len += strlen(" no-llgr"); + break; + case COMMUNITY_ACCEPT_OWN_NEXTHOP: + len += strlen(" accept-own-nexthop"); + break; + case COMMUNITY_BLACKHOLE: + len += strlen(" blackhole"); + break; + case COMMUNITY_NO_EXPORT: + len += strlen(" no-export"); + break; + case COMMUNITY_NO_ADVERTISE: + len += strlen(" no-advertise"); + break; + case COMMUNITY_LOCAL_AS: + len += strlen(" local-AS"); + break; + case COMMUNITY_NO_PEER: + len += strlen(" no-peer"); + break; + default: + len = BUFSIZ; + break; + } + } + + /* Allocate memory. */ + str = XCALLOC(MTYPE_COMMUNITY_STR, len); + + community_json_list(com, make_json ? json_community_list : NULL, str, + len, translate_alias); if (make_json) { json_object_string_add(com->json, "string", str); From 1622cfce103f93a6212f336932a611a22f1eb878 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 11 Sep 2024 13:54:43 +0200 Subject: [PATCH 4/9] bgpd: add community API to return json object The community_get_json() API is used to return a json object. At display time, this object will use the community_list_json_to_string() function to handle the output. Signed-off-by: Philippe Guibert --- bgpd/bgp_community.c | 148 +++++++++++++++++++++++++++++++++---------- bgpd/bgp_community.h | 1 + bgpd/bgp_route.c | 12 ++-- 3 files changed, 122 insertions(+), 39 deletions(-) diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index f804c9b0b511..ec5d5692adee 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -169,7 +169,8 @@ struct community *community_uniq_sort(struct community *com) */ static void community_json_list(struct community *com, json_object *json_community_list, char *str, - int len, bool translate_alias) + int len, bool translate_alias, + struct printbuf *pb, bool incremental_print) { int i; int first; @@ -191,8 +192,12 @@ static void community_json_list(struct community *com, switch (comval) { case COMMUNITY_GSHUT: - strlcat(str, "graceful-shutdown", len); - if (json_community_list) { + if (str) + strlcat(str, "graceful-shutdown", len); + if (incremental_print) + sprintbuf(pb, "%s\"gracefulShutdown\"", + i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "gracefulShutdown"); json_object_array_add(json_community_list, @@ -200,8 +205,11 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_ACCEPT_OWN: - strlcat(str, "accept-own", len); - if (json_community_list) { + if (str) + strlcat(str, "accept-own", len); + if (incremental_print) + sprintbuf(pb, "%s\"acceptOwn\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "acceptown"); json_object_array_add(json_community_list, @@ -209,8 +217,12 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: - strlcat(str, "route-filter-translated-v4", len); - if (json_community_list) { + if (str) + strlcat(str, "route-filter-translated-v4", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterTranslateV4\"", + i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "routeFilterTranslatedV4"); json_object_array_add(json_community_list, @@ -218,8 +230,12 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_ROUTE_FILTER_v4: - strlcat(str, "route-filter-v4", len); - if (json_community_list) { + if (str) + strlcat(str, "route-filter-v4", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterV4\"", + i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "routeFilterV4"); json_object_array_add(json_community_list, @@ -227,8 +243,12 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: - strlcat(str, "route-filter-translated-v6", len); - if (json_community_list) { + if (str) + strlcat(str, "route-filter-translated-v6", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterTranslatedV6\"", + i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "routeFilterTranslatedV6"); json_object_array_add(json_community_list, @@ -236,8 +256,12 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_ROUTE_FILTER_v6: - strlcat(str, "route-filter-v6", len); - if (json_community_list) { + if (str) + strlcat(str, "route-filter-v6", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterV6\"", + i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "routeFilterV6"); json_object_array_add(json_community_list, @@ -245,8 +269,11 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_LLGR_STALE: - strlcat(str, "llgr-stale", len); - if (json_community_list) { + if (str) + strlcat(str, "llgr-stale", len); + if (incremental_print) + sprintbuf(pb, "%s\"llgrStale\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "llgrStale"); json_object_array_add(json_community_list, @@ -254,8 +281,11 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_NO_LLGR: - strlcat(str, "no-llgr", len); - if (json_community_list) { + if (str) + strlcat(str, "no-llgr", len); + if (incremental_print) + sprintbuf(pb, "%s\"noLlgr\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "noLlgr"); json_object_array_add(json_community_list, @@ -263,8 +293,12 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_ACCEPT_OWN_NEXTHOP: - strlcat(str, "accept-own-nexthop", len); - if (json_community_list) { + if (str) + strlcat(str, "accept-own-nexthop", len); + if (incremental_print) + sprintbuf(pb, "%s\"acceptownnexthop\"", + i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "acceptownnexthop"); json_object_array_add(json_community_list, @@ -272,8 +306,11 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_BLACKHOLE: - strlcat(str, "blackhole", len); - if (json_community_list) { + if (str) + strlcat(str, "blackhole", len); + if (incremental_print) + sprintbuf(pb, "%s\"blackhole\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string( "blackhole"); json_object_array_add(json_community_list, @@ -281,8 +318,11 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_NO_EXPORT: - strlcat(str, "no-export", len); - if (json_community_list) { + if (str) + strlcat(str, "no-export", len); + if (incremental_print) + sprintbuf(pb, "%s\"noExport\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string("noExport"); json_object_array_add(json_community_list, @@ -290,8 +330,11 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_NO_ADVERTISE: - strlcat(str, "no-advertise", len); - if (json_community_list) { + if (str) + strlcat(str, "no-advertise", len); + if (incremental_print) + sprintbuf(pb, "%s\"noAdvertise\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string("noAdvertise"); json_object_array_add(json_community_list, @@ -299,16 +342,22 @@ static void community_json_list(struct community *com, } break; case COMMUNITY_LOCAL_AS: - strlcat(str, "local-AS", len); - if (json_community_list) { + if (str) + strlcat(str, "local-AS", len); + if (incremental_print) + sprintbuf(pb, "%s\"localAs\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string("localAs"); json_object_array_add(json_community_list, json_string); } break; case COMMUNITY_NO_PEER: - strlcat(str, "no-peer", len); - if (json_community_list) { + if (str) + strlcat(str, "no-peer", len); + if (incremental_print) + sprintbuf(pb, "%s\"noPeer\"", i ? "," : ""); + else if (json_community_list) { json_string = json_object_new_string("noPeer"); json_object_array_add(json_community_list, json_string); @@ -322,8 +371,14 @@ static void community_json_list(struct community *com, const char *com2alias = translate_alias ? bgp_community2alias(buf) : buf; - strlcat(str, com2alias, len); - if (json_community_list) { + if (str) + strlcat(str, com2alias, len); + if (incremental_print) + sprintbuf(pb, "%s\"%s\"", i ? "," : "", + translate_alias + ? bgp_community2alias(buf) + : buf); + else if (json_community_list) { json_string = json_object_new_string(com2alias); json_object_array_add(json_community_list, json_string); @@ -445,7 +500,7 @@ static void set_community_string(struct community *com, bool make_json, str = XCALLOC(MTYPE_COMMUNITY_STR, len); community_json_list(com, make_json ? json_community_list : NULL, str, - len, translate_alias); + len, translate_alias, NULL, false); if (make_json) { json_object_string_add(com->json, "string", str); @@ -933,6 +988,25 @@ static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg) *aggr_community = community_dup(hb_community); } +static int community_list_json_to_string(struct json_object *jso, + struct printbuf *pb, int level, + int flags) +{ + struct community *com; + + com = json_object_get_userdata(jso); + assert(com); + if (!com->str) + return 0; + printbuf_strappend(pb, "{\"string\":\""); + printbuf_memappend(pb, com->str, strlen(com->str)); + printbuf_strappend(pb, "\",\"list\":["); + + community_json_list(com, NULL, NULL, 0, true, pb, true); + printbuf_strappend(pb, "]}"); + return 0; +} + void bgp_aggr_community_remove(void *arg) { struct community *community = arg; @@ -1054,3 +1128,13 @@ void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate, } } } + +json_object *community_get_json(struct community *com) +{ + json_object *json_community_list = NULL; + + json_community_list = json_object_new_object(); + json_object_set_serializer(json_community_list, + community_list_json_to_string, com, NULL); + return json_community_list; +} diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 7c7e7af4d29d..85d7b384434e 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -60,6 +60,7 @@ extern void community_free(struct community **comm); 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 json_object *community_get_json(struct community *com); extern void community_unintern(struct community **com); extern char *community_str(struct community *com, bool make_json, bool translate_alias); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7be8f1e202fd..c5215e88003f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10516,6 +10516,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_object *json_string = NULL; json_object *json_adv_to = NULL; json_object *json_aspath = NULL; + json_object *json_community = NULL; int first = 0; struct listnode *node, *nnode; struct peer *peer; @@ -11215,13 +11216,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, /* Line 4 display Community */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) { if (json_paths) { - if (!bgp_attr_get_community(attr)->json) - community_str(bgp_attr_get_community(attr), - true, true); - json_object_lock(bgp_attr_get_community(attr)->json); - json_object_object_add( - json_path, "community", - bgp_attr_get_community(attr)->json); + json_community = + community_get_json(bgp_attr_get_community(attr)); + json_object_object_add(json_path, "community", + json_community); } else { vty_out(vty, " Community: %s\n", bgp_attr_get_community(attr)->str); From 5d9a13e019ad0a8a9d6648cf30b628c1e44c01b9 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 11 Sep 2024 13:54:43 +0200 Subject: [PATCH 5/9] bgpd: remove make_json option from bgp community list The bgp community list library stores a json pointer that is no more used. Remove the 'make_json' argument of the associated functions. Remove the 'json' attribute from the bgp community structure. Signed-off-by: Philippe Guibert --- bgpd/bgp_attr.c | 2 +- bgpd/bgp_clist.c | 2 +- bgpd/bgp_community.c | 134 +++---------------------------------------- bgpd/bgp_community.h | 6 +- bgpd/bgp_debug.c | 6 +- bgpd/bgp_routemap.c | 2 +- bgpd/bgp_vty.c | 4 +- 7 files changed, 17 insertions(+), 139 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 7cdf98cba706..08f60cde1aa0 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -989,7 +989,7 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) attr->origin, attr->weight, attr->label, sid, attr->aigp_metric); vty_out(vty, "\taspath: %s Community: %s Large Community: %s\n", aspath_print(attr->aspath), - community_str(attr->community, false, false), + community_str(attr->community, false), lcommunity_str(attr->lcommunity, false, false)); vty_out(vty, "\tExtended Community: %s Extended IPv6 Community: %s\n", ecommunity_str(attr->ecommunity), diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index ad154e638b55..3f4871464ea9 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -540,7 +540,7 @@ static bool community_regexp_match(struct community *com, regex_t *reg) if (com == NULL || com->size == 0) str = ""; else - str = community_str(com, false, true); + str = community_str(com, true); regstr = bgp_alias2community_str(str); diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index ec5d5692adee..3629b18e02b5 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -33,11 +33,6 @@ void community_free(struct community **com) XFREE(MTYPE_COMMUNITY_VAL, (*com)->val); XFREE(MTYPE_COMMUNITY_STR, (*com)->str); - if ((*com)->json) { - json_object_free((*com)->json); - (*com)->json = NULL; - } - XFREE(MTYPE_COMMUNITY, (*com)); } @@ -150,7 +145,6 @@ struct community *community_uniq_sort(struct community *com) return NULL; new = community_new(); - new->json = NULL; for (i = 0; i < com->size; i++) { val = community_val_get(com, i); @@ -167,16 +161,14 @@ struct community *community_uniq_sort(struct community *com) /* Convert communities attribute to string and optionally add json objects * into the pased json_community_list string */ -static void community_json_list(struct community *com, - json_object *json_community_list, char *str, - int len, bool translate_alias, - struct printbuf *pb, bool incremental_print) +static void community_json_list(struct community *com, char *str, int len, + bool translate_alias, struct printbuf *pb, + bool incremental_print) { int i; int first; first = 1; uint32_t comval; - json_object *json_string = NULL; uint16_t as; uint16_t val; @@ -197,24 +189,12 @@ static void community_json_list(struct community *com, if (incremental_print) sprintbuf(pb, "%s\"gracefulShutdown\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "gracefulShutdown"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_ACCEPT_OWN: if (str) strlcat(str, "accept-own", len); if (incremental_print) sprintbuf(pb, "%s\"acceptOwn\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "acceptown"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: if (str) @@ -222,12 +202,6 @@ static void community_json_list(struct community *com, if (incremental_print) sprintbuf(pb, "%s\"routeFilterTranslateV4\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "routeFilterTranslatedV4"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_ROUTE_FILTER_v4: if (str) @@ -235,12 +209,6 @@ static void community_json_list(struct community *com, if (incremental_print) sprintbuf(pb, "%s\"routeFilterV4\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "routeFilterV4"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: if (str) @@ -248,12 +216,6 @@ static void community_json_list(struct community *com, if (incremental_print) sprintbuf(pb, "%s\"routeFilterTranslatedV6\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "routeFilterTranslatedV6"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_ROUTE_FILTER_v6: if (str) @@ -261,36 +223,18 @@ static void community_json_list(struct community *com, if (incremental_print) sprintbuf(pb, "%s\"routeFilterV6\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "routeFilterV6"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_LLGR_STALE: if (str) strlcat(str, "llgr-stale", len); if (incremental_print) sprintbuf(pb, "%s\"llgrStale\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "llgrStale"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_NO_LLGR: if (str) strlcat(str, "no-llgr", len); if (incremental_print) sprintbuf(pb, "%s\"noLlgr\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "noLlgr"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_ACCEPT_OWN_NEXTHOP: if (str) @@ -298,70 +242,36 @@ static void community_json_list(struct community *com, if (incremental_print) sprintbuf(pb, "%s\"acceptownnexthop\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "acceptownnexthop"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_BLACKHOLE: if (str) strlcat(str, "blackhole", len); if (incremental_print) sprintbuf(pb, "%s\"blackhole\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string( - "blackhole"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_NO_EXPORT: if (str) strlcat(str, "no-export", len); if (incremental_print) sprintbuf(pb, "%s\"noExport\"", i ? "," : ""); - else if (json_community_list) { - json_string = - json_object_new_string("noExport"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_NO_ADVERTISE: if (str) strlcat(str, "no-advertise", len); if (incremental_print) sprintbuf(pb, "%s\"noAdvertise\"", i ? "," : ""); - else if (json_community_list) { - json_string = - json_object_new_string("noAdvertise"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_LOCAL_AS: if (str) strlcat(str, "local-AS", len); if (incremental_print) sprintbuf(pb, "%s\"localAs\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string("localAs"); - json_object_array_add(json_community_list, - json_string); - } break; case COMMUNITY_NO_PEER: if (str) strlcat(str, "no-peer", len); if (incremental_print) sprintbuf(pb, "%s\"noPeer\"", i ? "," : ""); - else if (json_community_list) { - json_string = json_object_new_string("noPeer"); - json_object_array_add(json_community_list, - json_string); - } break; default: as = CHECK_FLAG((comval >> 16), 0xFFFF); @@ -378,11 +288,6 @@ static void community_json_list(struct community *com, translate_alias ? bgp_community2alias(buf) : buf); - else if (json_community_list) { - json_string = json_object_new_string(com2alias); - json_object_array_add(json_community_list, - json_string); - } break; } } @@ -408,33 +313,21 @@ static void community_json_list(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 translate_alias) { int i; char *str; int len; uint32_t comval; - json_object *json_community_list = NULL; if (!com) return; - if (make_json) { - com->json = json_object_new_object(); - json_community_list = json_object_new_array(); - } - /* When communities attribute is empty. */ if (com->size == 0) { str = XMALLOC(MTYPE_COMMUNITY_STR, 1); str[0] = '\0'; - if (make_json) { - json_object_string_add(com->json, "string", ""); - json_object_object_add(com->json, "list", - json_community_list); - } com->str = str; return; } @@ -499,13 +392,8 @@ static void set_community_string(struct community *com, bool make_json, /* Allocate memory. */ str = XCALLOC(MTYPE_COMMUNITY_STR, len); - community_json_list(com, make_json ? json_community_list : NULL, str, - len, translate_alias, NULL, false); + community_json_list(com, str, len, translate_alias, NULL, false); - if (make_json) { - json_object_string_add(com->json, "string", str); - json_object_object_add(com->json, "list", json_community_list); - } com->str = str; } @@ -530,7 +418,7 @@ struct community *community_intern(struct community *com) /* Make string. */ if (!find->str) - set_community_string(find, false, true); + set_community_string(find, true); return find; } @@ -591,16 +479,13 @@ 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 translate_alias) { 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, translate_alias); return com->str; } @@ -910,7 +795,6 @@ struct community *community_str2com(const char *str) case community_token_no_peer: if (com == NULL) { com = community_new(); - com->json = NULL; } community_add_val(com, val); break; @@ -1002,7 +886,7 @@ static int community_list_json_to_string(struct json_object *jso, printbuf_memappend(pb, com->str, strlen(com->str)); printbuf_strappend(pb, "\",\"list\":["); - community_json_list(com, NULL, NULL, 0, true, pb, true); + community_json_list(com, NULL, 0, true, pb, true); printbuf_strappend(pb, "]}"); return 0; } diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 85d7b384434e..c1ed3245bd47 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -21,9 +21,6 @@ struct community { /* Communities value. */ uint32_t *val; - /* Communities as a json object */ - json_object *json; - /* String of community attribute. This sring is used by vty output and expanded community-list for regular expression match. */ char *str; @@ -62,8 +59,7 @@ extern struct community *community_parse(uint32_t *pnt, unsigned short length); extern struct community *community_intern(struct community *com); extern json_object *community_get_json(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 translate_alias); 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_debug.c b/bgpd/bgp_debug.c index 97c3e5740f6e..af1beb5fcd41 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -433,10 +433,8 @@ bool bgp_dump_attr(struct attr *attr, char *buf, size_t size) attr->med); if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) - snprintf(buf + strlen(buf), size - strlen(buf), - ", community %s", - community_str(bgp_attr_get_community(attr), false, - true)); + snprintf(buf + strlen(buf), size - strlen(buf), ", community %s", + community_str(bgp_attr_get_community(attr), true)); if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) snprintf(buf + strlen(buf), size - strlen(buf), diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index d37b1710635a..1d2551f708be 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -6504,7 +6504,7 @@ DEFUN_YANG (set_community, XFREE(MTYPE_TMP, str); /* Set communites 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_vty.c b/bgpd/bgp_vty.c index f669564bb84d..50bbd3c481ef 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16418,7 +16418,7 @@ static void community_show_all_iterator(struct hash_bucket *bucket, com = (struct community *)bucket->data; vty_out(vty, "[%p] (%ld) %s\n", (void *)com, com->refcnt, - community_str(com, false, false)); + community_str(com, false)); } /* Show BGP's community internal data. */ @@ -22204,7 +22204,7 @@ 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); else From 1e0524f74ddb37a9e25b7e1e32e798289e7332cb Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 3 Sep 2024 16:41:48 +0200 Subject: [PATCH 6/9] bgpd: move large community list build in a separate function Create the lcommunity_json_list() function, that returns the large community list output in a returned buffer, and optionally in the json large community list passed array. This commit does not have any functional impact. Signed-off-by: Philippe Guibert --- bgpd/bgp_lcommunity.c | 77 ++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 1b8c22a5038c..ff1e350f84f2 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -161,43 +161,25 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, return lcom1; } -static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, +/* 3 32-bit integers, 2 colons, and a space */ +#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1) + +/* Convert large communities attribute to string and optionally add json objects + * into the pased json_community_list string + */ +static char *lcommunity_json_list(struct lcommunity *lcom, + json_object *json_lcommunity_list, bool translate_alias) { - int i; - int len; - char *str_buf; + int i, len; const uint8_t *pnt; uint32_t global, local1, local2; - json_object *json_lcommunity_list = NULL; + char *str_buf; + size_t str_buf_sz; json_object *json_string = NULL; - /* 3 32-bit integers, 2 colons, and a space */ -#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1) - - if (!lcom) - return; - - if (make_json) { - lcom->json = json_object_new_object(); - json_lcommunity_list = json_object_new_array(); - } - - if (lcom->size == 0) { - str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1); - - if (make_json) { - json_object_string_add(lcom->json, "string", ""); - json_object_object_add(lcom->json, "list", - json_lcommunity_list); - } - - lcom->str = str_buf; - return; - } - /* 1 space + lcom->size lcom strings + null terminator */ - size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; + str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz); len = 0; @@ -233,12 +215,45 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, len = strlcat(str_buf, com2alias, str_buf_sz); - if (make_json) { + if (json_lcommunity_list) { json_string = json_object_new_string(com2alias); json_object_array_add(json_lcommunity_list, json_string); } } + return str_buf; +} + +static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, + bool translate_alias) +{ + char *str_buf; + json_object *json_lcommunity_list = NULL; + + if (!lcom) + return; + + if (make_json) { + lcom->json = json_object_new_object(); + json_lcommunity_list = json_object_new_array(); + } + + if (lcom->size == 0) { + str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1); + + if (make_json) { + json_object_string_add(lcom->json, "string", ""); + json_object_object_add(lcom->json, "list", + json_lcommunity_list); + } + + lcom->str = str_buf; + return; + } + + str_buf = lcommunity_json_list(lcom, + make_json ? json_lcommunity_list : NULL, + translate_alias); if (make_json) { json_object_string_add(lcom->json, "string", str_buf); From a5c615516ae2ecfc313c3f66b2a0c8feea7d070d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 3 Sep 2024 18:27:51 +0200 Subject: [PATCH 7/9] bgpd: add large community API to return json object The lcommunity_get_json() API is used to return a json object. At display time, this object will use the lcommunity_json_to_string() function to handle the output. Signed-off-by: Philippe Guibert --- bgpd/bgp_lcommunity.c | 62 +++++++++++++++++++++++++++++++++---------- bgpd/bgp_lcommunity.h | 1 + bgpd/bgp_route.c | 12 ++++----- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index ff1e350f84f2..96c1e00f8368 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -169,22 +169,25 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, */ static char *lcommunity_json_list(struct lcommunity *lcom, json_object *json_lcommunity_list, - bool translate_alias) + bool translate_alias, bool return_buffer, + struct printbuf *pb, bool incremental_print) { int i, len; const uint8_t *pnt; uint32_t global, local1, local2; - char *str_buf; - size_t str_buf_sz; + char *str_buf = NULL; + size_t str_buf_sz = 0; json_object *json_string = NULL; /* 1 space + lcom->size lcom strings + null terminator */ - str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; - str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz); + if (return_buffer) { + str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; + str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz); + } len = 0; for (i = 0; i < lcom->size; i++) { - if (i > 0) + if (return_buffer && i > 0) len = strlcat(str_buf, " ", str_buf_sz); pnt = lcom->val + (i * LCOMMUNITY_SIZE); @@ -207,15 +210,18 @@ static char *lcommunity_json_list(struct lcommunity *lcom, const char *com2alias = translate_alias ? bgp_community2alias(lcsb) : lcsb; size_t individual_len = strlen(com2alias); - if (individual_len + len > str_buf_sz) { - str_buf_sz = individual_len + len + 1; - str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, - str_buf_sz); + if (str_buf) { + if (individual_len + len > str_buf_sz) { + str_buf_sz = individual_len + len + 1; + str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, + str_buf, str_buf_sz); + } + len = strlcat(str_buf, com2alias, str_buf_sz); } - len = strlcat(str_buf, com2alias, str_buf_sz); - - if (json_lcommunity_list) { + if (incremental_print) + sprintbuf(pb, "%s\"%s\"", i ? "," : "", com2alias); + else if (json_lcommunity_list) { json_string = json_object_new_string(com2alias); json_object_array_add(json_lcommunity_list, json_string); @@ -253,7 +259,7 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, str_buf = lcommunity_json_list(lcom, make_json ? json_lcommunity_list : NULL, - translate_alias); + translate_alias, true, NULL, false); if (make_json) { json_object_string_add(lcom->json, "string", str_buf); @@ -567,6 +573,24 @@ static void bgp_aggr_lcommunity_prepare(struct hash_bucket *hb, void *arg) *aggr_lcommunity = lcommunity_dup(hb_lcommunity); } +static int lcommunity_json_to_string(struct json_object *jso, + struct printbuf *pb, int level, int flags) +{ + struct lcommunity *lcom; + + lcom = json_object_get_userdata(jso); + assert(lcom); + if (!lcom->str) + return 0; + printbuf_strappend(pb, "{\"string\":\""); + printbuf_memappend(pb, lcom->str, strlen(lcom->str)); + printbuf_strappend(pb, "\",\"list\":["); + + lcommunity_json_list(lcom, NULL, 0, false, pb, true); + printbuf_strappend(pb, "]}"); + return 0; +} + void bgp_aggr_lcommunity_remove(void *arg) { struct lcommunity *lcommunity = arg; @@ -687,3 +711,13 @@ void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate, } } } + +json_object *lcommunity_get_json(struct lcommunity *lcom) +{ + json_object *json_lcommunity = NULL; + + json_lcommunity = json_object_new_object(); + json_object_set_serializer(json_lcommunity, lcommunity_json_to_string, + lcom, NULL); + return json_lcommunity; +} diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h index 151b79fa63c4..91a04595f40e 100644 --- a/bgpd/bgp_lcommunity.h +++ b/bgpd/bgp_lcommunity.h @@ -48,6 +48,7 @@ extern struct lcommunity *lcommunity_merge(struct lcommunity *, struct lcommunity *); extern struct lcommunity *lcommunity_uniq_sort(struct lcommunity *); extern struct lcommunity *lcommunity_intern(struct lcommunity *); +extern json_object *lcommunity_get_json(struct lcommunity *lcom); extern bool lcommunity_cmp(const void *arg1, const void *arg2); extern void lcommunity_unintern(struct lcommunity **); extern unsigned int lcommunity_hash_make(const void *); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index c5215e88003f..220544f586ca 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10517,6 +10517,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_object *json_adv_to = NULL; json_object *json_aspath = NULL; json_object *json_community = NULL; + json_object *json_lcommunity = NULL; int first = 0; struct listnode *node, *nnode; struct peer *peer; @@ -11259,13 +11260,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, /* Line 6 display Large community */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) { if (json_paths) { - if (!bgp_attr_get_lcommunity(attr)->json) - lcommunity_str(bgp_attr_get_lcommunity(attr), - true, true); - json_object_lock(bgp_attr_get_lcommunity(attr)->json); - json_object_object_add( - json_path, "largeCommunity", - bgp_attr_get_lcommunity(attr)->json); + json_lcommunity = lcommunity_get_json( + bgp_attr_get_lcommunity(attr)); + json_object_object_add(json_path, "largeCommunity", + json_lcommunity); } else { vty_out(vty, " Large Community: %s\n", bgp_attr_get_lcommunity(attr)->str); From eb6b0a12d7ea89982aa8f30856909d9988d89fa8 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 11 Sep 2024 14:56:30 +0200 Subject: [PATCH 8/9] bgpd: remove make_json option from bgp large community The bgp large community library stores a json pointer that is no more used. Remove the 'make_json' argument of the associated functions. Remove the 'json' attribute from the bgp large community structure. Signed-off-by: Philippe Guibert --- bgpd/bgp_attr.c | 2 +- bgpd/bgp_clist.c | 2 +- bgpd/bgp_debug.c | 3 +-- bgpd/bgp_lcommunity.c | 52 ++++++++----------------------------------- bgpd/bgp_lcommunity.h | 6 +---- bgpd/bgp_vty.c | 4 ++-- 6 files changed, 15 insertions(+), 54 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 08f60cde1aa0..9182e9cce6fe 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -990,7 +990,7 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) vty_out(vty, "\taspath: %s Community: %s Large Community: %s\n", aspath_print(attr->aspath), community_str(attr->community, false), - lcommunity_str(attr->lcommunity, false, false)); + lcommunity_str(attr->lcommunity, false)); vty_out(vty, "\tExtended Community: %s Extended IPv6 Community: %s\n", ecommunity_str(attr->ecommunity), ecommunity_str(attr->ipv6_ecommunity)); diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 3f4871464ea9..01ec5b4b6bbf 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -614,7 +614,7 @@ static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) if (com == NULL || com->size == 0) str = ""; else - str = lcommunity_str(com, false, true); + str = lcommunity_str(com, true); regstr = bgp_alias2community_str(str); diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index af1beb5fcd41..7f0520c0fa0a 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -439,8 +439,7 @@ bool bgp_dump_attr(struct attr *attr, char *buf, size_t size) if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) snprintf(buf + strlen(buf), size - strlen(buf), ", large-community %s", - lcommunity_str(bgp_attr_get_lcommunity(attr), false, - true)); + lcommunity_str(bgp_attr_get_lcommunity(attr), true)); if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) snprintf(buf + strlen(buf), size - strlen(buf), diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 96c1e00f8368..a1b94f5d0501 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -36,8 +36,6 @@ void lcommunity_free(struct lcommunity **lcom) XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val); XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str); - if ((*lcom)->json) - json_object_free((*lcom)->json); XFREE(MTYPE_LCOMMUNITY, *lcom); } @@ -167,17 +165,15 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, /* Convert large communities attribute to string and optionally add json objects * into the pased json_community_list string */ -static char *lcommunity_json_list(struct lcommunity *lcom, - json_object *json_lcommunity_list, - bool translate_alias, bool return_buffer, - struct printbuf *pb, bool incremental_print) +static char *lcommunity_json_list(struct lcommunity *lcom, bool translate_alias, + bool return_buffer, struct printbuf *pb, + bool incremental_print) { int i, len; const uint8_t *pnt; uint32_t global, local1, local2; char *str_buf = NULL; size_t str_buf_sz = 0; - json_object *json_string = NULL; /* 1 space + lcom->size lcom strings + null terminator */ if (return_buffer) { @@ -221,51 +217,25 @@ static char *lcommunity_json_list(struct lcommunity *lcom, if (incremental_print) sprintbuf(pb, "%s\"%s\"", i ? "," : "", com2alias); - else if (json_lcommunity_list) { - json_string = json_object_new_string(com2alias); - json_object_array_add(json_lcommunity_list, - json_string); - } } return str_buf; } -static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, - bool translate_alias) +static void set_lcommunity_string(struct lcommunity *lcom, bool translate_alias) { char *str_buf; - json_object *json_lcommunity_list = NULL; if (!lcom) return; - if (make_json) { - lcom->json = json_object_new_object(); - json_lcommunity_list = json_object_new_array(); - } - if (lcom->size == 0) { str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1); - if (make_json) { - json_object_string_add(lcom->json, "string", ""); - json_object_object_add(lcom->json, "list", - json_lcommunity_list); - } - lcom->str = str_buf; return; } - str_buf = lcommunity_json_list(lcom, - make_json ? json_lcommunity_list : NULL, - translate_alias, true, NULL, false); - - if (make_json) { - json_object_string_add(lcom->json, "string", str_buf); - json_object_object_add(lcom->json, "list", - json_lcommunity_list); - } + str_buf = lcommunity_json_list(lcom, translate_alias, true, NULL, false); lcom->str = str_buf; } @@ -285,7 +255,7 @@ struct lcommunity *lcommunity_intern(struct lcommunity *lcom) find->refcnt++; if (!find->str) - set_lcommunity_string(find, false, true); + set_lcommunity_string(find, true); return find; } @@ -312,17 +282,13 @@ 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 translate_alias) { 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, translate_alias); return lcom->str; } @@ -586,7 +552,7 @@ static int lcommunity_json_to_string(struct json_object *jso, printbuf_memappend(pb, lcom->str, strlen(lcom->str)); printbuf_strappend(pb, "\",\"list\":["); - lcommunity_json_list(lcom, NULL, 0, false, pb, true); + lcommunity_json_list(lcom, 0, false, pb, true); printbuf_strappend(pb, "]}"); return 0; } diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h index 91a04595f40e..0d96c56875cb 100644 --- a/bgpd/bgp_lcommunity.h +++ b/bgpd/bgp_lcommunity.h @@ -25,9 +25,6 @@ struct lcommunity { /* Large Communities value. */ uint8_t *val; - /* Large Communities as a json object */ - json_object *json; - /* Human readable format string. */ char *str; }; @@ -56,8 +53,7 @@ extern struct hash *lcommunity_hash(void); extern struct lcommunity *lcommunity_str2com(const char *); extern bool lcommunity_match(const struct lcommunity *, const struct lcommunity *); -extern char *lcommunity_str(struct lcommunity *, bool make_json, - bool translate_alias); +extern char *lcommunity_str(struct lcommunity *, bool translate_alias); 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_vty.c b/bgpd/bgp_vty.c index 50bbd3c481ef..f0eb710c98fe 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16447,7 +16447,7 @@ static void lcommunity_show_all_iterator(struct hash_bucket *bucket, lcom = (struct lcommunity *)bucket->data; vty_out(vty, "[%p] (%ld) %s\n", (void *)lcom, lcom->refcnt, - lcommunity_str(lcom, false, false)); + lcommunity_str(lcom, false)); } /* Show BGP's community internal data. */ @@ -22206,7 +22206,7 @@ static const char *community_list_config_str(struct community_entry *entry) if (entry->style == COMMUNITY_LIST_STANDARD) 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; From 8a4fec880286e22934c56dfbcb9b4f3bbe95b132 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 12 Sep 2024 13:40:01 +0200 Subject: [PATCH 9/9] lib: add printbuf.h API from json This API proposes to use the sprintbuf() method to facilitate the forge of buffers in json. Note that this buffer is limited to 128 bytes, so it can not be used everywhere as it without any checks. Signed-off-by: Philippe Guibert --- lib/json.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/json.h b/lib/json.h index 4763803acd24..b6b146bc734e 100644 --- a/lib/json.h +++ b/lib/json.h @@ -13,6 +13,7 @@ extern "C" { #include "command.h" #include "printfrr.h" #include +#include /* * FRR style JSON iteration.