api: vat2 and json autogeneration for api messages

VAT2: A completely auto-generated replacement of VAT.
Reads input message in JSON from stdin and outputs received messages in JSON.

A VAT2 plugin is automatically built for a .api file.
There no longer a need for a separate _test.c.

Example:
vat2 show_version {}
{
        "_msgname":     "show_version_reply",
        "retval":       0,
        "program":      "vpe",
        "version":      "21.01-rc0~411-gf6eb348a6",
        "build_date":   "2020-11-19T09:49:25",
        "build_directory":      "/vpp/autogen3"
}

vat2 sw_interface_dump '{"sw_if_index": -1,
                         "name_filter_valid": 0,
                         "name_filter": ""}'
[{
                "_msgname":     "sw_interface_details",
                "sw_if_index":  0,
                "sup_sw_if_index":      0,
                "l2_address":   "00:00:00:00:00:00",
                "flags":        "Invalid ENUM",
                "type": "IF_API_TYPE_HARDWARE",
                "link_duplex":  "LINK_DUPLEX_API_UNKNOWN",
                "link_speed":   0,
                "link_mtu":     0,
                "mtu":  [0, 0, 0, 0],
                "sub_id":       0,
                "sub_number_of_tags":   0,
                "sub_outer_vlan_id":    0,
                "sub_inner_vlan_id":    0,
                "sub_if_flags": "Invalid ENUM",
                "vtr_op":       0,
                "vtr_push_dot1q":       0,
                "vtr_tag1":     0,
                "vtr_tag2":     0,
                "outer_tag":    0,
                "b_dmac":       "00:00:00:00:00:00",
                "b_smac":       "00:00:00:00:00:00",
                "b_vlanid":     0,
                "i_sid":        0,
                "interface_name":       "local0",
                "interface_dev_type":   "local",
                "tag":  ""
        }]

This is the first phase and vat2 is not integrated in packaging yet.

