[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&#39;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