[prev in list] [next in list] [prev in thread] [next in thread]
List: bird-users
Subject: Patch to output JSON from BIRD
From: Alistair Crooks <agc () netflix ! com>
Date: 2018-03-22 17:02:06
Message-ID: CADzBdM4CKx+zM83bidafGChW4-PDb_ez1g_SAXk+00g=jU7+VQ () mail ! gmail ! com
[Download RAW message or body]
[Attachment #2 (multipart/alternative)]
Hi folks,
I've attached a patch which outputs JSON from BIRD - this is used to save
some cycles - where previously we converted to JSON externally, this patch
enables output directly in JSON.
This is based on bird-1.6.3_4 (from FreeBSD ports)
Best,
Alistair
[Attachment #5 (text/html)]
<div dir="ltr">Hi folks,<div><br></div><div>I've attached a patch which outputs \
JSON from BIRD - this is used to save some cycles - where previously we converted to \
JSON externally, this patch enables output directly in \
JSON.</div><div><br></div><div>This is based on bird-1.6.3_4 (from FreeBSD \
ports)</div><div><br></div><div>Best,</div><div>Alistair</div><div><br></div><div><br></div></div>
["netflix_json.patch" (application/octet-stream)]
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 <sys/types.h>
#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 <control-socket>] [-v] [-r] [-l]\n", name);
+ fprintf(stderr, "Usage: %s [-s <control-socket>] [-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, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter \
<f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] \
[protocol <p>] [stats|count]]], [[Show routing table]]) +CF_CLI(SHOW ROUTE, r_args, \
[[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] \
[primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] \
[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, "<type %02x>", 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; i<eal->count; 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; }
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic