diff -uar client/client.c.orig client/client.c --- client/client.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ client/client.c 2017-10-11 13:59:34.000000000 -0400 @@ -30,6 +30,7 @@ #include #include "nest/bird.h" +#include "nest/cli.h" #include "lib/resource.h" #include "lib/string.h" #include "client/client.h" @@ -37,7 +38,7 @@ #define SERVER_READ_BUF_LEN 4096 -static char *opt_list = "s:vrl"; +static char *opt_list = "s:vrlq"; static int verbose, restricted, once; static char *init_cmd; @@ -53,13 +54,15 @@ static int num_lines, skip_input; int term_lns, term_cls; +static int quiet; +static int lastcode = -1; /*** Parsing of arguments ***/ static void usage(char *name) { - fprintf(stderr, "Usage: %s [-s ] [-v] [-r] [-l]\n", name); + fprintf(stderr, "Usage: %s [-s ] [-v] [-r] [-l] [-q]\n", name); exit(1); } @@ -86,6 +89,9 @@ if (!server_changed) server_path = xbasename(server_path); break; + case 'q': + quiet++; + break; default: usage(argv[0]); } @@ -274,23 +280,35 @@ if (*x == '+') /* Async reply */ PRINTF(len, ">>> %s\n", x+1); else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + PRINTF(len, "%s%s%s", verbose ? " " : "", x+1, lastcode == CLI_JSON_CODE ? "" : "\n"); else if (strlen(x) > 4 && sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && (x[4] == ' ' || x[4] == '-')) { + if (code && code == 1 && quiet) + { + code = 0; + quiet--; + } if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); + { + PRINTF(len, "%s%s", verbose ? x : x+5, code == CLI_JSON_CODE ? "" : "\n"); + lastcode = code; + } if (x[4] == ' ') { busy = 0; skip_input = 0; + lastcode = -1; return; } } else - PRINTF(len, "??? <%s>\n", x); + { + PRINTF(len, "??? <%s>\n", x); + lastcode = -1; + } if (interactive && busy && !skip_input && !init && (len > 0)) { diff -uar filter/filter.c.orig filter/filter.c --- filter/filter.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ filter/filter.c 2017-10-06 14:28:56.000000000 -0400 @@ -542,7 +542,7 @@ case T_PREFIX_SET: trie_format(v.val.ti, buf); return; case T_SET: tree_format(v.val.t, buf); return; case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return; - case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return; + case T_PATH: as_path_format(v.val.ad, buf2, 1000, 0); buffer_print(buf, "(path %s)", buf2); return; case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return; case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return; case T_LCLIST: lc_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return; diff -uar nest/a-path.c.orig nest/a-path.c --- nest/a-path.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/a-path.c 2017-10-11 10:27:21.000000000 -0400 @@ -123,47 +123,67 @@ return dst - dst_start; } -void -as_path_format(struct adata *path, byte *buf, uint size) +int +as_path_format(struct adata *path, byte *buf, uint size, int json) { byte *p = path->data; byte *e = p + path->length; byte *end = buf + size - 16; int sp = 1; int l, isset; + int cnt = 0; + if (json) + *buf++ = '['; while (p < e) { if (buf > end) { - strcpy(buf, " ..."); - return; + if (json) + strcpy(buf, "]"); + else + strcpy(buf, " ..."); + return (cnt); } isset = (*p++ == AS_PATH_SET); l = *p++; - if (isset) + if (isset && !json) { if (!sp) *buf++ = ' '; *buf++ = '{'; sp = 0; + cnt++; } while (l-- && buf <= end) { - if (!sp) - *buf++ = ' '; - buf += bsprintf(buf, "%u", get_as(p)); + if (!isset || !json) + { + if (!sp) + { + if (json) + *buf++ = ','; + else + *buf++ = ' '; + } + if (!isset) + cnt++; + buf += bsprintf(buf, "%u", get_as(p)); + sp = 0; + } p += BS; - sp = 0; } - if (isset) + if (isset && !json) { *buf++ = ' '; *buf++ = '}'; sp = 0; } } + if (json) + *buf++ = ']'; *buf = 0; + return (cnt); } int diff -uar nest/a-set.c.orig nest/a-set.c --- nest/a-set.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/a-set.c 2017-10-19 15:24:48.000000000 -0400 @@ -35,30 +35,50 @@ int_set_format(struct adata *set, int way, int from, byte *buf, uint size) { u32 *z = (u32 *) set->data; - byte *end = buf + size - 24; + byte *end = buf + size - 27; int from2 = MAX(from, 0); int to = set->length / 4; + int json; int i; + json = (way & FMT_JSON); + way &= ~(FMT_JSON); + if (json && from2 == 0) + *buf++ = '['; + for (i = from2; i < to; i++) { if (buf > end) { - if (from < 0) + if (from < 0 && json) + strcpy(buf, "]"); + else if (from < 0) strcpy(buf, " ..."); else *buf = 0; return i; } - if (i > from2) + if (i && json) + *buf++ = ','; + else if (i > from2 && !json) *buf++ = ' '; - if (way) + if (way && json) + buf += bsprintf(buf, "\"%d:%d\"", z[i] >> 16, z[i] & 0xffff); + else if (way) buf += bsprintf(buf, "(%d,%d)", z[i] >> 16, z[i] & 0xffff); else - buf += bsprintf(buf, "%R", z[i]); + { + if (json) + *buf++ = '\"'; + buf += bsprintf(buf, "%R", z[i]); + if (json) + *buf++ = '\"'; + } } + if (json) + *buf++ = ']'; *buf = 0; return 0; } diff -uar nest/attrs.h.orig nest/attrs.h --- nest/attrs.h.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/attrs.h 2017-10-06 17:00:08.000000000 -0400 @@ -30,7 +30,7 @@ struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as); int as_path_convert_to_old(struct adata *path, byte *dst, int *new_used); int as_path_convert_to_new(struct adata *path, byte *dst, int req_as); -void as_path_format(struct adata *path, byte *buf, uint size); +int as_path_format(struct adata *path, byte *buf, uint size, int json); int as_path_getlen(struct adata *path); int as_path_getlen_int(struct adata *path, int bs); int as_path_get_first(struct adata *path, u32 *orig_as); @@ -124,6 +124,7 @@ { memcpy(dst, src, LCOMM_LENGTH); return dst + 3; } +#define FMT_JSON 0x40000000 int int_set_format(struct adata *set, int way, int from, byte *buf, uint size); int ec_format(byte *buf, u64 ec); int ec_set_format(struct adata *set, int from, byte *buf, uint size); diff -uar nest/cli.h.orig nest/cli.h --- nest/cli.h.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/cli.h 2017-10-11 13:58:25.000000000 -0400 @@ -49,6 +49,7 @@ extern pool *cli_pool; extern struct cli *this_cli; /* Used during parsing */ +#define CLI_JSON_CODE 2999 #define CLI_ASYNC_CODE 10000 /* Functions to be called by command handlers */ diff -uar nest/config.Y.orig nest/config.Y --- nest/config.Y.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/config.Y 2017-10-06 12:48:53.000000000 -0400 @@ -63,6 +63,7 @@ CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) +CF_KEYWORDS(JSON) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) @@ -481,7 +482,7 @@ { if_show_summary(); } ; CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) -CF_CLI(SHOW ROUTE, r_args, [[[|for |for ] [table ] [filter |where ] [all] [primary] [filtered] [(export|preexport|noexport)

] [protocol

] [stats|count]]], [[Show routing table]]) +CF_CLI(SHOW ROUTE, r_args, [[[|for |for ] [table ] [filter |where ] [all] [primary] [filtered] [(export|preexport|noexport)

] [protocol

] [stats|count|json]]], [[Show routing table]]) { rt_show($3); } ; r_args: @@ -555,6 +556,10 @@ $$ = $1; $$->stats = 2; } + | r_args JSON { + $$ = $1; + $$->json = 1; + } ; export_mode: diff -uar nest/protocol.h.orig nest/protocol.h --- nest/protocol.h.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/protocol.h 2017-10-12 06:04:19.000000000 -0400 @@ -55,6 +55,8 @@ void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */ void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */ int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */ + int (*get_json_attr)(struct eattr *, byte *buf, int buflen); /* JSONify dynamic attribute (returns GA_*) */ + struct ea_list *(*get_json_defaults)(void); /* Get default attribute list for JSON output */ void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */ void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */ }; diff -uar nest/route.h.orig nest/route.h --- nest/route.h.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/route.h 2017-10-12 06:51:02.000000000 -0400 @@ -312,6 +312,7 @@ struct config *running_on_config; int net_counter, rt_counter, show_counter; int stats, show_for; + int json; }; void rt_show(struct rt_show_data *); @@ -519,7 +520,7 @@ static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; } void rta_dump(rta *); void rta_dump_all(void); -void rta_show(struct cli *, rta *, ea_list *); +void rta_show(struct cli *, rta *, ea_list *, int json); void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_addr *ll); /* diff -uar nest/rt-attr.c.orig nest/rt-attr.c --- nest/rt-attr.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/rt-attr.c 2017-10-12 08:33:52.000000000 -0400 @@ -808,11 +808,18 @@ ea_show_int_set(struct cli *c, struct adata *ad, int way, byte *pos, byte *buf, byte *end) { int i = int_set_format(ad, way, 0, pos, end - pos); - cli_printf(c, -1012, "\t%s", buf); + int json = way & FMT_JSON; + if (json) + cli_printf(c, -CLI_JSON_CODE, "%s", buf); + else + cli_printf(c, -1012, "\t%s", buf); while (i) { i = int_set_format(ad, way, i, buf, end - buf - 1); - cli_printf(c, -1012, "\t\t%s", buf); + if (json) + cli_printf(c, -CLI_JSON_CODE, "%s", buf); + else + cli_printf(c, -1012, "\t\t%s", buf); } } @@ -844,75 +851,125 @@ * ea_show - print an &eattr to CLI * @c: destination CLI * @e: attribute to be printed + * @json: print with JSON * * This function takes an extended attribute represented by its &eattr * structure and prints it to the CLI according to the type information. * * If the protocol defining the attribute provides its own * get_attr() hook, it's consulted first. + * + * If the json argument is non-zero, this function only prints attributes + * identified by a protocol get_json_attr() hook. */ void -ea_show(struct cli *c, eattr *e) +ea_show(struct cli *c, eattr *e, int json) { struct protocol *p; int status = GA_UNKNOWN; + int cnt; struct adata *ad = (e->type & EAF_EMBEDDED) ? NULL : e->u.ptr; byte buf[CLI_MSG_SIZE]; byte *pos = buf, *end = buf + sizeof(buf); if (p = attr_class_to_protocol[EA_PROTO(e->id)]) { - pos += bsprintf(pos, "%s.", p->name); - if (p->get_attr) - status = p->get_attr(e, pos, end - pos); + if (!json) + { + pos += bsprintf(pos, "%s.", p->name); + if (p->get_attr) + status = p->get_attr(e, pos, end - pos); + } + else + { + *pos++ = ','; + *pos = '\0'; + if (p->get_json_attr) + status = p->get_json_attr(e, pos, end - pos); + /* Skip attributes without a JSON translation. */ + if (status < GA_NAME) + return; + } pos += strlen(pos); } - else if (EA_PROTO(e->id)) + else if (!json && EA_PROTO(e->id)) pos += bsprintf(pos, "%02x.", EA_PROTO(e->id)); - else + else if (!json) status = get_generic_attr(e, &pos, end - pos); + else + return; if (status < GA_NAME) pos += bsprintf(pos, "%02x", EA_ID(e->id)); if (status < GA_FULL) { *pos++ = ':'; - *pos++ = ' '; + if (!json) + *pos++ = ' '; switch (e->type & EAF_TYPE_MASK) { case EAF_TYPE_INT: bsprintf(pos, "%u", e->u.data); break; case EAF_TYPE_OPAQUE: + if (json) + *pos++ = '\"'; opaque_format(ad, pos, end - pos); + if (json) + strlcat(pos, "\"", end - pos); break; case EAF_TYPE_IP_ADDRESS: + if (json) + *pos++ = '\"'; bsprintf(pos, "%I", *(ip_addr *) ad->data); + if (json) + strlcat(pos, "\"", end - pos); break; case EAF_TYPE_ROUTER_ID: + if (json) + *pos++ = '\"'; bsprintf(pos, "%R", e->u.data); + if (json) + strlcat(pos, "\"", end - pos); break; case EAF_TYPE_AS_PATH: - as_path_format(ad, pos, end - pos); + cnt = as_path_format(ad, pos, end - pos, json); + if (!json) + break; + cli_printf(c, -CLI_JSON_CODE, "%s", buf); + bsprintf(buf, ",\"asnPathLength\":%d", cnt); break; case EAF_TYPE_BITFIELD: + if (json) + *pos++ = '\"'; bsprintf(pos, "%08x", e->u.data); + if (json) + strlcat(pos, "\"", end - pos); break; case EAF_TYPE_INT_SET: - ea_show_int_set(c, ad, 1, pos, buf, end); + ea_show_int_set(c, ad, json ? (1 | FMT_JSON) : 1, pos, buf, end); return; case EAF_TYPE_EC_SET: - ea_show_ec_set(c, ad, pos, buf, end); + if (!json) + ea_show_ec_set(c, ad, pos, buf, end); return; case EAF_TYPE_LC_SET: - ea_show_lc_set(c, ad, pos, buf, end); + if (!json) + ea_show_lc_set(c, ad, pos, buf, end); return; case EAF_TYPE_UNDEF: default: + if (json) + *pos++ = '\"'; bsprintf(pos, "", e->type); + if (json) + strlcat(pos, "\"", end - pos); } } - cli_printf(c, -1012, "\t%s", buf); + if (json) + cli_printf(c, -CLI_JSON_CODE, "%s", buf); + else + cli_printf(c, -1012, "\t%s", buf); } /** @@ -1248,19 +1305,20 @@ } void -rta_show(struct cli *c, rta *a, ea_list *eal) +rta_show(struct cli *c, rta *a, ea_list *eal, int json) { static char *src_names[] = { "dummy", "static", "inherit", "device", "static-device", "redirect", "RIP", "OSPF", "OSPF-IA", "OSPF-E1", "OSPF-E2", "BGP", "pipe" }; static char *cast_names[] = { "unicast", "broadcast", "multicast", "anycast" }; int i; - cli_printf(c, -1008, "\tType: %s %s %s", src_names[a->source], cast_names[a->cast], ip_scope_text(a->scope)); + if (!json) + cli_printf(c, -1008, "\tType: %s %s %s", src_names[a->source], cast_names[a->cast], ip_scope_text(a->scope)); if (!eal) eal = a->eattrs; for(; eal; eal=eal->next) for(i=0; icount; i++) - ea_show(c, &eal->attrs[i]); + ea_show(c, &eal->attrs[i], json); } /** diff -uar nest/rt-table.c.orig nest/rt-table.c --- nest/rt-table.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ nest/rt-table.c 2017-10-13 11:13:09.000000000 -0400 @@ -2417,21 +2417,39 @@ { /* Need to normalize the extended attributes */ ea_list *t = tmpa; + ea_list *json_defaults; + if (d->json && a->src->proto->proto->get_json_defaults) + json_defaults = a->src->proto->proto->get_json_defaults(); + else + json_defaults = NULL; t = ea_append(t, a->eattrs); - tmpa = alloca(ea_scan(t)); + tmpa = alloca(ea_scan(t) + ea_scan(json_defaults)); ea_merge(t, tmpa); + if (json_defaults) + { + memcpy(tmpa->attrs + tmpa->count, json_defaults->attrs, sizeof(eattr)*json_defaults->count); + tmpa->count += json_defaults->count; + } ea_sort(tmpa); } - if (get_route_info) - get_route_info(e, info, tmpa); + if (d->json) + cli_printf(c, -CLI_JSON_CODE, "%s{\"cidr\":\"%s\"", d->show_counter > 1 ? "," : "", ia); else - bsprintf(info, " (%d)", e->pref); - cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name, - tm, from, primary ? (sync_error ? " !" : " *") : "", info); - for (nh = a->nexthops; nh; nh = nh->next) - cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1); + { + + if (get_route_info) + get_route_info(e, info, tmpa); + else + bsprintf(info, " (%d)", e->pref); + cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name, + tm, from, primary ? (sync_error ? " !" : " *") : "", info); + for (nh = a->nexthops; nh; nh = nh->next) + cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1); + } if (d->verbose) - rta_show(c, a, tmpa); + rta_show(c, a, tmpa, d->json); + if (d->json) + cli_printf(c, -CLI_JSON_CODE, "}"); } static void @@ -2547,6 +2565,11 @@ struct fib *fib = &d->table->fib; struct fib_iterator *it = &d->fit; + if (d->json == 1) + { + cli_printf(c, -CLI_JSON_CODE, "["); + d->json = 2; + } FIB_ITERATE_START(fib, it, f) { net *n = (net *) f; @@ -2570,6 +2593,8 @@ FIB_ITERATE_END(f); if (d->stats) cli_printf(c, 14, "%d of %d routes for %d networks", d->show_counter, d->rt_counter, d->net_counter); + else if (d->json) + cli_printf(c, CLI_JSON_CODE, "]"); else cli_printf(c, 0, ""); done: @@ -2599,6 +2624,12 @@ if (d->filtered && (d->export_mode || d->primary_only)) cli_msg(0, ""); + /* JSON is not supported with stats or count. */ + if (d->json && d->stats) { + cli_msg(9001, "json not supported with stats or count options"); + return; + } + if (d->pxlen == 256) { FIB_ITERATE_INIT(&d->fit, &d->table->fib); diff -uar proto/bgp/attrs.c.orig proto/bgp/attrs.c --- proto/bgp/attrs.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ proto/bgp/attrs.c 2017-10-13 11:14:50.000000000 -0400 @@ -73,6 +73,8 @@ int allow_in_ebgp; int (*validate)(struct bgp_proto *p, byte *attr, int len); void (*format)(eattr *ea, byte *buf, int buflen); + char *jsonname; + void (*jsonformat)(eattr *ea, byte *buf, int buflen); }; #define IGNORE -1 @@ -298,43 +300,43 @@ static struct attr_desc bgp_attr_table[] = { { NULL, -1, 0, 0, 0, /* Undefined */ - NULL, NULL }, + NULL, NULL, NULL, NULL }, { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */ - bgp_check_origin, bgp_format_origin }, + bgp_check_origin, bgp_format_origin, NULL, NULL }, { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */ - NULL, NULL }, /* is checked by validate_as_path() as a special case */ + NULL, NULL, "asnPath", NULL /* XXX */ }, /* is checked by validate_as_path() as a special case */ { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */ - bgp_check_next_hop, bgp_format_next_hop }, + bgp_check_next_hop, bgp_format_next_hop, NULL, NULL }, { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */ - NULL, NULL }, + NULL, NULL, "cost", NULL }, { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */ - NULL, NULL }, + NULL, NULL, NULL, NULL }, { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ - NULL, NULL }, + NULL, NULL, NULL, NULL }, { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */ - bgp_check_aggregator, bgp_format_aggregator }, + bgp_check_aggregator, bgp_format_aggregator, NULL, NULL }, { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ - bgp_check_community, NULL }, + bgp_check_community, NULL, "communities", NULL /* XXX */ }, { "originator_id", 4, BAF_OPTIONAL, EAF_TYPE_ROUTER_ID, 0, /* BA_ORIGINATOR_ID */ - NULL, NULL }, + NULL, NULL, NULL, NULL }, { "cluster_list", -1, BAF_OPTIONAL, EAF_TYPE_INT_SET, 0, /* BA_CLUSTER_LIST */ - bgp_check_cluster_list, bgp_format_cluster_list }, - { .name = NULL }, /* BA_DPA */ - { .name = NULL }, /* BA_ADVERTISER */ - { .name = NULL }, /* BA_RCID_PATH */ + bgp_check_cluster_list, bgp_format_cluster_list, NULL, NULL }, + { .name = NULL, .jsonname = NULL }, /* BA_DPA */ + { .name = NULL, .jsonname = NULL }, /* BA_ADVERTISER */ + { .name = NULL, .jsonname = NULL }, /* BA_RCID_PATH */ { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_REACH_NLRI */ - bgp_check_reach_nlri, NULL }, + bgp_check_reach_nlri, NULL, NULL, NULL }, { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */ - bgp_check_unreach_nlri, NULL }, + bgp_check_unreach_nlri, NULL, NULL, NULL }, { "ext_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, 1, /* BA_EXT_COMMUNITY */ - bgp_check_ext_community, NULL }, + bgp_check_ext_community, NULL, NULL, NULL }, { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */ - NULL, NULL }, + NULL, NULL, NULL, NULL }, { "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */ - NULL, NULL }, + NULL, NULL, NULL, NULL }, [BA_LARGE_COMMUNITY] = { "large_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_LC_SET, 1, - bgp_check_large_community, NULL } + bgp_check_large_community, NULL, NULL, NULL } }; /* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH. @@ -342,6 +344,7 @@ */ #define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name) +#define JSONATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].jsonname) static inline struct adata * bgp_alloc_adata(struct linpool *pool, unsigned len) @@ -416,6 +419,26 @@ return wlen; } +static ea_list *bgp_json_defaults = NULL; + +ea_list * +bgp_get_json_defaults(void) +{ + if (bgp_json_defaults == NULL) + { + struct adata *commlist; + bgp_json_defaults = xmalloc(sizeof(ea_list) + sizeof(eattr)*2); + bgp_json_defaults->next = NULL; + bgp_json_defaults->flags = 0; + bgp_json_defaults->count = 2; + bgp_set_attr(bgp_json_defaults->attrs, BA_MULTI_EXIT_DISC, 0); + commlist = xmalloc(sizeof(struct adata)); + commlist->length = 0; + bgp_set_attr(bgp_json_defaults->attrs + 1, BA_COMMUNITY, (uintptr_t)commlist); + } + return (bgp_json_defaults); +} + static void aggregator_convert_to_old(struct adata *aggr, byte *dst, int *new_used) { @@ -1938,6 +1961,29 @@ return GA_NAME; } +int +bgp_get_json_attr(eattr *a, byte *buf, int buflen) +{ + uint i = EA_ID(a->id); + struct attr_desc *d; + int len; + + if (JSONATTR_KNOWN(i)) + { + d = &bgp_attr_table[i]; + len = bsprintf(buf, "\"%s\"", d->jsonname); + buf += len; + if (d->jsonformat) + { + *buf++ = ':'; + d->jsonformat(a, buf, buflen - len); + return GA_FULL; + } + return GA_NAME; + } + return GA_UNKNOWN; +} + void bgp_init_bucket_table(struct bgp_proto *p) { diff -uar proto/bgp/bgp.c.orig proto/bgp/bgp.c --- proto/bgp/bgp.c.orig 2016-12-22 17:53:39.000000000 -0500 +++ proto/bgp/bgp.c 2017-10-11 15:57:01.000000000 -0400 @@ -1598,6 +1598,8 @@ .copy_config = bgp_copy_config, .get_status = bgp_get_status, .get_attr = bgp_get_attr, + .get_json_attr = bgp_get_json_attr, + .get_json_defaults = bgp_get_json_defaults, .get_route_info = bgp_get_route_info, .show_proto_info = bgp_show_proto_info }; diff -uar proto/bgp/bgp.h.orig proto/bgp/bgp.h --- proto/bgp/bgp.h.orig 2016-12-22 17:53:39.000000000 -0500 +++ proto/bgp/bgp.h 2017-10-12 05:53:47.000000000 -0400 @@ -247,6 +247,7 @@ byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len); struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, uint len, struct linpool *pool, int mandatory); int bgp_get_attr(struct eattr *e, byte *buf, int buflen); +int bgp_get_json_attr(struct eattr *e, byte *buf, int buflen); int bgp_rte_better(struct rte *, struct rte *); int bgp_rte_mergable(rte *pri, rte *sec); int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best); @@ -260,6 +261,7 @@ void bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp); uint bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains); void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs); +ea_list *bgp_get_json_defaults(void); inline static void bgp_attach_attr_ip(struct ea_list **to, struct linpool *pool, unsigned attr, ip_addr a) { *(ip_addr *) bgp_attach_attr_wa(to, pool, attr, sizeof(ip_addr)) = a; }