diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 4c1615a5c625..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); 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,27 +605,68 @@ 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); +} + +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\":["); - aspath_make_str_count(as, make_json); + 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; + + 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) { @@ -690,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); @@ -727,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; @@ -864,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++; @@ -1112,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; } @@ -1267,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; } @@ -1292,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; } @@ -1314,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; } @@ -1340,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; } @@ -1358,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; @@ -1412,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; } @@ -1468,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; } @@ -1485,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; } @@ -1505,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; } @@ -1550,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. */ @@ -1661,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 @@ -1679,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 @@ -1766,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 @@ -1804,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; } @@ -1895,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; } @@ -1956,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", @@ -2028,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; } @@ -2079,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; } @@ -2220,7 +2218,7 @@ struct aspath *aspath_str2aspath(const char *str, } } - aspath_make_str_count(aspath, false); + aspath_make_str_count(aspath); return aspath; } @@ -2232,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 f7e57fd66dda..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,9 +106,10 @@ 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); 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_attr.c b/bgpd/bgp_attr.c index 7cdf98cba706..9182e9cce6fe 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -989,8 +989,8 @@ 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), - 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(attr->ipv6_ecommunity)); diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index ad154e638b55..01ec5b4b6bbf 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); @@ -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_community.c b/bgpd/bgp_community.c index 602c1437af11..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); @@ -164,6 +158,141 @@ struct community *community_uniq_sort(struct community *com) return new; } +/* 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, char *str, int len, + bool translate_alias, struct printbuf *pb, + bool incremental_print) +{ + int i; + int first; + first = 1; + uint32_t comval; + uint16_t as; + uint16_t val; + + /* Fill in string. */ + for (i = 0; i < com->size; i++) { + memcpy(&comval, com_nthval(com, i), sizeof(uint32_t)); + comval = ntohl(comval); + + if (first) + first = 0; + else + strlcat(str, " ", len); + + switch (comval) { + case COMMUNITY_GSHUT: + if (str) + strlcat(str, "graceful-shutdown", len); + if (incremental_print) + sprintbuf(pb, "%s\"gracefulShutdown\"", + i ? "," : ""); + break; + case COMMUNITY_ACCEPT_OWN: + if (str) + strlcat(str, "accept-own", len); + if (incremental_print) + sprintbuf(pb, "%s\"acceptOwn\"", i ? "," : ""); + break; + case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: + if (str) + strlcat(str, "route-filter-translated-v4", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterTranslateV4\"", + i ? "," : ""); + break; + case COMMUNITY_ROUTE_FILTER_v4: + if (str) + strlcat(str, "route-filter-v4", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterV4\"", + i ? "," : ""); + break; + case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: + if (str) + strlcat(str, "route-filter-translated-v6", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterTranslatedV6\"", + i ? "," : ""); + break; + case COMMUNITY_ROUTE_FILTER_v6: + if (str) + strlcat(str, "route-filter-v6", len); + if (incremental_print) + sprintbuf(pb, "%s\"routeFilterV6\"", + i ? "," : ""); + break; + case COMMUNITY_LLGR_STALE: + if (str) + strlcat(str, "llgr-stale", len); + if (incremental_print) + sprintbuf(pb, "%s\"llgrStale\"", i ? "," : ""); + break; + case COMMUNITY_NO_LLGR: + if (str) + strlcat(str, "no-llgr", len); + if (incremental_print) + sprintbuf(pb, "%s\"noLlgr\"", i ? "," : ""); + break; + case COMMUNITY_ACCEPT_OWN_NEXTHOP: + if (str) + strlcat(str, "accept-own-nexthop", len); + if (incremental_print) + sprintbuf(pb, "%s\"acceptownnexthop\"", + i ? "," : ""); + break; + case COMMUNITY_BLACKHOLE: + if (str) + strlcat(str, "blackhole", len); + if (incremental_print) + sprintbuf(pb, "%s\"blackhole\"", i ? "," : ""); + break; + case COMMUNITY_NO_EXPORT: + if (str) + strlcat(str, "no-export", len); + if (incremental_print) + sprintbuf(pb, "%s\"noExport\"", i ? "," : ""); + break; + case COMMUNITY_NO_ADVERTISE: + if (str) + strlcat(str, "no-advertise", len); + if (incremental_print) + sprintbuf(pb, "%s\"noAdvertise\"", i ? "," : ""); + break; + case COMMUNITY_LOCAL_AS: + if (str) + strlcat(str, "local-AS", len); + if (incremental_print) + sprintbuf(pb, "%s\"localAs\"", i ? "," : ""); + break; + case COMMUNITY_NO_PEER: + if (str) + strlcat(str, "no-peer", len); + if (incremental_print) + sprintbuf(pb, "%s\"noPeer\"", i ? "," : ""); + break; + default: + as = CHECK_FLAG((comval >> 16), 0xFFFF); + 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; + + if (str) + strlcat(str, com2alias, len); + if (incremental_print) + sprintbuf(pb, "%s\"%s\"", i ? "," : "", + translate_alias + ? bgp_community2alias(buf) + : buf); + break; + } + } +} + /* Convert communities attribute to string. For Well-known communities value, below keyword is used. @@ -184,37 +313,21 @@ 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 translate_alias) { int i; char *str; int len; - int first; uint32_t comval; - 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; } @@ -278,165 +391,9 @@ static void set_community_string(struct community *com, bool make_json, /* Allocate memory. */ str = XCALLOC(MTYPE_COMMUNITY_STR, len); - first = 1; - - /* Fill in string. */ - for (i = 0; i < com->size; i++) { - memcpy(&comval, com_nthval(com, i), sizeof(uint32_t)); - comval = ntohl(comval); - if (first) - first = 0; - else - strlcat(str, " ", len); + community_json_list(com, str, len, translate_alias, NULL, false); - switch (comval) { - case COMMUNITY_GSHUT: - strlcat(str, "graceful-shutdown", len); - if (make_json) { - json_string = json_object_new_string( - "gracefulShutdown"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_ACCEPT_OWN: - strlcat(str, "accept-own", len); - if (make_json) { - json_string = json_object_new_string( - "acceptown"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: - strlcat(str, "route-filter-translated-v4", len); - if (make_json) { - json_string = json_object_new_string( - "routeFilterTranslatedV4"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_ROUTE_FILTER_v4: - strlcat(str, "route-filter-v4", len); - if (make_json) { - json_string = json_object_new_string( - "routeFilterV4"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: - strlcat(str, "route-filter-translated-v6", len); - if (make_json) { - json_string = json_object_new_string( - "routeFilterTranslatedV6"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_ROUTE_FILTER_v6: - strlcat(str, "route-filter-v6", len); - if (make_json) { - json_string = json_object_new_string( - "routeFilterV6"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_LLGR_STALE: - strlcat(str, "llgr-stale", len); - if (make_json) { - json_string = json_object_new_string( - "llgrStale"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_NO_LLGR: - strlcat(str, "no-llgr", len); - if (make_json) { - json_string = json_object_new_string( - "noLlgr"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_ACCEPT_OWN_NEXTHOP: - strlcat(str, "accept-own-nexthop", len); - if (make_json) { - json_string = json_object_new_string( - "acceptownnexthop"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_BLACKHOLE: - strlcat(str, "blackhole", len); - if (make_json) { - json_string = json_object_new_string( - "blackhole"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_NO_EXPORT: - strlcat(str, "no-export", len); - if (make_json) { - json_string = - json_object_new_string("noExport"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_NO_ADVERTISE: - strlcat(str, "no-advertise", len); - if (make_json) { - json_string = - json_object_new_string("noAdvertise"); - json_object_array_add(json_community_list, - json_string); - } - break; - case COMMUNITY_LOCAL_AS: - strlcat(str, "local-AS", len); - if (make_json) { - 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 (make_json) { - json_string = json_object_new_string("noPeer"); - json_object_array_add(json_community_list, - json_string); - } - break; - default: - as = CHECK_FLAG((comval >> 16), 0xFFFF); - 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; - - strlcat(str, com2alias, len); - if (make_json) { - json_string = json_object_new_string(com2alias); - json_object_array_add(json_community_list, - json_string); - } - break; - } - } - - if (make_json) { - json_object_string_add(com->json, "string", str); - json_object_object_add(com->json, "list", json_community_list); - } com->str = str; } @@ -461,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; } @@ -522,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; } @@ -841,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; @@ -919,6 +872,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, 0, true, pb, true); + printbuf_strappend(pb, "]}"); + return 0; +} + void bgp_aggr_community_remove(void *arg) { struct community *community = arg; @@ -1040,3 +1012,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..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; @@ -60,9 +57,9 @@ 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); +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..7f0520c0fa0a 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -433,16 +433,13 @@ 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), ", 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 1b8c22a5038c..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); } @@ -161,48 +159,31 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, return lcom1; } -static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, - bool translate_alias) +/* 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, bool translate_alias, + bool return_buffer, struct printbuf *pb, + bool incremental_print) { - 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; - 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; - } + char *str_buf = NULL; + size_t str_buf_sz = 0; /* 1 space + lcom->size lcom strings + null terminator */ - size_t 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); @@ -225,27 +206,37 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, 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 (make_json) { - json_string = json_object_new_string(com2alias); - json_object_array_add(json_lcommunity_list, - json_string); - } + if (incremental_print) + sprintbuf(pb, "%s\"%s\"", i ? "," : "", com2alias); } + return str_buf; +} + +static void set_lcommunity_string(struct lcommunity *lcom, bool translate_alias) +{ + char *str_buf; + + if (!lcom) + return; + + if (lcom->size == 0) { + str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1); - if (make_json) { - json_object_string_add(lcom->json, "string", str_buf); - json_object_object_add(lcom->json, "list", - json_lcommunity_list); + lcom->str = str_buf; + return; } + str_buf = lcommunity_json_list(lcom, translate_alias, true, NULL, false); + lcom->str = str_buf; } @@ -264,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; } @@ -291,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; } @@ -552,6 +539,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, 0, false, pb, true); + printbuf_strappend(pb, "]}"); + return 0; +} + void bgp_aggr_lcommunity_remove(void *arg) { struct lcommunity *lcommunity = arg; @@ -672,3 +677,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..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; }; @@ -48,6 +45,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 *); @@ -55,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_route.c b/bgpd/bgp_route.c index 3c0f0c8b53b1..220544f586ca 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10515,6 +10515,9 @@ 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; + json_object *json_community = NULL; + json_object *json_lcommunity = NULL; int first = 0; struct listnode *node, *nnode; struct peer *peer; @@ -10654,11 +10657,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); @@ -11216,13 +11217,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); @@ -11262,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); 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..f0eb710c98fe 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. */ @@ -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. */ @@ -22204,9 +22204,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/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, 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.