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