Type: feature
Signed-off-by: Ole Troan <ot@cisco.com>
Change-Id: Ib45ddeafb180ea7da8c5dc274a9274d7a4edc876
Signed-off-by: Ole Troan <ot@cisco.com>
diff --git a/src/vat2/CMakeLists.txt b/src/vat2/CMakeLists.txt
new file mode 100644
index 0000000..690267c
--- /dev/null
+++ b/src/vat2/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright (c) 2020 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+##############################################################################
+# vat2
+##############################################################################
+add_vpp_executable(vat2 ENABLE_EXPORTS NO_INSTALL
+  SOURCES
+  main.c
+  plugin.c
+  jsonconvert.c
+
+  DEPENDS api_headers
+
+  LINK_LIBRARIES
+  vlibmemoryclient
+  svm
+  vppinfra
+  vppapiclient
+  Threads::Threads
+  rt m dl crypto
+)
+
+##############################################################################
+# vat2 headers
+##############################################################################
+install(
+  FILES
+  jsonconvert.h
+  vat2_helpers.h
+  DESTINATION include/vat2
+  COMPONENT vpp-dev
+)
diff --git a/src/vat2/jsonconvert.c b/src/vat2/jsonconvert.c
new file mode 100644
index 0000000..3aeaeed
--- /dev/null
+++ b/src/vat2/jsonconvert.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vppinfra/cJSON.h>
+#include <vnet/ethernet/mac_address.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip_format_fns.h>
+#include <vpp/api/types.h>
+#include "jsonconvert.h"
+
+#define _(T)                                    \
+int vl_api_ ##T## _fromjson(cJSON *o, T *d)     \
+{                                               \
+    if (!cJSON_IsNumber(o)) return -1;          \
+    memcpy(d, &o->valueint, sizeof(T));         \
+    return 0;                                   \
+}
+  foreach_vat2_fromjson
+#undef _
+
+int vl_api_bool_fromjson(cJSON *o, bool *d)
+{
+    if (!cJSON_IsBool(o)) return -1;
+    *d = o->valueint ? true : false;
+    return 0;
+}
+
+int vl_api_u8_string_fromjson(cJSON *o, u8 *s, int len)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "0x%U", unformat_hex_string, s);
+    return 0;
+}
+
+u8 *
+u8string_fromjson(cJSON *o, char *fieldname)
+{
+    u8 *s = 0;
+    unformat_input_t input;
+    cJSON *item = cJSON_GetObjectItem(o, fieldname);
+    if (!item) {
+        printf("Illegal JSON, no such fieldname %s\n", fieldname);
+        return 0;
+    }
+
+    char *p = cJSON_GetStringValue(item);
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "0x%U", unformat_hex_string, &s);
+    return s;
+}
+
+int
+u8string_fromjson2(cJSON *o, char *fieldname, u8 *data)
+{
+    u8 *s = u8string_fromjson(o, fieldname);
+    if (!s) return 0;
+    memcpy(data, s, vec_len(s));
+    vec_free(s);
+    return 0;
+}
+
+/* Parse an IP4 address %d.%d.%d.%d. */
+uword
+unformat_ip4_address (unformat_input_t * input, va_list * args)
+{
+  u8 *result = va_arg (*args, u8 *);
+  unsigned a[4];
+
+  if (!unformat (input, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]))
+    return 0;
+
+  if (a[0] >= 256 || a[1] >= 256 || a[2] >= 256 || a[3] >= 256)
+    return 0;
+
+  result[0] = a[0];
+  result[1] = a[1];
+  result[2] = a[2];
+  result[3] = a[3];
+
+  return 1;
+}
+
+/* Parse an IP6 address. */
+uword
+unformat_ip6_address (unformat_input_t * input, va_list * args)
+{
+  ip6_address_t *result = va_arg (*args, ip6_address_t *);
+  u16 hex_quads[8];
+  uword hex_quad, n_hex_quads, hex_digit, n_hex_digits;
+  uword c, n_colon, double_colon_index;
+
+  n_hex_quads = hex_quad = n_hex_digits = n_colon = 0;
+  double_colon_index = ARRAY_LEN (hex_quads);
+  while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
+    {
+      hex_digit = 16;
+      if (c >= '0' && c <= '9')
+        hex_digit = c - '0';
+      else if (c >= 'a' && c <= 'f')
+        hex_digit = c + 10 - 'a';
+      else if (c >= 'A' && c <= 'F')
+        hex_digit = c + 10 - 'A';
+      else if (c == ':' && n_colon < 2)
+        n_colon++;
+      else
+        {
+          unformat_put_input (input);
+          break;
+        }
+
+      /* Too many hex quads. */
+      if (n_hex_quads >= ARRAY_LEN (hex_quads))
+        return 0;
+
+      if (hex_digit < 16)
+        {
+          hex_quad = (hex_quad << 4) | hex_digit;
+
+          /* Hex quad must fit in 16 bits. */
+          if (n_hex_digits >= 4)
+            return 0;
+
+          n_colon = 0;
+          n_hex_digits++;
+        }
+
+      /* Save position of :: */
+      if (n_colon == 2)
+        {
+          /* More than one :: ? */
+          if (double_colon_index < ARRAY_LEN (hex_quads))
+            return 0;
+          double_colon_index = n_hex_quads;
+        }
+
+      if (n_colon > 0 && n_hex_digits > 0)
+        {
+          hex_quads[n_hex_quads++] = hex_quad;
+          hex_quad = 0;
+          n_hex_digits = 0;
+        }
+    }
+
+  if (n_hex_digits > 0)
+    hex_quads[n_hex_quads++] = hex_quad;
+
+
+  {
+    word i;
+
+    /* Expand :: to appropriate number of zero hex quads. */
+    if (double_colon_index < ARRAY_LEN (hex_quads))
+      {
+        word n_zero = ARRAY_LEN (hex_quads) - n_hex_quads;
+
+        for (i = n_hex_quads - 1; i >= (signed) double_colon_index; i--)
+          hex_quads[n_zero + i] = hex_quads[i];
+
+        for (i = 0; i < n_zero; i++)
+          {
+            ASSERT ((double_colon_index + i) < ARRAY_LEN (hex_quads));
+            hex_quads[double_colon_index + i] = 0;
+          }
+
+        n_hex_quads = ARRAY_LEN (hex_quads);
+      }
+
+    /* Too few hex quads given. */
+    if (n_hex_quads < ARRAY_LEN (hex_quads))
+      return 0;
+
+    for (i = 0; i < ARRAY_LEN (hex_quads); i++)
+      result->as_u16[i] = clib_host_to_net_u16 (hex_quads[i]);
+
+    return 1;
+  }
+}
+
+u8 *
+format_ip6_address (u8 * s, va_list * args)
+{
+  ip6_address_t *a = va_arg (*args, ip6_address_t *);
+  u32 max_zero_run = 0, this_zero_run = 0;
+  int max_zero_run_index = -1, this_zero_run_index = 0;
+  int in_zero_run = 0, i;
+  int last_double_colon = 0;
+
+  /* Ugh, this is a pain. Scan forward looking for runs of 0's */
+  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
+    {
+      if (a->as_u16[i] == 0)
+        {
+          if (in_zero_run)
+            this_zero_run++;
+          else
+            {
+              in_zero_run = 1;
+              this_zero_run = 1;
+              this_zero_run_index = i;
+            }
+        }
+      else
+        {
+          if (in_zero_run)
+            {
+              /* offer to compress the biggest run of > 1 zero */
+              if (this_zero_run > max_zero_run && this_zero_run > 1)
+                {
+                  max_zero_run_index = this_zero_run_index;
+                  max_zero_run = this_zero_run;
+                }
+            }
+          in_zero_run = 0;
+          this_zero_run = 0;
+        }
+    }
+
+  if (in_zero_run)
+    {
+      if (this_zero_run > max_zero_run && this_zero_run > 1)
+        {
+          max_zero_run_index = this_zero_run_index;
+          max_zero_run = this_zero_run;
+        }
+    }
+
+  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
+    {
+      if (i == max_zero_run_index)
+        {
+          s = format (s, "::");
+          i += max_zero_run - 1;
+          last_double_colon = 1;
+        }
+      else
+        {
+          s = format (s, "%s%x",
+                      (last_double_colon || i == 0) ? "" : ":",
+                      clib_net_to_host_u16 (a->as_u16[i]));
+          last_double_colon = 0;
+        }
+    }
+
+  return s;
+}
+
+void *vl_api_ip4_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_address_t *a)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    if (!p) return 0;
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "%U", unformat_ip4_address, a);
+    return mp;
+}
+
+void *vl_api_ip4_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    if (!p) return 0;
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "%U/%d", unformat_ip4_address, &a->address, &a->len);
+    return mp;
+}
+
+void *vl_api_ip4_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a)
+{
+  return vl_api_ip4_prefix_t_fromjson(mp, len, o, a);
+}
+void *vl_api_ip6_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_address_t *a)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    if (!p) return 0;
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "%U", unformat_ip6_address, a);
+    return mp;
+}
+
+void *vl_api_ip6_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a)
+{
+  unformat_input_t input;
+  char *p = cJSON_GetStringValue(o);
+  if (!p) return 0;
+  unformat_init_string (&input, p, strlen(p));
+  unformat(&input, "%U/%d", unformat_ip6_address, &a->address, &a->len);
+  return mp;
+}
+
+void *vl_api_ip6_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a)
+{
+  return vl_api_ip6_prefix_t_fromjson(mp, len, o, a);
+}
+
+void *vl_api_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_address_t *a)
+{
+  unformat_input_t input;
+
+  char *p = cJSON_GetStringValue(o);
+  if (!p) return 0;
+  unformat_init_string (&input, p, strlen(p));
+  if (a->af == ADDRESS_IP4)
+    unformat(&input, "%U", unformat_ip4_address, &a->un.ip4);
+  else if (a->af == ADDRESS_IP6)
+    unformat(&input, "%U", unformat_ip6_address, &a->un.ip6);
+  else
+    return 0;
+  return mp;
+}
+
+void *vl_api_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a)
+{
+  unformat_input_t input;
+
+  char *p = cJSON_GetStringValue(o);
+  if (!p) return 0;
+  unformat_init_string (&input, p, strlen(p));
+  if (a->address.af == ADDRESS_IP4)
+    unformat(&input, "%U/%d", unformat_ip4_address, &a->address.un.ip4, &a->len);
+  else if (a->address.af == ADDRESS_IP6)
+    unformat(&input, "%U/%d", unformat_ip6_address, &a->address.un.ip6, &a->len);
+  else
+    return 0;
+  return mp;
+}
+
+void *vl_api_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a)
+{
+  return vl_api_prefix_t_fromjson(mp, len, o, a);
+}
+
+uword
+unformat_mac_address (unformat_input_t * input, va_list * args)
+{
+  mac_address_t *mac = va_arg (*args, mac_address_t *);
+  u32 i, a[3];
+
+  if (unformat (input, "%_%X:%X:%X:%X:%X:%X%_",
+                1, &mac->bytes[0], 1, &mac->bytes[1], 1, &mac->bytes[2],
+                1, &mac->bytes[3], 1, &mac->bytes[4], 1, &mac->bytes[5]))
+    return (1);
+  else if (unformat (input, "%_%x.%x.%x%_", &a[0], &a[1], &a[2]))
+    {
+      for (i = 0; i < ARRAY_LEN (a); i++)
+        if (a[i] >= (1 << 16))
+          return 0;
+
+      mac->bytes[0] = (a[0] >> 8) & 0xff;
+      mac->bytes[1] = (a[0] >> 0) & 0xff;
+      mac->bytes[2] = (a[1] >> 8) & 0xff;
+      mac->bytes[3] = (a[1] >> 0) & 0xff;
+      mac->bytes[4] = (a[2] >> 8) & 0xff;
+      mac->bytes[5] = (a[2] >> 0) & 0xff;
+
+      return (1);
+    }
+  return (0);
+}
+
+void *vl_api_mac_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_mac_address_t *a)
+{
+  unformat_input_t input;
+
+  char *p = cJSON_GetStringValue(o);
+  unformat_init_string (&input, p, strlen(p));
+  unformat(&input, "%U", unformat_mac_address, a);
+  return mp;
+}
+
+/* Format an IP4 address. */
+u8 *
+format_ip4_address (u8 * s, va_list * args)
+{
+  u8 *a = va_arg (*args, u8 *);
+  return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
+}
+
+int
+vl_api_c_string_to_api_string (const char *buf, vl_api_string_t * str)
+{
+  /* copy without nul terminator */
+  u32 len = strlen (buf);
+  if (len > 0)
+    clib_memcpy_fast (str->buf, buf, len);
+  str->length = htonl (len);
+  return len + sizeof (u32);
+}
+
+u8 *
+format_vl_api_interface_index_t (u8 *s, va_list *args)
+{
+  u32 *a = va_arg (*args, u32 *);
+  return format (s, "%u", *a);
+}
+
+uword
+unformat_vl_api_interface_index_t (unformat_input_t * input, va_list * args)
+{
+    u32 *a = va_arg (*args, u32 *);
+
+    if (!unformat (input, "%u", a))
+        return 0;
+    return 1;
+}
+
+void
+vl_api_string_cJSON_AddToObject(cJSON * const object, const char * const name, vl_api_string_t *astr)
+{
+
+    if (astr == 0) return;
+    u32 length = clib_net_to_host_u32 (astr->length);
+
+    char *cstr = malloc(length + 1);
+    memcpy(cstr, astr->buf, length);
+    cstr[length] = '\0';
+    cJSON_AddStringToObject(object, name, cstr);
+    free(cstr);
+}
+
+u8 *
+format_vl_api_timestamp_t(u8 * s, va_list * args)
+{
+    f64 timestamp = va_arg (*args, f64);
+    struct tm *tm;
+    word msec;
+
+    time_t t = timestamp;
+    tm = gmtime (&t);
+    msec = 1e6 * (timestamp - t);
+    return format (s, "%4d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm->tm_year,
+                   1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+                   tm->tm_sec, msec);
+}
+
+u8 *
+format_vl_api_timedelta_t(u8 * s, va_list * args)
+{
+    return format_vl_api_timestamp_t(s, args);
+}
+
+uword
+unformat_vl_api_timedelta_t(unformat_input_t * input, va_list * args)
+{
+    return 0;
+}
+
+uword
+unformat_vl_api_timestamp_t(unformat_input_t * input, va_list * args)
+{
+    return 0;
+}
+u8 *format_vl_api_gbp_scope_t(u8 * s, va_list * args)
+{
+    return 0;
+}
+uword unformat_vl_api_gbp_scope_t(unformat_input_t * input, va_list * args)
+{
+    return 0;
+}
+
+cJSON *
+vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a) {
+  u8 *s = format(0, "%U", format_vl_api_ip4_address_t, a);
+  cJSON *o = cJSON_CreateString((char *)s);
+  vec_free(s);
+  return o;
+}
+cJSON *
+vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a) {
+  u8 *s = format(0, "%U", format_vl_api_ip6_address_t, a);
+  cJSON *o = cJSON_CreateString((char *)s);
+  vec_free(s);
+  return o;
+}
+cJSON *
+vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a) {
+  u8 *s = format(0, "%U", format_vl_api_address_t, a);
+  cJSON *o = cJSON_CreateString((char *)s);
+  vec_free(s);
+  return o;
+}
+u8 *
+format_vl_api_mac_address_t (u8 * s, va_list * args)
+{
+  const mac_address_t *mac = va_arg (*args, mac_address_t *);
+
+  return format (s, "%02x:%02x:%02x:%02x:%02x:%02x",
+                 mac->bytes[0], mac->bytes[1], mac->bytes[2],
+                 mac->bytes[3], mac->bytes[4], mac->bytes[5]);
+}
+#define _(T)                                                \
+  cJSON *vl_api_ ##T## _t_tojson (vl_api_ ##T## _t *a) {   \
+  u8 *s = format(0, "%U", format_vl_api_ ##T## _t, a);      \
+  cJSON *o = cJSON_CreateString((char *)s);                 \
+  vec_free(s);                                              \
+  return o;                                                 \
+  }
+foreach_vat2_tojson
+#undef _
diff --git a/src/vat2/jsonconvert.h b/src/vat2/jsonconvert.h
new file mode 100644
index 0000000..2e723fa
--- /dev/null
+++ b/src/vat2/jsonconvert.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_json_convert_h
+#define included_json_convert_h
+
+#include <stdbool.h>
+#include <vppinfra/cJSON.h>
+#include <vnet/ethernet/mac_address.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip_types.api_types.h>
+#include <vnet/ethernet/ethernet_types.api_types.h>
+
+#define foreach_vat2_fromjson                   \
+  _(i8)                                         \
+  _(u8)                                         \
+  _(i16)                                        \
+  _(u16)                                        \
+  _(i32)                                        \
+  _(u32)                                        \
+  _(u64)                                        \
+  _(f64)
+
+#define _(T)                                    \
+  int vl_api_ ##T## _fromjson(cJSON *o, T *d);
+  foreach_vat2_fromjson
+#undef _
+
+int vl_api_bool_fromjson(cJSON *o, bool *d);
+void *vl_api_ip4_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_address_t *a);
+void *vl_api_ip4_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a);
+void *vl_api_ip4_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a);
+void *vl_api_ip6_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_address_t *a);
+void *vl_api_ip6_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a);
+void *vl_api_ip6_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a);
+void *vl_api_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_address_t *a);
+void *vl_api_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a);
+void *vl_api_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a);
+void *vl_api_mac_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_mac_address_t *a);
+
+uword unformat_ip4_address(unformat_input_t * input, va_list * args);
+uword unformat_ip6_address(unformat_input_t * input, va_list * args);
+u8 *format_ip6_address(u8 * s, va_list * args);
+uword unformat_mac_address(unformat_input_t * input, va_list * args);
+u8 *format_ip4_address(u8 * s, va_list * args);
+u8 *format_vl_api_interface_index_t(u8 *s, va_list *args);
+uword unformat_vl_api_interface_index_t(unformat_input_t * input, va_list * args);
+u8 *format_vl_api_timestamp_t(u8 * s, va_list * args);
+u8 *format_vl_api_timedelta_t(u8 * s, va_list * args);
+uword unformat_vl_api_timedelta_t(unformat_input_t * input, va_list * args);
+uword unformat_vl_api_timestamp_t(unformat_input_t * input, va_list * args);
+u8 *format_vl_api_gbp_scope_t(u8 * s, va_list * args);
+uword unformat_vl_api_gbp_scope_t(unformat_input_t * input, va_list * args);
+
+int vl_api_c_string_to_api_string(const char *buf, vl_api_string_t * str);
+void vl_api_string_cJSON_AddToObject(cJSON * const object, const char * const name, vl_api_string_t *astr);
+
+u8 *u8string_fromjson(cJSON *o, char *fieldname);
+int u8string_fromjson2(cJSON *o, char *fieldname, u8 *data);
+int vl_api_u8_string_fromjson(cJSON *o, u8 *s, int len);
+
+#define foreach_vat2_tojson                     \
+  _(ip4_address)                                \
+  _(ip4_prefix)                                 \
+  _(ip6_address)                                \
+  _(ip6_prefix)                                 \
+  _(address)                                    \
+  _(prefix)                                     \
+  _(mac_address)
+
+#define _(T)                                    \
+  cJSON *vl_api_ ##T## _t_tojson(vl_api_ ##T## _t *);
+  foreach_vat2_tojson
+#undef _
+
+cJSON *vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a);
+cJSON *vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a);
+cJSON *vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a);
+
+#endif
diff --git a/src/vat2/main.c b/src/vat2/main.c
new file mode 100644
index 0000000..5b042e2
--- /dev/null
+++ b/src/vat2/main.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <vlib/vlib.h>
+#include <vlibapi/api_types.h>
+#include <vppinfra/cJSON.h>
+
+/* VPP API client includes */
+#include <vpp-api/client/vppapiclient.h>
+
+#include <limits.h>
+#include "vat2.h"
+
+uword *function_by_name;
+bool debug = false;
+
+char *vat2_plugin_path;
+static void
+vat2_find_plugin_path ()
+{
+  char *p, path[PATH_MAX];
+  int rv;
+  u8 *s;
+
+  /* find executable path */
+  if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
+    return;
+
+  /* readlink doesn't provide null termination */
+  path[rv] = 0;
+
+  /* strip filename */
+  if ((p = strrchr (path, '/')) == 0)
+    return;
+  *p = 0;
+
+  /* strip bin/ */
+  if ((p = strrchr (path, '/')) == 0)
+    return;
+  *p = 0;
+
+  s = format (0, "%s/lib/" CLIB_TARGET_TRIPLET "/vat2_plugins:"
+              "%s/lib/vat2_plugins", path, path);
+  vec_add1 (s, 0);
+  vat2_plugin_path = (char *) s;
+}
+
+void
+vac_callback (unsigned char *data, int len)
+{
+  u16 result_msg_id = ntohs(*((u16 *)data));
+  DBG("Received something async: %d\n", result_msg_id);
+}
+
+int vat2_load_plugins (char *path, char *filter, int *loaded);
+
+static int
+register_function (void)
+{
+  int loaded;
+
+  vat2_find_plugin_path();
+  DBG("Plugin Path %s\n", vat2_plugin_path);
+  int rv = vat2_load_plugins(vat2_plugin_path, 0, &loaded);
+  DBG("Loaded %u plugins\n", loaded);
+  return rv;
+}
+
+void
+vat2_register_function(char *name, cJSON (*f)(cJSON *))
+{
+  hash_set_mem(function_by_name, name, f);
+}
+
+int main (int argc, char **argv)
+{
+  /* Create a heap of 64MB */
+  clib_mem_init (0, 64 << 20);
+  char *filename = 0;
+  int index;
+  int c;
+  opterr = 0;
+  cJSON *o = 0;
+  uword *p = 0;
+
+  while ((c = getopt (argc, argv, "df:")) != -1) {
+    switch (c) {
+      case 'd':
+        debug = true;
+        break;
+      case 'f':
+        filename = optarg;
+        break;
+      case '?':
+        if (optopt == 'f')
+          fprintf (stderr, "Option -%c requires an argument.\n", optopt);
+        else if (isprint (optopt))
+          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
+        else
+          fprintf (stderr,
+                   "Unknown option character `\\x%x'.\n",
+                   optopt);
+        return 1;
+      default:
+        abort ();
+    }
+  }
+
+  DBG("debug = %d, filename = %s\n", debug, filename);
+
+  for (index = optind; index < argc; index++)
+    DBG ("Non-option argument %s\n", argv[index]);
+
+  index = optind;
+
+  /* Load plugins */
+  function_by_name = hash_create_string (0, sizeof (uword));
+  int res = register_function();
+  if (res < 0) {
+    fprintf(stderr, "%s: loading plugins failed\n", argv[0]);
+    exit(-1);
+  }
+
+  if (argc > index + 2) {
+    fprintf(stderr, "%s: Too many arguments\n", argv[0]);
+    exit(-1);
+  }
+
+  /* Read JSON from stdin, command line or file */
+  if (argc >= (index + 1)) {
+    p = hash_get_mem (function_by_name, argv[index]);
+    if (p == 0) {
+      fprintf(stderr, "%s: Unknown command: %s\n", argv[0], argv[index]);
+      exit(-1);
+    }
+  }
+
+  if (argc == (index + 2)) {
+    o = cJSON_Parse(argv[index+1]);
+    if (!o) {
+      fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
+      exit(-1);
+    }
+  }
+
+  if (filename) {
+    if (argc > index + 1) {
+      fprintf(stderr, "%s: Superfluous arguments when filename given\n", argv[0]);
+      exit(-1);
+    }
+
+    FILE *f = fopen(filename, "r");
+    size_t bufsize = 1024;
+    size_t n_read = 0;
+    size_t n;
+
+    if (!f) {
+      fprintf(stderr, "%s: can't open file: %s\n", argv[0], filename);
+      exit(-1);
+    }
+    char *buf = malloc(bufsize);
+    while ((n = fread(buf, 1, bufsize, f))) {
+      n_read += n;
+      if (n == bufsize)
+        buf = realloc(buf, bufsize);
+    }
+    fclose(f);
+    if (n_read) {
+      o = cJSON_Parse(buf);
+      free(buf);
+      if (!o) {
+        fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
+        exit(-1);
+      }
+    }
+  }
+
+  if (!o) {
+    fprintf(stderr, "%s: Failed parsing JSON input\n", argv[0]);
+    exit(-1);
+  }
+
+  if (vac_connect("vat2", 0, 0, 1024)) {
+    fprintf(stderr, "Failed connecting to VPP\n");
+    exit(-1);
+  }
+  if (!p) {
+    fprintf(stderr, "No such command\n");
+    exit(-1);
+  }
+
+  cJSON * (*fp) (cJSON *);
+  fp = (void *) p[0];
+  cJSON *r = (*fp) (o);
+
+  if (o)
+    cJSON_Delete(o);
+
+  if (r) {
+    char *output = cJSON_Print(r);
+    cJSON_Delete(r);
+    printf("%s\n", output);
+    free(output);
+  } else {
+    fprintf(stderr, "Call failed\n");
+    exit(-1);
+  }
+
+  vac_disconnect();
+  exit (0);
+
+}
diff --git a/src/vat2/plugin.c b/src/vat2/plugin.c
new file mode 100644
index 0000000..6b6d55a
--- /dev/null
+++ b/src/vat2/plugin.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <vlib/vlib.h>
+#include "vat2.h"
+
+typedef struct
+{
+  u8 *name;
+  u8 *filename;
+  struct stat file_info;
+  void *handle;
+} plugin_info_t;
+
+/* loaded plugin info */
+plugin_info_t *plugin_info;
+
+static int
+load_one_plugin (plugin_info_t * pi)
+{
+  void *handle, *register_handle;
+  clib_error_t *(*fp) (void);
+  clib_error_t *error;
+
+  handle = dlopen ((char *) pi->name, RTLD_LAZY);
+
+  /*
+   * Note: this can happen if the plugin has an undefined symbol reference,
+   * so print a warning. Otherwise, the poor slob won't know what happened.
+   * Ask me how I know that...
+   */
+  if (handle == 0)
+    {
+      clib_warning ("%s", dlerror ());
+      return -1;
+    }
+
+  pi->handle = handle;
+
+  register_handle = dlsym (pi->handle, "vat2_register_plugin");
+  if (register_handle == 0)
+    {
+      clib_warning ("%s: symbol vat2_register_plugin not found", pi->name);
+      dlclose (handle);
+      return -1;
+    }
+
+  fp = register_handle;
+
+  error = (*fp) ();
+
+  if (error)
+    {
+      clib_error_report (error);
+      dlclose (handle);
+      return -1;
+    }
+
+  return 0;
+}
+
+static u8 **
+split_plugin_path (char *plugin_path)
+{
+  int i;
+  u8 **rv = 0;
+  u8 *path = (u8 *) plugin_path;
+  u8 *this = 0;
+
+  for (i = 0; i < vec_len (plugin_path); i++)
+    {
+      if (path[i] != ':')
+	{
+	  vec_add1 (this, path[i]);
+	  continue;
+	}
+      vec_add1 (this, 0);
+      vec_add1 (rv, this);
+      this = 0;
+    }
+  if (this)
+    {
+      vec_add1 (this, 0);
+      vec_add1 (rv, this);
+    }
+  return rv;
+}
+
+int
+vat2_load_plugins (char *path, char *filter, int *loaded)
+{
+  DIR *dp;
+  struct dirent *entry;
+  struct stat statb;
+  uword *p;
+  plugin_info_t *pi;
+  u8 **plugin_path;
+  int i;
+  int res = 0;
+  uword *plugin_by_name_hash = hash_create_string (0, sizeof (uword));
+
+  *loaded = 0;
+  plugin_path = split_plugin_path (path);
+
+  for (i = 0; i < vec_len (plugin_path); i++)
+    {
+      DBG ("Opening path: %s\n", plugin_path[i]);
+      dp = opendir ((char *) plugin_path[i]);
+
+      if (dp == 0)
+	continue;
+
+      while ((entry = readdir (dp)))
+	{
+	  u8 *plugin_name;
+
+	  if (filter)
+	    {
+	      int j;
+	      for (j = 0; j < vec_len (filter); j++)
+		if (entry->d_name[j] != filter[j])
+		  goto next;
+	    }
+
+	  plugin_name = format (0, "%s/%s%c", plugin_path[i],
+				entry->d_name, 0);
+
+	  /* unreadable */
+	  if (stat ((char *) plugin_name, &statb) < 0)
+	    {
+	    ignore:
+	      vec_free (plugin_name);
+	      continue;
+	    }
+
+	  /* a dir or other things which aren't plugins */
+	  if (!S_ISREG (statb.st_mode))
+	    goto ignore;
+
+	  p = hash_get_mem (plugin_by_name_hash, plugin_name);
+	  if (p == 0)
+	    {
+	      vec_add2 (plugin_info, pi, 1);
+	      pi->name = plugin_name;
+	      pi->file_info = statb;
+
+	      if (load_one_plugin (pi))
+		{
+		  res = -1;
+		  vec_free (plugin_name);
+		  _vec_len (plugin_info) = vec_len (plugin_info) - 1;
+		  continue;
+		}
+	      clib_memset (pi, 0, sizeof (*pi));
+	      hash_set_mem (plugin_by_name_hash, plugin_name,
+			    pi - plugin_info);
+	      *loaded = *loaded + 1;
+	    }
+	next:
+	  ;
+	}
+      closedir (dp);
+      vec_free (plugin_path[i]);
+    }
+  vec_free (plugin_path);
+  return res;
+}
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vat2/vat2.h b/src/vat2/vat2.h
new file mode 100644
index 0000000..d477b72
--- /dev/null
+++ b/src/vat2/vat2.h
@@ -0,0 +1,12 @@
+#ifndef included_vat2_h
+#define included_vat2_h
+
+#include <stdbool.h>
+
+extern bool debug;
+
+#define DBG(fmt, args...) do {if (debug) fprintf(stderr, fmt, ## args); } while(0)
+#define ERR(fmt, args...) fprintf(stderr, "VAT2: %s:%d:%s(): " fmt, \
+                                  __FILE__, __LINE__, __func__, ##args)
+
+#endif
diff --git a/src/vat2/vat2_helpers.h b/src/vat2/vat2_helpers.h
new file mode 100644
index 0000000..929c012
--- /dev/null
+++ b/src/vat2/vat2_helpers.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_vat2_helpers_h
+#define included_vat2_helpers_h
+
+/* For control ping */
+#define vl_endianfun
+#include <vpp/api/vpe.api.h>
+#undef vl_endianfun
+
+static inline void
+vat2_control_ping (u32 context)
+{
+    vl_api_control_ping_t mp = {0};
+    mp._vl_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_CRC);
+    mp.context = context;
+    vl_api_control_ping_t_endian(&mp);
+    vac_write((char *)&mp, sizeof(mp));
+}
+
+#endif