From 36217e3ca8a1ca2e7a341b6b44ffc25e6497191c Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Fri, 23 Jul 2021 08:51:10 +0000 Subject: api: API trace improvements Type: improvement * add support for JSON format in API trace * add ability to replay JSON API trace in both VPP and VAT2 * use CRC for backward compatibility check during JSON API replay * fix API trace CLI (and remove duplicits) * remove custom dump * remove vppapitrace.py * update docs accordingly Change-Id: I5294f68bebe6cbe738630f457f3a87720e06486b Signed-off-by: Filip Tehlar Signed-off-by: Ole Troan --- src/plugins/hs_apps/sapi/vpp_echo_bapi.c | 9 +- src/plugins/mactime/mactime_top.c | 14 +- src/plugins/tracedump/tracedump_test.c | 12 +- src/tools/vppapigen/vppapigen_c.py | 71 +++-- src/tools/vppapitrace/vppapitrace | 1 - src/tools/vppapitrace/vppapitrace.py | 492 ---------------------------- src/vat/api_format.c | 4 +- src/vat2/CMakeLists.txt | 4 - src/vat2/jsonconvert.c | 531 ------------------------------- src/vat2/jsonconvert.h | 105 ------ src/vat2/main.c | 68 +++- src/vat2/test/vat2_test.c | 1 + src/vcl/vcl_bapi.c | 9 +- src/vlibapi/api.h | 9 +- src/vlibapi/api_common.h | 25 +- src/vlibapi/api_shared.c | 283 ++++++++++------ src/vlibmemory/CMakeLists.txt | 6 + src/vlibmemory/memclnt.api | 4 +- src/vlibmemory/memclnt_api.c | 34 +- src/vlibmemory/memory_api.c | 30 +- src/vlibmemory/memory_client.c | 16 +- src/vlibmemory/socket_api.c | 31 +- src/vlibmemory/socket_client.c | 13 +- src/vlibmemory/vlib_api_cli.c | 506 ++++++++++++++++++++++++----- src/vnet/srmpls/sr_mpls_api.c | 31 +- src/vpp-api/client/client.c | 8 - src/vppinfra/CMakeLists.txt | 4 +- src/vppinfra/cJSON.c | 85 ++--- src/vppinfra/cJSON.h | 4 + src/vppinfra/jsonformat.c | 522 ++++++++++++++++++++++++++++++ src/vppinfra/jsonformat.h | 116 +++++++ 31 files changed, 1544 insertions(+), 1504 deletions(-) delete mode 120000 src/tools/vppapitrace/vppapitrace delete mode 100755 src/tools/vppapitrace/vppapitrace.py delete mode 100644 src/vat2/jsonconvert.c delete mode 100644 src/vat2/jsonconvert.h create mode 100644 src/vppinfra/jsonformat.c create mode 100644 src/vppinfra/jsonformat.h (limited to 'src') diff --git a/src/plugins/hs_apps/sapi/vpp_echo_bapi.c b/src/plugins/hs_apps/sapi/vpp_echo_bapi.c index 38fb522351c..30f3b78aff3 100644 --- a/src/plugins/hs_apps/sapi/vpp_echo_bapi.c +++ b/src/plugins/hs_apps/sapi/vpp_echo_bapi.c @@ -569,10 +569,11 @@ echo_api_hookup (echo_main_t * em) return; #define _(N, n) \ - vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_##N, #n, \ - vl_api_##n##_t_handler, vl_noop_handler, \ - vl_api_##n##_t_endian, vl_api_##n##_t_print, \ - sizeof (vl_api_##n##_t), 1); + vl_msg_api_set_handlers ( \ + REPLY_MSG_ID_BASE + VL_API_##N, #n, vl_api_##n##_t_handler, \ + vl_noop_handler, vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_print_json, \ + vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson); foreach_quic_echo_msg; #undef _ } diff --git a/src/plugins/mactime/mactime_top.c b/src/plugins/mactime/mactime_top.c index 72d1964f32f..ed4c7345721 100644 --- a/src/plugins/mactime/mactime_top.c +++ b/src/plugins/mactime/mactime_top.c @@ -143,14 +143,12 @@ connect_to_vpp (char *name) if (mm->msg_id_base == (u16) ~ 0) return -1; -#define _(N,n) \ - vl_msg_api_set_handlers((VL_API_##N + mm->msg_id_base), \ - #n, \ - vl_api_##n##_t_handler, \ - vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); +#define _(N, n) \ + vl_msg_api_set_handlers ((VL_API_##N + mm->msg_id_base), #n, \ + vl_api_##n##_t_handler, vl_noop_handler, \ + vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson); foreach_mactime_api_msg; #undef _ diff --git a/src/plugins/tracedump/tracedump_test.c b/src/plugins/tracedump/tracedump_test.c index 3bf50efb4ac..05191a49ac4 100644 --- a/src/plugins/tracedump/tracedump_test.c +++ b/src/plugins/tracedump/tracedump_test.c @@ -246,12 +246,12 @@ api_trace_clear_capture (vat_main_t * vam) void manual_setup_message_id_table (vat_main_t * vam) { - vl_msg_api_set_handlers (VL_API_TRACE_DETAILS - + tracedump_test_main.msg_id_base, "trace_details", - vl_api_trace_details_t_handler, vl_noop_handler, - vl_api_trace_details_t_endian, - vl_api_trace_details_t_print, - sizeof (vl_api_trace_details_t), 1); + vl_msg_api_set_handlers ( + VL_API_TRACE_DETAILS + tracedump_test_main.msg_id_base, "trace_details", + vl_api_trace_details_t_handler, vl_noop_handler, + vl_api_trace_details_t_endian, vl_api_trace_details_t_print, + sizeof (vl_api_trace_details_t), 1, vl_api_trace_details_t_print_json, + vl_api_trace_details_t_tojson, vl_api_trace_details_t_fromjson); } #define VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE manual_setup_message_id_table diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index db684adb06d..ae47625a474 100644 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -66,7 +66,10 @@ class ToJSON(): write('#ifndef included_{}_api_tojson_h\n'.format(self.module)) write('#define included_{}_api_tojson_h\n'.format(self.module)) write('#include \n\n') - write('#include \n\n') + write('#include \n\n') + if self.module == 'interface_types': + write('#define vl_printfun\n') + write('#include \n\n') def footer(self): '''Output the bottom boilerplate.''' @@ -231,6 +234,8 @@ class ToJSON(): write(' cJSON *o = cJSON_CreateObject();\n') write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n' .format(o.name)) + write(' cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n' + .format(crc=o.crc)) for t in o.block: self._dispatch[t.type](self, t) @@ -312,7 +317,7 @@ class FromJSON(): write('#ifndef included_{}_api_fromjson_h\n'.format(self.module)) write('#define included_{}_api_fromjson_h\n'.format(self.module)) write('#include \n\n') - write('#include \n\n') + write('#include \n\n') write('#pragma GCC diagnostic ignored "-Wunused-label"\n') def is_base_type(self, t): @@ -338,7 +343,7 @@ class FromJSON(): if o.modern_vla: write(' char *p = cJSON_GetStringValue(item);\n') write(' size_t plen = strlen(p);\n') - write(' {msgvar} = realloc({msgvar}, {msgsize} + plen);\n' + write(' {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n' .format(msgvar=msgvar, msgsize=msgsize)) write(' if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar)) write(' vl_api_c_string_to_api_string(p, (void *){msgvar} + ' @@ -393,7 +398,7 @@ class FromJSON(): cJSON *array = cJSON_GetObjectItem(o, "{n}"); int size = cJSON_GetArraySize(array); {lfield} = size; - {realloc} = realloc({realloc}, {msgsize} + sizeof({t}) * size); + {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize}); {t} *d = (void *){realloc} + {msgsize}; {msgsize} += sizeof({t}) * size; for (i = 0; i < size; i++) {{ @@ -419,8 +424,8 @@ class FromJSON(): write(' if (!s) goto error;\n') write(' {} = vec_len(s);\n'.format(lfield)) - write(' {realloc} = realloc({realloc}, {msgsize} + ' - 'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc)) + write(' {realloc} = cJSON_realloc({realloc}, {msgsize} + ' + 'vec_len(s), {msgsize});\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc)) write(' memcpy((void *){realloc} + {msgsize}, s, ' 'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize)) write(' {msgsize} += vec_len(s);\n'.format(msgsize=msgsize)) @@ -553,7 +558,7 @@ class FromJSON(): write(' cJSON *item __attribute__ ((unused));\n') write(' u8 *s __attribute__ ((unused));\n') write(' int l = sizeof(vl_api_{}_t);\n'.format(o.name)) - write(' vl_api_{}_t *a = malloc(l);\n'.format(o.name)) + write(' vl_api_{}_t *a = cJSON_malloc(l);\n'.format(o.name)) write('\n') for t in o.block: @@ -573,7 +578,7 @@ class FromJSON(): if error: write('\n error:\n') - write(' free(a);\n') + write(' cJSON_free(a);\n') write(' return 0;\n') write('}\n') @@ -928,10 +933,13 @@ def printfun(objs, stream, modulename): #define _uword_cast long #endif +#include "{module}.api_tojson.h" +#include "{module}.api_fromjson.h" + ''' signature = '''\ -static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle) +static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle) {{ u8 *s = 0; u32 indent __attribute__((unused)) = 2; @@ -946,7 +954,7 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle) if t.manual_print: write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name) continue - write(signature.format(name=t.name)) + write(signature.format(name=t.name, suffix='')) write(' /* Message definition: vl_api_{}_t: */\n'.format(t.name)) write(" s = format(s, \"vl_api_%s_t:\");\n" % t.name) for o in t.block: @@ -957,6 +965,16 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle) write(' return handle;\n') write('}\n\n') + write(signature.format(name=t.name, suffix='_json')) + write(' cJSON * o = vl_api_{}_t_tojson(a);\n'.format(t.name)) + write(' (void)s;\n'); + write(' char *out = cJSON_Print(o);\n') + write(' vl_print(handle, out);\n'); + write(' cJSON_Delete(o);\n') + write(' cJSON_free(out);\n'); + write(' return handle;\n') + write('}\n\n'); + write("\n#endif") write("\n#endif /* vl_printfun */\n") @@ -1346,6 +1364,9 @@ def generate_c_boilerplate(services, defines, counters, file_crc, ' .print = vl_api_{n}_t_print,\n' ' .traced = 1,\n' ' .replay = 1,\n' + ' .print_json = vl_api_{n}_t_print_json,\n' + ' .tojson = vl_api_{n}_t_tojson,\n' + ' .fromjson = vl_api_{n}_t_fromjson,\n' ' .is_autoendian = {auto}}};\n' .format(n=s.caller, ID=s.caller.upper(), auto=d.autoendian)) @@ -1359,6 +1380,11 @@ def generate_c_boilerplate(services, defines, counters, file_crc, ' .cleanup = vl_noop_handler,\n' ' .endian = vl_api_{n}_t_endian,\n' ' .print = vl_api_{n}_t_print,\n' + ' .traced = 1,\n' + ' .replay = 1,\n' + ' .print_json = vl_api_{n}_t_print_json,\n' + ' .tojson = vl_api_{n}_t_tojson,\n' + ' .fromjson = vl_api_{n}_t_fromjson,\n' ' .is_autoendian = {auto}}};\n' .format(n=s.reply, ID=s.reply.upper(), auto=d.autoendian)) @@ -1455,7 +1481,10 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, ' vl_noop_handler,\n' ' vl_api_{n}_t_endian, ' ' vl_api_{n}_t_print,\n' - ' sizeof(vl_api_{n}_t), 1);\n' + ' sizeof(vl_api_{n}_t), 1,\n' + ' vl_api_{n}_t_print_json,\n' + ' vl_api_{n}_t_tojson,\n' + ' vl_api_{n}_t_fromjson);\n' .format(n=s.reply, ID=s.reply.upper())) write(' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n' .format(n=s.caller)) @@ -1474,7 +1503,10 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, ' vl_noop_handler,\n' ' vl_api_{n}_t_endian, ' ' vl_api_{n}_t_print,\n' - ' sizeof(vl_api_{n}_t), 1);\n' + ' sizeof(vl_api_{n}_t), 1,\n' + ' vl_api_{n}_t_print_json,\n' + ' vl_api_{n}_t_tojson,\n' + ' vl_api_{n}_t_fromjson);\n' .format(n=e, ID=e.upper())) write('}\n') @@ -1526,7 +1558,7 @@ api_{n} (cJSON *o) mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC); vl_api_{n}_t_endian(mp); vac_write((char *)mp, len); - free(mp); + cJSON_free(mp); /* Read reply */ char *p; @@ -1559,7 +1591,7 @@ api_{n} (cJSON *o) mp->_vl_msg_id = msg_id; vl_api_{n}_t_endian(mp); vac_write((char *)mp, len); - free(mp); + cJSON_free(mp); vat2_control_ping(123); // FIX CONTEXT cJSON *reply = cJSON_CreateArray(); @@ -1615,7 +1647,7 @@ api_{n} (cJSON *o) vl_api_{n}_t_endian(mp); vac_write((char *)mp, len); - free(mp); + cJSON_free(mp); cJSON *reply = cJSON_CreateArray(); @@ -1713,13 +1745,16 @@ def generate_c_test2_boilerplate(services, defines, module, stream): continue c_test_api_service(s, s.stream, stream) - write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *));\n') + write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n') # write('__attribute__((constructor))') write('clib_error_t *\n') write('vat2_register_plugin (void) {\n') for s in services: - write(' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson);\n' - .format(n=s.caller)) + if s.reply not in define_hash: + continue + crc = define_hash[s.caller].crc + write(' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n' + .format(n=s.caller, crc=crc)) write(' return 0;\n') write('}\n') diff --git a/src/tools/vppapitrace/vppapitrace b/src/tools/vppapitrace/vppapitrace deleted file mode 120000 index d0ece85a809..00000000000 --- a/src/tools/vppapitrace/vppapitrace +++ /dev/null @@ -1 +0,0 @@ -vppapitrace.py \ No newline at end of file diff --git a/src/tools/vppapitrace/vppapitrace.py b/src/tools/vppapitrace/vppapitrace.py deleted file mode 100755 index 8089b3a2236..00000000000 --- a/src/tools/vppapitrace/vppapitrace.py +++ /dev/null @@ -1,492 +0,0 @@ -#!/usr/bin/env python3 - -# -# Copyright (c) 2019 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. -# - -# -# Convert from VPP API trace to JSON. - -import argparse -import struct -import sys -import logging -import json -from ipaddress import * -from collections import namedtuple -from vpp_papi import MACAddress, VPPApiJSONFiles -import base64 -import os -import textwrap - -def serialize_likely_small_unsigned_integer(x): - r = x - - # Low bit set means it fits into 1 byte. - if r < (1 << 7): - return struct.pack("B", 1 + 2 * r) - - # Low 2 bits 1 0 means it fits into 2 bytes. - r -= (1 << 7) - if r < (1 << 14): - return struct.pack("Q", data, offset+1)[0], 8 - - -def serialize_cstring(s): - bstring = s.encode('utf8') - l = len(bstring) - b = serialize_likely_small_unsigned_integer(l) - b += struct.pack('{}s'.format(l), bstring) - return b - - -def unserialize_cstring(data, offset): - l, size = unserialize_likely_small_unsigned_integer(data, offset) - name = struct.unpack_from('{}s'.format(l), data, offset+size)[0] - return name.decode('utf8'), size + len(name) - - -def unserialize_msgtbl(data, offset): - msgtable_by_id = {} - msgtable_by_name = {} - i = 0 - nmsg = struct.unpack_from(">I", data, offset)[0] - o = 4 - while i < nmsg: - (msgid, size) = unserialize_likely_small_unsigned_integer( - data, offset + o) - o += size - (name, size) = unserialize_cstring(data, offset + o) - o += size - msgtable_by_id[msgid] = name - msgtable_by_name[name] = msgid - - i += 1 - return msgtable_by_id, msgtable_by_name, o - - -def serialize_msgtbl(messages): - offset = 0 - # XXX 100K? - data = bytearray(100000) - nmsg = len(messages) - data = struct.pack(">I", nmsg) - - for k, v in messages.items(): - name = k + '_' + v.crc[2:] - data += serialize_likely_small_unsigned_integer(v._vl_msg_id) - data += serialize_cstring(name) - return data - - -def apitrace2json(messages, filename): - result = [] - with open(filename, 'rb') as file: - bytes_read = file.read() - # Read header - (nitems, msgtbl_size, wrapped) = struct.unpack_from(">IIB", - bytes_read, 0) - logging.debug('nitems: {} message table size: {} wrapped: {}' - .format(nitems, msgtbl_size, wrapped)) - if wrapped: - sys.stdout.write('Wrapped/incomplete trace, results may vary') - offset = 9 - - msgtbl_by_id, msgtbl_by_name, size = unserialize_msgtbl(bytes_read, - offset) - offset += size - - i = 0 - while i < nitems: - size = struct.unpack_from(">I", bytes_read, offset)[0] - offset += 4 - if size == 0: - break - msgid = struct.unpack_from(">H", bytes_read, offset)[0] - name = msgtbl_by_id[msgid] - n = name[:name.rfind("_")] - msgobj = messages[n] - if n + '_' + msgobj.crc[2:] != name: - sys.exit("CRC Mismatch between JSON API definition " - "and trace. {}".format(name)) - - x, s = msgobj.unpack(bytes_read[offset:offset+size]) - msgname = type(x).__name__ - offset += size - # Replace named tuple illegal _0 - y = x._asdict() - y.pop('_0') - result.append({'name': msgname, 'args': y}) - i += 1 - - file.close() - return result - - -def json2apitrace(messages, filename): - """Input JSON file and API message definition. Output API trace - bytestring.""" - - msgs = [] - with open(filename, 'r') as file: - msgs = json.load(file, object_hook=vpp_decode) - result = b'' - for m in msgs: - name = m['name'] - msgobj = messages[name] - m['args']['_vl_msg_id'] = messages[name]._vl_msg_id - b = msgobj.pack(m['args']) - - result += struct.pack('>I', len(b)) - result += b - return len(msgs), result - - -class VPPEncoder(json.JSONEncoder): - def default(self, o): - if type(o) is bytes: - return "base64:" + base64.b64encode(o).decode('utf-8') - # Let the base class default method raise the TypeError - return json.JSONEncoder.default(self, o) - - def encode(self, obj): - def hint_tuples(item): - if isinstance(item, tuple): - return hint_tuples(item._asdict()) - if isinstance(item, list): - return [hint_tuples(e) for e in item] - if isinstance(item, dict): - return {key: hint_tuples(value) for key, value in item.items()} - else: - return item - - return super(VPPEncoder, self).encode(hint_tuples(obj)) - - -def vpp_decode(obj): - for k, v in obj.items(): - if type(v) is str and v.startswith('base64:'): - s = v.lstrip('base64:') - obj[k] = base64.b64decode(v[7:]) - return obj - - -def vpp_encoder(obj): - if isinstance(obj, IPv6Network): - return str(obj) - if isinstance(obj, IPv4Network): - return str(obj) - if isinstance(obj, IPv6Address): - return str(obj) - if isinstance(obj, IPv4Address): - return str(obj) - if isinstance(obj, MACAddress): - return str(obj) - if type(obj) is bytes: - return "base64:" + base64.b64encode(obj).decode('ascii') - raise TypeError('Unknown object {} {}\n'.format(type(obj), obj)) - -message_filter = { - 'control_ping', - 'memclnt_create', - 'memclnt_delete', - 'get_first_msg_id', -} - -argument_filter = { - 'client_index', - 'context', -} - -def topython(messages, services): - import pprint - pp = pprint.PrettyPrinter() - - s = '''\ -#!/usr/bin/env python3 -from vpp_papi import VPP, VppEnum -vpp = VPP(use_socket=True) -vpp.connect(name='vppapitrace') -''' - - for m in messages: - if m['name'] not in services: - s += '# ignoring reply message: {}\n'.format(m['name']) - continue - if m['name'] in message_filter: - s += '# ignoring message {}\n'.format(m['name']) - continue - for k in argument_filter: - try: - m['args'].pop(k) - except KeyError: - pass - a = pp.pformat(m['args']) - s += 'rv = vpp.api.{}(**{})\n'.format(m['name'], a) - s += 'print("RV:", rv)\n' - s += 'vpp.disconnect()\n' - - return s - -def todump_items(k, v, level): - klen = len(k) if k else 0 - spaces = ' ' * level + ' ' * (klen + 3) - wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=spaces, width=60) - s = '' - if type(v) is dict: - if k: - s += ' ' * level + '{}:\n'.format(k) - for k2, v2 in v.items(): - s += todump_items(k2, v2, level + 1) - return s - - if type(v) is list: - for v2 in v: - s += '{}'.format(todump_items(k, v2, level)) - return s - - if type(v) is bytes: - w = wrapper.fill(bytes.hex(v)) - s += ' ' * level + '{}: {}\n'.format(k, w) - else: - if type(v) is str: - v = wrapper.fill(v) - s += ' ' * level + '{}: {}\n'.format(k, v) - return s - - -def todump(messages, services): - import pprint - pp = pprint.PrettyPrinter() - - s = '' - for m in messages: - if m['name'] not in services: - s += '# ignoring reply message: {}\n'.format(m['name']) - continue - #if m['name'] in message_filter: - # s += '# ignoring message {}\n'.format(m['name']) - # continue - for k in argument_filter: - try: - m['args'].pop(k) - except KeyError: - pass - a = pp.pformat(m['args']) - s += '{}:\n'.format(m['name']) - s += todump_items(None, m['args'], 0) - return s - - -def init_api(apidir): - # Read API definitions - apifiles = VPPApiJSONFiles.find_api_files(api_dir=apidir) - messages = {} - services = {} - for file in apifiles: - with open(file) as apidef_file: - m, s = VPPApiJSONFiles.process_json_file(apidef_file) - messages.update(m) - services.update(s) - return messages, services - - -def replaymsgs(vpp, msgs): - for m in msgs: - name = m['name'] - if name not in vpp.services: - continue - if name == 'control_ping': - continue - try: - m['args'].pop('client_index') - except KeyError: - pass - if m['args']['context'] == 0: - m['args']['context'] = 1 - f = vpp.get_function(name) - rv = f(**m['args']) - print('RV {}'.format(rv)) - - -def replay(args): - """Replay into running VPP instance""" - - from vpp_papi import VPP - - JSON = 1 - APITRACE = 2 - - filename, file_extension = os.path.splitext(args.input) - input_type = JSON if file_extension == '.json' else APITRACE - - vpp = VPP(use_socket=args.socket) - rv = vpp.connect(name='vppapireplay', chroot_prefix=args.shmprefix) - if rv != 0: - sys.exit('Cannot connect to VPP') - - if input_type == JSON: - with open(args.input, 'r') as file: - msgs = json.load(file, object_hook=vpp_decode) - else: - msgs = apitrace2json(messages, args.input) - - replaymsgs(vpp, msgs) - - vpp.disconnect() - - -def generate(args): - """Generate JSON""" - - JSON = 1 - APITRACE = 2 - PYTHON = 3 - DUMP = 4 - - filename, file_extension = os.path.splitext(args.input) - input_type = JSON if file_extension == '.json' else APITRACE - filename, file_extension = os.path.splitext(args.output) - - if args.todump: - output_type = DUMP - else: - if file_extension == '.json' or filename == '-': - output_type = JSON - elif file_extension == '.py': - output_type = PYTHON - else: - output_type = APITRACE - - if input_type == output_type: - sys.exit("error: Nothing to convert between") - - if input_type != JSON and output_type == APITRACE: - sys.exit("error: Input file must be JSON file: {}".format(args.input)) - - messages, services = init_api(args.apidir) - - if input_type == JSON and output_type == APITRACE: - i = 0 - for k, v in messages.items(): - v._vl_msg_id = i - i += 1 - - n, result = json2apitrace(messages, args.input) - msgtbl = serialize_msgtbl(messages) - - print('API messages: {}'.format(n)) - header = struct.pack(">IIB", n, len(msgtbl), 0) - - with open(args.output, 'wb') as outfile: - outfile.write(header) - outfile.write(msgtbl) - outfile.write(result) - - return - - if input_type == APITRACE: - result = apitrace2json(messages, args.input) - if output_type == PYTHON: - s = json.dumps(result, cls=VPPEncoder, default=vpp_encoder) - x = json.loads(s, object_hook=vpp_decode) - s = topython(x, services) - elif output_type == DUMP: - s = json.dumps(result, cls=VPPEncoder, default=vpp_encoder) - x = json.loads(s, object_hook=vpp_decode) - s = todump(x, services) - else: - s = json.dumps(result, cls=VPPEncoder, - default=vpp_encoder, indent=4 * ' ') - elif output_type == PYTHON: - with open(args.input, 'r') as file: - x = json.load(file, object_hook=vpp_decode) - s = topython(x, services) - else: - sys.exit('Input file must be API trace file: {}'.format(args.input)) - - if args.output == '-': - sys.stdout.write(s + '\n') - else: - print('Generating {} from API trace: {}' - .format(args.output, args.input)) - with open(args.output, 'w') as outfile: - outfile.write(s) - -def general(args): - return - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('--debug', action='store_true', - help='enable debug mode') - parser.add_argument('--apidir', - help='Location of JSON API definitions') - - parser.set_defaults(func=general) - subparsers = parser.add_subparsers(title='subcommands', - description='valid subcommands', - help='additional help') - - parser_convert = subparsers.add_parser('convert', - help='Convert API trace to JSON or Python and back') - parser_convert.add_argument('input', - help='Input file (API trace | JSON)') - parser_convert.add_argument('--todump', action='store_true', help='Output text format') - parser_convert.add_argument('output', - help='Output file (Python | JSON | API trace)') - parser_convert.set_defaults(func=generate) - - - parser_replay = subparsers.add_parser('replay', - help='Replay messages to running VPP instance') - parser_replay.add_argument('input', help='Input file (API trace | JSON)') - parser_replay.add_argument('--socket', action='store_true', - help='use default socket to connect to VPP') - parser_replay.add_argument('--shmprefix', - help='connect to VPP on shared memory prefix') - parser_replay.set_defaults(func=replay) - - args = parser.parse_args() - if args.debug: - logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) - - args.func(args) - - -main() diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 1458fde5190..61ac92fac38 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -2627,7 +2627,9 @@ vat_api_hookup (vat_main_t * vam) #define _(N, n) \ vl_msg_api_set_handlers (VL_API_##N + 1, #n, vl_api_##n##_t_handler_uni, \ vl_noop_handler, vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 1); + vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 1, \ + vl_api_##n##_t_print_json, vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson); foreach_vpe_api_reply_msg; #if VPP_API_TEST_BUILTIN == 0 foreach_standalone_reply_msg; diff --git a/src/vat2/CMakeLists.txt b/src/vat2/CMakeLists.txt index 9ee2d8211eb..c44d2452ea1 100644 --- a/src/vat2/CMakeLists.txt +++ b/src/vat2/CMakeLists.txt @@ -18,7 +18,6 @@ add_vpp_executable(vat2 ENABLE_EXPORTS SOURCES main.c plugin.c - jsonconvert.c DEPENDS api_headers @@ -41,7 +40,6 @@ vpp_generate_api_c_header (test/vat2_test.api) add_vpp_executable(test_vat2 ENABLE_EXPORTS NO_INSTALL SOURCES test/vat2_test.c - jsonconvert.c DEPENDS api_headers @@ -57,7 +55,6 @@ add_vpp_executable(test_vat2 ENABLE_EXPORTS NO_INSTALL if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.13" AND "${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") set(TARGET_NAME test_vat2) - set(COV_SOURCES ${CMAKE_SOURCE_DIR}/vat2/jsonconvert.c) message("Building with llvm Code Coverage Tools ${TARGET_NAME}") target_compile_options(${TARGET_NAME} PRIVATE -fprofile-instr-generate -fcoverage-mapping) @@ -96,7 +93,6 @@ endif() ############################################################################## install( FILES - jsonconvert.h vat2_helpers.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/vat2 COMPONENT vpp-dev diff --git a/src/vat2/jsonconvert.c b/src/vat2/jsonconvert.c deleted file mode 100644 index 1437b90688a..00000000000 --- a/src/vat2/jsonconvert.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#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)); - if (!unformat (&input, "0x%U", unformat_hex_string, s)) - return -1; - 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)); - if (!unformat (&input, "0x%U", unformat_hex_string, &s)) - return 0; - return s; -} - -int -u8string_fromjson2(cJSON *o, char *fieldname, u8 *data) -{ - u8 *s = u8string_fromjson(o, fieldname); - if (!s) - return -1; - 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; -} - -int -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 -1; - unformat_init_string (&input, p, strlen(p)); - if (!unformat (&input, "%U", unformat_ip4_address, a)) - return -1; - return 0; -} - -int -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 -1; - unformat_init_string (&input, p, strlen(p)); - if (!unformat (&input, "%U/%d", unformat_ip4_address, &a->address, - &a->len)) - return -1; - return 0; -} - -int -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); -} -int -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 -1; - unformat_init_string (&input, p, strlen(p)); - if (!unformat (&input, "%U", unformat_ip6_address, a)) - return -1; - return 0; -} - -int -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 -1; - unformat_init_string (&input, p, strlen(p)); - if (!unformat (&input, "%U/%d", unformat_ip6_address, &a->address, &a->len)) - return -1; - return 0; -} - -int -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); -} - -int -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 -1; - unformat_init_string (&input, p, strlen(p)); - if (unformat (&input, "%U", unformat_ip4_address, &a->un.ip4)) - a->af = ADDRESS_IP4; - else if (unformat (&input, "%U", unformat_ip6_address, &a->un.ip6)) - a->af = ADDRESS_IP6; - else - return -1; - return 0; -} - -int -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 -1; - unformat_init_string (&input, p, strlen(p)); - int plen; - if (unformat (&input, "%U/%d", unformat_ip4_address, &a->address.un.ip4, &plen)) - a->address.af = ADDRESS_IP4; - else if (unformat (&input, "%U/%d", unformat_ip6_address, &a->address.un.ip6, &plen)) - a->address.af = ADDRESS_IP6; - else - return -1; - a->len = plen; - return 0; -} - -int -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); -} - -int -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)); - if (!unformat (&input, "%U", unformat_mac_address, a)) - return -1; - return 0; -} - -/* 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); -} - -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) { - return vl_api_ip4_prefix_t_tojson (a); -} -cJSON * -vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a) { - return vl_api_ip6_prefix_t_tojson (a); -} -cJSON * -vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a) { - return vl_api_prefix_t_tojson (a); -} -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 deleted file mode 100644 index 038ad74ac0e..00000000000 --- a/src/vat2/jsonconvert.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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 _ - - /* Prototypes */ - int - vl_api_bool_fromjson (cJSON *o, bool *d); -int vl_api_ip4_address_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_ip4_address_t *a); -int vl_api_ip4_prefix_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_ip4_prefix_t *a); -int vl_api_ip4_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_ip4_prefix_t *a); -int vl_api_ip6_address_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_ip6_address_t *a); -int vl_api_ip6_prefix_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_ip6_prefix_t *a); -int vl_api_ip6_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_ip6_prefix_t *a); -int vl_api_address_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_address_t *a); -int vl_api_prefix_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_prefix_t *a); -int vl_api_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o, - vl_api_prefix_t *a); -int 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); -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 index 208e0c5d2c2..667f473c635 100644 --- a/src/vat2/main.c +++ b/src/vat2/main.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -30,6 +30,33 @@ #include #include "vat2.h" +/* + * Filter these messages as they are used to manage the API connection to VPP + */ +char *filter_messages_strings[] = { "memclnt_create", + "memclnt_delete", + "sockclnt_create", + "sockclnt_delete", + "memclnt_rx_thread_suspend", + "memclnt_read_timeout", + "rx_thread_exit", + "trace_plugin_msg_ids", + 0 }; + +static bool +filter_message (char *msgname) +{ + char **p = filter_messages_strings; + + while (*p) + { + if (strcmp (*p, msgname) == 0) + return true; + p++; + } + return false; +} + uword *function_by_name; bool debug = false; @@ -89,15 +116,16 @@ struct apifuncs_s { cJSON (*f) (cJSON *); cJSON (*tojson) (void *); + u32 crc; }; struct apifuncs_s *apifuncs = 0; void vat2_register_function (char *name, cJSON (*f) (cJSON *), - cJSON (*tojson) (void *)) + cJSON (*tojson) (void *), u32 crc) { - struct apifuncs_s funcs = { .f = f, .tojson = tojson }; + struct apifuncs_s funcs = { .f = f, .tojson = tojson, .crc = crc }; vec_add1 (apifuncs, funcs); hash_set_mem (function_by_name, name, vec_len (apifuncs) - 1); } @@ -105,12 +133,28 @@ vat2_register_function (char *name, cJSON (*f) (cJSON *), static int vat2_exec_command_by_name (char *msgname, cJSON *o) { + if (filter_message (msgname)) + return 0; + + cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc"); + if (!crc_obj) + { + fprintf (stderr, "Missing '_crc' element!\n"); + return -1; + } + char *crc_str = cJSON_GetStringValue (crc_obj); + u32 crc = (u32) strtol (crc_str, NULL, 16); + uword *p = hash_get_mem (function_by_name, msgname); if (!p) { - fprintf (stderr, "No such command %s", msgname); + fprintf (stderr, "No such command %s\n", msgname); return -1; } + if (crc != apifuncs[p[0]].crc) + { + fprintf (stderr, "API CRC does not match: %s!\n", msgname); + } cJSON *(*fp) (cJSON *); fp = (void *) apifuncs[p[0]].f; @@ -143,9 +187,10 @@ vat2_exec_command (cJSON *o) } char *name = cJSON_GetStringValue (msg_id_obj); - assert (name); + return vat2_exec_command_by_name (name, o); } + static void print_template (char *msgname) { @@ -307,6 +352,12 @@ int main (int argc, char **argv) } } + if (!msgname && !filename) + { + print_help (); + exit (-1); + } + /* Read message from file */ if (filename) { if (argc > index) @@ -325,6 +376,7 @@ int main (int argc, char **argv) fprintf(stderr, "%s: can't open file: %s\n", argv[0], filename); exit(-1); } + chunksize = bufsize = 1024; char *buf = malloc(bufsize); while ((n = fread (buf + n_read, 1, chunksize, f))) @@ -339,17 +391,17 @@ int main (int argc, char **argv) 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); } } + free (buf); } - if (!msgname && !filename) + if (!o) { - print_help (); + fprintf (stderr, "%s: Failed parsing JSON input\n", argv[0]); exit (-1); } diff --git a/src/vat2/test/vat2_test.c b/src/vat2/test/vat2_test.c index 1ac46527b3c..7aa5e71296e 100644 --- a/src/vat2/test/vat2_test.c +++ b/src/vat2/test/vat2_test.c @@ -196,6 +196,7 @@ struct tests tests[] = { "[\"2001:db8::23\", \"2001:db8::23\"] }" }, { .s = "{\"_msgname\": \"test_empty\"}" }, { .s = "{\"_msgname\": \"test_interface\", \"sw_if_index\": 100 }" }, + { .s = "{\"_msgname\": \"test_interface\", \"sw_if_index\": 4294967295 }" }, }; int main (int argc, char **argv) diff --git a/src/vcl/vcl_bapi.c b/src/vcl/vcl_bapi.c index 60fbe737a41..72440989727 100644 --- a/src/vcl/vcl_bapi.c +++ b/src/vcl/vcl_bapi.c @@ -299,10 +299,11 @@ vcl_bapi_hookup (void) return; #define _(N, n) \ - vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_##N, #n, \ - vl_api_##n##_t_handler, vl_noop_handler, \ - vl_api_##n##_t_endian, vl_api_##n##_t_print, \ - sizeof (vl_api_##n##_t), 1); + vl_msg_api_set_handlers ( \ + REPLY_MSG_ID_BASE + VL_API_##N, #n, vl_api_##n##_t_handler, \ + vl_noop_handler, vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_print_json, \ + vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson); foreach_sock_msg; #undef _ } diff --git a/src/vlibapi/api.h b/src/vlibapi/api.h index 431155c5e09..d05395a213c 100644 --- a/src/vlibapi/api.h +++ b/src/vlibapi/api.h @@ -36,8 +36,8 @@ typedef CLIB_PACKED ( struct { }) vl_api_trace_file_header_t; /* *INDENT-ON* */ -int vl_msg_api_trace_save (api_main_t * am, - vl_api_trace_which_t which, FILE * fp); +int vl_msg_api_trace_save (api_main_t *am, vl_api_trace_which_t which, + FILE *fp, u8 is_json); #define VLIB_API_INIT_FUNCTION(x) VLIB_DECLARE_INIT_FUNCTION(x,api_init) @@ -123,6 +123,11 @@ vlib_node_t ***vlib_node_unserialize (u8 * vector); u32 vl_msg_api_get_msg_length (void *msg_arg); +typedef int (*vl_msg_traverse_trace_fn) (u8 *, void *); + +int vl_msg_traverse_trace (vl_api_trace_t *tp, vl_msg_traverse_trace_fn fn, + void *ctx); + #endif /* included_api_h */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h index 3fdc1bbdd36..a955636ba3f 100644 --- a/src/vlibapi/api_common.h +++ b/src/vlibapi/api_common.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,9 @@ typedef struct void *cleanup; /**< non-default message cleanup handler */ void *endian; /**< message endian function */ void *print; /**< message print function */ + void *print_json; /**< message print function (JSON format) */ + void *tojson; /**< binary to JSON convert function */ + void *fromjson; /**< JSON to binary convert function */ int size; /**< message size */ int traced; /**< is this message to be traced? */ int replay; /**< is this message to be replayed? */ @@ -173,11 +177,10 @@ void vl_msg_api_trace_only (void *the_msg); void vl_msg_api_cleanup_handler (void *the_msg); void vl_msg_api_replay_handler (void *the_msg); void vl_msg_api_socket_handler (void *the_msg); -void vl_msg_api_set_handlers (int msg_id, char *msg_name, - void *handler, - void *cleanup, - void *endian, - void *print, int msg_size, int traced); +void vl_msg_api_set_handlers (int msg_id, char *msg_name, void *handler, + void *cleanup, void *endian, void *print, + int msg_size, int traced, void *print_json, + void *tojson, void *fromjson); void vl_msg_api_clean_handlers (int msg_id); void vl_msg_api_config (vl_msg_api_msg_config_t *); void vl_msg_api_set_cleanup_handler (int msg_id, void *fp); @@ -241,9 +244,21 @@ typedef struct api_main_t /** Message print function vector */ void (**msg_print_handlers) (void *, void *); + /** Message print function vector in JSON */ + void (**msg_print_json_handlers) (void *, void *); + + /** Message convert function vector */ + cJSON *(**msg_tojson_handlers) (void *); + + /** Message convert function vector */ + void *(**msg_fromjson_handlers) (cJSON *, int *); + /** Message name vector */ const char **msg_names; + /** API message ID by name hash table */ + uword *msg_id_by_name; + /** Don't automatically free message buffer vetor */ u8 *message_bounce; diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c index 65288d89f67..52e66f41342 100644 --- a/src/vlibapi/api_shared.c +++ b/src/vlibapi/api_shared.c @@ -223,13 +223,160 @@ vl_api_serialize_message_table (api_main_t * am, u8 * vector) return serialize_close_vector (sm); } +static int +vl_msg_api_trace_write_one (api_main_t *am, u8 *msg, FILE *fp) +{ + u8 *tmpmem = 0; + int tlen, slen; + cJSON *(*tojson_fn) (void *); + + u32 msg_length = vec_len (msg); + vec_validate (tmpmem, msg_length - 1); + clib_memcpy_fast (tmpmem, msg, msg_length); + u16 id = clib_net_to_host_u16 (*((u16 *) msg)); + + void (*endian_fp) (void *); + endian_fp = am->msg_endian_handlers[id]; + (*endian_fp) (tmpmem); + + if (id < vec_len (am->msg_tojson_handlers) && am->msg_tojson_handlers[id]) + { + tojson_fn = am->msg_tojson_handlers[id]; + cJSON *o = tojson_fn (tmpmem); + char *s = cJSON_Print (o); + slen = strlen (s); + tlen = fwrite (s, 1, slen, fp); + cJSON_free (s); + cJSON_Delete (o); + vec_free (tmpmem); + if (tlen != slen) + { + fformat (stderr, "writing to file error\n"); + return -11; + } + } + else + fformat (stderr, " [no registered tojson fn]\n"); + + return 0; +} + +#define vl_msg_fwrite(_s, _f) fwrite (_s, 1, sizeof (_s) - 1, _f) + +typedef struct +{ + FILE *fp; + u32 n_traces; + u32 i; +} vl_msg_write_json_args_t; + +static int +vl_msg_write_json_fn (u8 *msg, void *ctx) +{ + vl_msg_write_json_args_t *arg = ctx; + FILE *fp = arg->fp; + api_main_t *am = vlibapi_get_main (); + int rc = vl_msg_api_trace_write_one (am, msg, fp); + if (rc < 0) + return rc; + + if (arg->i < arg->n_traces - 1) + vl_msg_fwrite (",\n", fp); + arg->i++; + return 0; +} + +static int +vl_msg_api_trace_write_json (api_main_t *am, vl_api_trace_t *tp, FILE *fp) +{ + vl_msg_write_json_args_t args; + clib_memset (&args, 0, sizeof (args)); + args.fp = fp; + args.n_traces = vec_len (tp->traces); + vl_msg_fwrite ("[\n", fp); + + int rv = vl_msg_traverse_trace (tp, vl_msg_write_json_fn, &args); + if (rv < 0) + return rv; + + vl_msg_fwrite ("]", fp); + return 0; +} + int -vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp) +vl_msg_traverse_trace (vl_api_trace_t *tp, vl_msg_traverse_trace_fn fn, + void *ctx) { - vl_api_trace_t *tp; - vl_api_trace_file_header_t fh; int i; u8 *msg; + int rv = 0; + + /* No-wrap case */ + if (tp->wrapped == 0) + { + for (i = 0; i < vec_len (tp->traces); i++) + { + /*sa_ignore NO_NULL_CHK */ + msg = tp->traces[i]; + if (!msg) + continue; + + rv = fn (msg, ctx); + if (rv < 0) + return rv; + } + } + else + { + /* Wrap case: write oldest -> end of buffer */ + for (i = tp->curindex; i < vec_len (tp->traces); i++) + { + msg = tp->traces[i]; + if (!msg) + continue; + + rv = fn (msg, ctx); + if (rv < 0) + return rv; + } + /* write beginning of buffer -> oldest-1 */ + for (i = 0; i < tp->curindex; i++) + { + /*sa_ignore NO_NULL_CHK */ + msg = tp->traces[i]; + if (!msg) + continue; + + rv = fn (msg, ctx); + if (rv < 0) + return rv; + } + } + return 0; +} + +static int +vl_api_msg_write_fn (u8 *msg, void *ctx) +{ + FILE *fp = ctx; + u32 msg_length = clib_host_to_net_u32 (vec_len (msg)); + if (fwrite (&msg_length, 1, sizeof (msg_length), fp) != sizeof (msg_length)) + { + return (-14); + } + if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) + { + return (-14); + } + return 0; +} + +int +vl_msg_api_trace_save (api_main_t *am, vl_api_trace_which_t which, FILE *fp, + u8 is_json) +{ + vl_api_trace_t *tp; + vl_api_trace_file_header_t fh; switch (which) { @@ -256,9 +403,13 @@ vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp) return -2; } + if (is_json) + return vl_msg_api_trace_write_json (am, tp, fp); + /* Write the file header */ fh.wrapped = tp->wrapped; fh.nitems = clib_host_to_net_u32 (vec_len (tp->traces)); + u8 *m = vl_api_serialize_message_table (am, 0); fh.msgtbl_size = clib_host_to_net_u32 (vec_len (m)); @@ -274,92 +425,7 @@ vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp) } vec_free (m); - /* No-wrap case */ - if (tp->wrapped == 0) - { - /* - * Note: vec_len return 0 when fed a NULL pointer. - * Unfortunately, the static analysis tool doesn't - * figure it out, hence the suppressed warnings. - * What a great use of my time. - */ - for (i = 0; i < vec_len (tp->traces); i++) - { - u32 msg_length; - /*sa_ignore NO_NULL_CHK */ - msg = tp->traces[i]; - /* - * This retarded check required to pass - * [sic] SA-checking. - */ - if (!msg) - continue; - - msg_length = clib_host_to_net_u32 (vec_len (msg)); - if (fwrite (&msg_length, 1, sizeof (msg_length), fp) - != sizeof (msg_length)) - { - return (-14); - } - if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) - { - return (-11); - } - } - } - else - { - /* Wrap case: write oldest -> end of buffer */ - for (i = tp->curindex; i < vec_len (tp->traces); i++) - { - u32 msg_length; - msg = tp->traces[i]; - /* - * This retarded check required to pass - * [sic] SA-checking - */ - if (!msg) - continue; - - msg_length = clib_host_to_net_u32 (vec_len (msg)); - if (fwrite (&msg_length, 1, sizeof (msg_length), fp) - != sizeof (msg_length)) - { - return (-14); - } - - if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) - { - return (-12); - } - } - /* write beginning of buffer -> oldest-1 */ - for (i = 0; i < tp->curindex; i++) - { - u32 msg_length; - /*sa_ignore NO_NULL_CHK */ - msg = tp->traces[i]; - /* - * This retarded check required to pass - * [sic] SA-checking - */ - if (!msg) - continue; - - msg_length = clib_host_to_net_u32 (vec_len (msg)); - if (fwrite (&msg_length, 1, sizeof (msg_length), fp) - != sizeof (msg_length)) - { - return (-14); - } - - if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg)) - { - return (-13); - } - } - } - return 0; + return vl_msg_traverse_trace (tp, vl_api_msg_write_fn, fp); } int @@ -807,16 +873,19 @@ vl_msg_api_socket_handler (void *the_msg) 1 /* do_it */ , 0 /* free_it */ ); } -#define foreach_msg_api_vector \ -_(msg_names) \ -_(msg_handlers) \ -_(msg_cleanup_handlers) \ -_(msg_endian_handlers) \ -_(msg_print_handlers) \ -_(api_trace_cfg) \ -_(message_bounce) \ -_(is_mp_safe) \ -_(is_autoendian) +#define foreach_msg_api_vector \ + _ (msg_names) \ + _ (msg_handlers) \ + _ (msg_cleanup_handlers) \ + _ (msg_endian_handlers) \ + _ (msg_print_handlers) \ + _ (msg_print_json_handlers) \ + _ (msg_tojson_handlers) \ + _ (msg_fromjson_handlers) \ + _ (api_trace_cfg) \ + _ (message_bounce) \ + _ (is_mp_safe) \ + _ (is_autoendian) void vl_msg_api_config (vl_msg_api_msg_config_t * c) @@ -855,6 +924,9 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c) am->msg_cleanup_handlers[c->id] = c->cleanup; am->msg_endian_handlers[c->id] = c->endian; am->msg_print_handlers[c->id] = c->print; + am->msg_print_json_handlers[c->id] = c->print_json; + am->msg_tojson_handlers[c->id] = c->tojson; + am->msg_fromjson_handlers[c->id] = c->fromjson; am->message_bounce[c->id] = c->message_bounce; am->is_mp_safe[c->id] = c->is_mp_safe; am->is_autoendian[c->id] = c->is_autoendian; @@ -862,6 +934,11 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c) am->api_trace_cfg[c->id].size = c->size; am->api_trace_cfg[c->id].trace_enable = c->traced; am->api_trace_cfg[c->id].replay_enable = c->replay; + + if (!am->msg_id_by_name) + am->msg_id_by_name = hash_create_string (0, sizeof (uword)); + + hash_set_mem (am->msg_id_by_name, c->name, c->id); } /* @@ -870,7 +947,8 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c) */ void vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup, - void *endian, void *print, int size, int traced) + void *endian, void *print, int size, int traced, + void *print_json, void *tojson, void *fromjson) { vl_msg_api_msg_config_t cfg; vl_msg_api_msg_config_t *c = &cfg; @@ -888,6 +966,9 @@ vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup, c->message_bounce = 0; c->is_mp_safe = 0; c->is_autoendian = 0; + c->tojson = tojson; + c->fromjson = fromjson; + c->print_json = print_json; vl_msg_api_config (c); } @@ -987,7 +1068,7 @@ vl_msg_api_post_mortem_dump (void) rv = write (2, "\n", 1); return; } - rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp); + rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp, 0); fclose (fp); if (rv < 0) { diff --git a/src/vlibmemory/CMakeLists.txt b/src/vlibmemory/CMakeLists.txt index 456cba9baeb..6d6483dc61f 100644 --- a/src/vlibmemory/CMakeLists.txt +++ b/src/vlibmemory/CMakeLists.txt @@ -57,3 +57,9 @@ add_dependencies(vlibmemoryclient vlibmemory_api_headers) add_vat_test_library(vlib vlibapi_test.c ) +############################################################################## +# VAT2 plugins +############################################################################## +add_vpp_test_library(vlibmemoryclient + memclnt.api +) diff --git a/src/vlibmemory/memclnt.api b/src/vlibmemory/memclnt.api index 07c6d47b9fc..a5194cd58c4 100644 --- a/src/vlibmemory/memclnt.api +++ b/src/vlibmemory/memclnt.api @@ -29,7 +29,6 @@ service { /* * Create a client registration */ -manual_print define memclnt_create { u32 context; /* opaque value to be returned in the reply */ i32 ctx_quota; /* requested punt context quota */ @@ -49,7 +48,6 @@ define memclnt_create_reply { /* * Delete a client registration */ -manual_print define memclnt_delete { u32 index; /* index, used e.g. by API trace replay */ u64 handle; /* handle by which vlib knows this client */ @@ -137,7 +135,7 @@ define api_versions_reply { * at api trace replay time */ -manual_print define trace_plugin_msg_ids +define trace_plugin_msg_ids { u32 client_index; u32 context; diff --git a/src/vlibmemory/memclnt_api.c b/src/vlibmemory/memclnt_api.c index 5ebc31f71dc..23d0088308f 100644 --- a/src/vlibmemory/memclnt_api.c +++ b/src/vlibmemory/memclnt_api.c @@ -52,16 +52,6 @@ #include #undef vl_printfun -static inline void * -vl_api_trace_plugin_msg_ids_t_print (vl_api_trace_plugin_msg_ids_t *a, - void *handle) -{ - vl_print (handle, "vl_api_trace_plugin_msg_ids: %s first %u last %u\n", - a->plugin_name, clib_host_to_net_u16 (a->first_msg_id), - clib_host_to_net_u16 (a->last_msg_id)); - return handle; -} - /* instantiate all the endian swap functions we know about */ #define vl_endianfun #include @@ -154,6 +144,12 @@ vlib_api_init (void) vl_msg_api_msg_config_t cfg; vl_msg_api_msg_config_t *c = &cfg; + cJSON_Hooks cjson_hooks = { + .malloc_fn = clib_mem_alloc, + .free_fn = clib_mem_free, + }; + cJSON_InitHooks (&cjson_hooks); + clib_memset (c, 0, sizeof (*c)); #define _(N, n) \ @@ -689,19 +685,25 @@ rpc_api_hookup (vlib_main_t *vm) { api_main_t *am = vlibapi_get_main (); #define _(N, n) \ - vl_msg_api_set_handlers ( \ - VL_API_##N, #n, vl_api_##n##_t_handler, vl_noop_handler, vl_noop_handler, \ - vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0 /* do not trace */); + vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler, \ + vl_noop_handler, vl_noop_handler, \ + vl_api_##n##_t_print, sizeof (vl_api_##n##_t), \ + 0 /* do not trace */, vl_api_##n##_t_print_json, \ + vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson); foreach_rpc_api_msg; #undef _ #define _(N, n) \ - vl_msg_api_set_handlers ( \ - VL_API_##N, #n, vl_api_##n##_t_handler, vl_noop_handler, vl_noop_handler, \ - vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 1 /* do trace */); + vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler, \ + vl_noop_handler, vl_noop_handler, \ + vl_api_##n##_t_print, sizeof (vl_api_##n##_t), \ + 1 /* do trace */, vl_api_##n##_t_print_json, \ + vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson); foreach_plugin_trace_msg; #undef _ + am->api_trace_cfg[VL_API_TRACE_PLUGIN_MSG_IDS].replay_enable = 0; + /* No reason to halt the parade to create a trace record... */ am->is_mp_safe[VL_API_TRACE_PLUGIN_MSG_IDS] = 1; rpc_call_main_thread_cb_fn = vl_api_rpc_call_main_thread; diff --git a/src/vlibmemory/memory_api.c b/src/vlibmemory/memory_api.c index 9db27ebd574..6c066a152f4 100644 --- a/src/vlibmemory/memory_api.c +++ b/src/vlibmemory/memory_api.c @@ -38,26 +38,6 @@ #include #undef vl_endianfun -static inline void * -vl_api_memclnt_create_t_print (vl_api_memclnt_create_t * a, void *handle) -{ - vl_print (handle, "vl_api_memclnt_create_t:\n"); - vl_print (handle, "name: %s\n", a->name); - vl_print (handle, "input_queue: 0x%wx\n", a->input_queue); - vl_print (handle, "context: %u\n", (unsigned) a->context); - vl_print (handle, "ctx_quota: %ld\n", (long) a->ctx_quota); - return handle; -} - -static inline void * -vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t * a, void *handle) -{ - vl_print (handle, "vl_api_memclnt_delete_t:\n"); - vl_print (handle, "index: %u\n", (unsigned) a->index); - vl_print (handle, "handle: 0x%wx\n", a->handle); - return handle; -} - volatile int **vl_api_queue_cursizes; static void @@ -417,11 +397,11 @@ vl_api_memclnt_keepalive_t_handler (vl_api_memclnt_keepalive_t * mp) * don't trace memclnt_keepalive[_reply] msgs */ -#define foreach_vlib_api_msg \ -_(MEMCLNT_CREATE, memclnt_create, 1) \ -_(MEMCLNT_DELETE, memclnt_delete, 1) \ -_(MEMCLNT_KEEPALIVE, memclnt_keepalive, 0) \ -_(MEMCLNT_KEEPALIVE_REPLY, memclnt_keepalive_reply, 0) +#define foreach_vlib_api_msg \ + _ (MEMCLNT_CREATE, memclnt_create, 0) \ + _ (MEMCLNT_DELETE, memclnt_delete, 0) \ + _ (MEMCLNT_KEEPALIVE, memclnt_keepalive, 0) \ + _ (MEMCLNT_KEEPALIVE_REPLY, memclnt_keepalive_reply, 0) /* * memory_api_init diff --git a/src/vlibmemory/memory_client.c b/src/vlibmemory/memory_client.c index 64650b64eca..f0b05b70695 100644 --- a/src/vlibmemory/memory_client.c +++ b/src/vlibmemory/memory_client.c @@ -362,14 +362,14 @@ _(MEMCLNT_KEEPALIVE, memclnt_keepalive) void vl_client_install_client_message_handlers (void) { - -#define _(N,n) \ - vl_msg_api_set_handlers(VL_API_##N, #n, \ - vl_api_##n##_t_handler, \ - noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); + api_main_t *am = vlibapi_get_main (); +#define _(N, n) \ + vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler, \ + noop_handler, vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0, \ + vl_api_##n##_t_print_json, vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson); \ + am->api_trace_cfg[VL_API_##N].replay_enable = 0; foreach_api_msg; #undef _ } diff --git a/src/vlibmemory/socket_api.c b/src/vlibmemory/socket_api.c index 60ca650d92f..ce834a70aac 100644 --- a/src/vlibmemory/socket_api.c +++ b/src/vlibmemory/socket_api.c @@ -495,7 +495,13 @@ vl_api_sockclnt_create_t_handler (vl_api_sockclnt_create_t * mp) regp = socket_main.current_rp; - ASSERT (regp->registration_type == REGISTRATION_TYPE_SOCKET_SERVER); + /* client already connected through shared memory? */ + if (!regp || regp->registration_type != REGISTRATION_TYPE_SOCKET_SERVER) + { + clib_warning ( + "unsupported API call: already connected though shared memory?"); + return; + } regp->name = format (0, "%s%c", mp->name, 0); @@ -765,14 +771,15 @@ reply: vl_sock_api_send_fd_msg (cf->file_descriptor, &memfd->fd, 1); } -#define foreach_vlib_api_msg \ - _(SOCKCLNT_CREATE, sockclnt_create, 1) \ - _(SOCKCLNT_DELETE, sockclnt_delete, 1) \ - _(SOCK_INIT_SHM, sock_init_shm, 1) +#define foreach_vlib_api_msg \ + _ (SOCKCLNT_CREATE, sockclnt_create, 0) \ + _ (SOCKCLNT_DELETE, sockclnt_delete, 0) \ + _ (SOCK_INIT_SHM, sock_init_shm, 0) clib_error_t * vl_sock_api_init (vlib_main_t * vm) { + api_main_t *am = vlibapi_get_main (); clib_file_main_t *fm = &file_main; clib_file_t template = { 0 }; vl_api_registration_t *rp; @@ -784,13 +791,13 @@ vl_sock_api_init (vlib_main_t * vm) if (sm->socket_name == 0) return 0; -#define _(N,n,t) \ - vl_msg_api_set_handlers(VL_API_##N, #n, \ - vl_api_##n##_t_handler, \ - vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), t); +#define _(N, n, t) \ + vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler, \ + vl_noop_handler, vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, sizeof (vl_api_##n##_t), t, \ + vl_api_##n##_t_print_json, vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson); \ + am->api_trace_cfg[VL_API_##N].replay_enable = 0; foreach_vlib_api_msg; #undef _ diff --git a/src/vlibmemory/socket_client.c b/src/vlibmemory/socket_client.c index 69126f88963..2fb6b8a0c4e 100644 --- a/src/vlibmemory/socket_client.c +++ b/src/vlibmemory/socket_client.c @@ -432,13 +432,12 @@ void vl_sock_client_install_message_handlers (void) { -#define _(N,n) \ - vl_msg_api_set_handlers(VL_API_##N, #n, \ - vl_api_##n##_t_handler, \ - noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); +#define _(N, n) \ + vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler, \ + noop_handler, vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0, \ + vl_api_##n##_t_print_json, vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson); foreach_sock_client_api_msg; #undef _ } diff --git a/src/vlibmemory/vlib_api_cli.c b/src/vlibmemory/vlib_api_cli.c index 0057c85adcf..74ad3c5cd76 100644 --- a/src/vlibmemory/vlib_api_cli.c +++ b/src/vlibmemory/vlib_api_cli.c @@ -344,6 +344,7 @@ VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) = typedef enum { DUMP, + DUMP_JSON, REPLAY, INITIALIZERS, } vl_api_replay_t; @@ -391,6 +392,7 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, { vl_api_trace_file_header_t *hp; int i, fd; + u16 *msgid_vec = 0; struct stat statb; size_t file_size; u8 *msg; @@ -453,13 +455,31 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, vlib_cli_output (vm, "Note: wrapped/incomplete trace, results may vary\n"); + size_t file_size_left = file_size; + +#define assert_size(size_left, s) \ + do \ + { \ + if ((s) >= size_left) \ + { \ + vlib_cli_output (vm, "corrupted file"); \ + munmap (hp, file_size); \ + vec_free (msgid_vec); \ + return; \ + } \ + size_left -= s; \ + } \ + while (0); + + assert_size (file_size_left, sizeof (hp[0])); msg = (u8 *) (hp + 1); - u16 *msgid_vec = 0; serialize_main_t _sm, *sm = &_sm; u32 msgtbl_size = ntohl (hp->msgtbl_size); u8 *name_and_crc; + assert_size (file_size_left, msgtbl_size); + unserialize_open_data (sm, msg, msgtbl_size); unserialize_integer (sm, &nitems_msgtbl, sizeof (u32)); @@ -480,9 +500,11 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, int size; u16 msg_id; + assert_size (file_size_left, sizeof (u32)); size = clib_host_to_net_u32 (*(u32 *) msg); msg += sizeof (u32); + assert_size (file_size_left, size); msg_id = ntohs (*((u16 *) msg)); if (msg_id < vec_len (msgid_vec)) msg_id = msgid_vec[msg_id]; @@ -491,6 +513,7 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, { vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id); munmap (hp, file_size); + vec_free (msgid_vec); return; } msg += size; @@ -536,7 +559,8 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, * Endian swap if needed. All msg data is supposed to be in * network byte order. */ - if (((which == DUMP) && clib_arch_is_little_endian)) + if (((which == DUMP || which == DUMP_JSON) && + clib_arch_is_little_endian)) { void (*endian_fp) (void *); if (msg_id >= vec_len (am->msg_endian_handlers) @@ -561,6 +585,23 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, switch (which) { + case DUMP_JSON: + if (msg_id < vec_len (am->msg_print_json_handlers) && + am->msg_print_json_handlers[msg_id]) + { + u8 *(*print_fp) (void *, void *); + + print_fp = (void *) am->msg_print_json_handlers[msg_id]; + (*print_fp) (tmpbuf + sizeof (uword), vm); + } + else + { + vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n", + msg_id); + break; + } + break; + case DUMP: if (msg_id < vec_len (am->msg_print_handlers) && am->msg_print_handlers[msg_id]) @@ -639,9 +680,319 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename, munmap (hp, file_size); vec_free (tmpbuf); + vec_free (msgid_vec); am->replay_in_progress = 0; } +static int +file_exists (u8 *fname) +{ + FILE *fp = 0; + fp = fopen ((char *) fname, "r"); + if (fp) + { + fclose (fp); + return 1; + } + return 0; +} + +typedef struct +{ + vlib_main_t *vm; + u8 is_json; +} vl_msg_print_args; + +static int +vl_msg_print_trace (u8 *msg, void *ctx) +{ + vl_msg_print_args *a = ctx; + api_main_t *am = vlibapi_get_main (); + u16 msg_id = ntohs (*((u16 *) msg)); + void (*print_fp) (void *, void *); + void (**handlers) (void *, void *); + u8 is_json = a->is_json; + u8 *tmpbuf = 0; + + if (clib_arch_is_little_endian) + { + u32 msg_length = vec_len (msg); + vec_validate (tmpbuf, msg_length - 1); + clib_memcpy_fast (tmpbuf, msg, msg_length); + msg = tmpbuf; + + void (*endian_fp) (void *); + endian_fp = am->msg_endian_handlers[msg_id]; + (*endian_fp) (tmpbuf); + } + + if (is_json) + handlers = am->msg_print_json_handlers; + else + handlers = am->msg_print_handlers; + + if (msg_id < vec_len (handlers) && handlers[msg_id]) + { + print_fp = (void *) handlers[msg_id]; + (*print_fp) (msg, a->vm); + } + else + { + vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id); + } + + vec_free (tmpbuf); + return 0; +} + +static int +vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json) +{ + api_main_t *am = vlibapi_get_main (); + vl_api_trace_t *tp; + + switch (which) + { + case VL_API_TRACE_TX: + tp = am->tx_trace; + break; + case VL_API_TRACE_RX: + tp = am->rx_trace; + break; + default: + return -1; + } + + if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0) + return -1; + + vl_msg_print_args args; + clib_memset (&args, 0, sizeof (args)); + args.is_json = is_json; + args.vm = vm; + vl_msg_traverse_trace (tp, vl_msg_print_trace, &args); + + return 0; +} + +static char * +vl_msg_read_file (FILE *f) +{ + const size_t bufsize = 1024; + char *buf[bufsize], *v = 0; + size_t n; + + while ((n = fread (buf, 1, bufsize, f))) + vec_add (v, buf, n); + + return v; +} + +static u16 +vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name) +{ + uword *p; + p = hash_get_mem (am->msg_index_by_name_and_crc, name); + if (!p) + return (u16) ~0; + + return p[0]; +} + +static u16 +vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name) +{ + uword *p; + + if (!am->msg_id_by_name) + { + vlib_cli_output (vm, "message id table not yet initialized!\n"); + return (u16) ~0; + } + + p = hash_get_mem (am->msg_id_by_name, name); + if (!p) + return (u16) ~0; + + return p[0]; +} + +static int +vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o) +{ + api_main_t *am = vlibapi_get_main (); + u16 msg_id; + void *(*fromjson) (cJSON *, int *); + int len = 0, rv = -1; + trace_cfg_t *cfgp; + u8 *msg = 0; + + cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname"); + if (!msg_id_obj) + { + vlib_cli_output (vm, "Missing '_msgname' element!\n"); + return rv; + } + char *name = cJSON_GetStringValue (msg_id_obj); + + cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc"); + if (!msg_id_obj) + { + vlib_cli_output (vm, "Missing '_crc' element!\n"); + return rv; + } + char *crc = cJSON_GetStringValue (crc_obj); + u8 proc_warning = 0; + + u8 *name_crc = format (0, "%s_%s%c", name, crc, 0); + msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc); + if (msg_id == (u16) ~0) + { + msg_id = vl_msg_find_id_by_name (vm, am, name); + if (msg_id == (u16) ~0) + { + vlib_cli_output (vm, "unknown msg id %d!\n", msg_id); + vec_free (name_crc); + return rv; + } + proc_warning = 1; + } + vec_free (name_crc); + + cfgp = am->api_trace_cfg + msg_id; + if (!cfgp) + { + vlib_cli_output (vm, "msg id %d no trace config\n", msg_id); + return rv; + } + + if (cfgp->replay_enable) + { + + if (proc_warning) + vlib_cli_output (vm, "warning: msg %d has different signature\n"); + + fromjson = am->msg_fromjson_handlers[msg_id]; + if (!fromjson) + { + vlib_cli_output (vm, "missing fromjson convert function! id %d\n", + msg_id); + return rv; + } + + msg = (u8 *) fromjson (o, &len); + if (!msg) + { + vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n", + msg_id); + return rv; + } + + if (clib_arch_is_little_endian) + { + void (*endian_fp) (void *); + endian_fp = am->msg_endian_handlers[msg_id]; + (*endian_fp) (msg); + } + + void (*handler) (void *, vlib_main_t *); + handler = (void *) am->msg_handlers[msg_id]; + if (!handler) + { + vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id); + goto end; + } + + if (!am->is_mp_safe[msg_id]) + vl_msg_api_barrier_sync (); + (*handler) (msg, vm); + if (!am->is_mp_safe[msg_id]) + vl_msg_api_barrier_release (); + } + + rv = 0; +end: + if (msg) + cJSON_free (msg); + return rv; +} + +static void +vl_msg_replay_json (vlib_main_t *vm, u8 *filename) +{ + api_main_t *am = vlibapi_get_main (); + cJSON *o = 0; + int rv = 0; + FILE *f = fopen ((char *) filename, "r"); + + if (!f) + { + vlib_cli_output (vm, "failed to open %s!\n", filename); + return; + } + + char *buf = vl_msg_read_file (f); + fclose (f); + + o = cJSON_Parse (buf); + vec_free (buf); + if (!o) + { + vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename, + cJSON_GetErrorPtr ()); + return; + } + + if (cJSON_IsArray (o)) + { + am->replay_in_progress = 1; + size_t size = cJSON_GetArraySize (o); + for (int i = 0; i < size; i++) + { + rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i)); + if (rv < 0) + { + am->replay_in_progress = 0; + break; + } + } + } + else + { + rv = vl_msg_exec_json_command (vm, o); + } + + if (rv < 0) + vlib_cli_output (vm, "error during replaying API trace"); + + cJSON_Delete (o); +} + +static void +vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename) +{ + FILE *f = fopen ((char *) filename, "r"); + char *buf; + + if (!f) + { + vlib_cli_output (vm, "failed to open %s!\n", filename); + return; + } + + buf = vl_msg_read_file (f); + fclose (f); + + if (!buf) + { + vlib_cli_output (vm, "no content in %s!\n", filename); + return; + } + + vlib_cli_output (vm, buf); + vec_free (buf); +} + /** api_trace_command_fn - control the binary API trace / replay feature Note: this command MUST be marked thread-safe. Replay with @@ -688,6 +1039,43 @@ api_trace_command_fn (vlib_main_t * vm, vl_msg_api_trace_onoff (am, which, 0); vlib_worker_thread_barrier_release (vm); } + else if (unformat (line_input, "save-json %s", &filename)) + { + if (strstr ((char *) filename, "..") || + index ((char *) filename, '/')) + { + vlib_cli_output (vm, "illegal characters in filename '%s'", + filename); + goto out; + } + + chroot_filename = format (0, "/tmp/%s%c", filename, 0); + + vec_free (filename); + + if (file_exists (chroot_filename)) + { + vlib_cli_output (vm, "file exists: %s\n", chroot_filename); + goto out; + } + + fp = fopen ((char *) chroot_filename, "w"); + if (fp == NULL) + { + vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename); + goto out; + } + vlib_worker_thread_barrier_sync (vm); + rv = vl_msg_api_trace_save (am, which, fp, 1); + if (rv == -1) + vlib_cli_output (vm, "API Trace data not present\n"); + else if (rv < 0) + vlib_cli_output (vm, "failed to save api trace\n"); + else + vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename); + vlib_worker_thread_barrier_release (vm); + fclose (fp); + } else if (unformat (line_input, "save %s", &filename)) { if (strstr ((char *) filename, "..") @@ -702,6 +1090,12 @@ api_trace_command_fn (vlib_main_t * vm, vec_free (filename); + if (file_exists (chroot_filename)) + { + vlib_cli_output (vm, "file exists: %s\n", chroot_filename); + goto out; + } + fp = fopen ((char *) chroot_filename, "w"); if (fp == NULL) { @@ -709,7 +1103,7 @@ api_trace_command_fn (vlib_main_t * vm, goto out; } vlib_worker_thread_barrier_sync (vm); - rv = vl_msg_api_trace_save (am, which, fp); + rv = vl_msg_api_trace_save (am, which, fp, 0); vlib_worker_thread_barrier_release (vm); fclose (fp); if (rv == -1) @@ -732,10 +1126,30 @@ api_trace_command_fn (vlib_main_t * vm, vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename); goto out; } - else if (unformat (line_input, "dump %s", &filename)) + else if (unformat (line_input, "tojson %s", &filename)) + { + vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON); + } + else if (unformat (line_input, "dump-file-json %s", &filename)) + { + vl_msg_dump_file_json (vm, filename); + } + else if (unformat (line_input, "dump-file %s", &filename)) { vl_msg_api_process_file (vm, filename, first, last, DUMP); } + else if (unformat (line_input, "dump-json")) + { + vl_msg_api_dump_trace (vm, which, 1); + } + else if (unformat (line_input, "dump")) + { + vl_msg_api_dump_trace (vm, which, 0); + } + else if (unformat (line_input, "replay-json %s", &filename)) + { + vl_msg_replay_json (vm, filename); + } else if (unformat (line_input, "replay %s", &filename)) { vl_msg_api_process_file (vm, filename, first, last, REPLAY); @@ -790,91 +1204,15 @@ out: /* *INDENT-OFF* */ VLIB_CLI_COMMAND (api_trace_command, static) = { .path = "api trace", - .short_help = "api trace [on|off][first ][last ][status][free]" - "[post-mortem-on][dump|save|replay ]", + .short_help = "api trace [tx][on|off][first ][last ][status][free]" + "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-" + "json|replay |replay-json ][nitems ]" + "[initializers ]", .function = api_trace_command_fn, .is_mp_safe = 1, }; /* *INDENT-ON* */ -static clib_error_t * -vl_api_trace_command (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cli_cmd) -{ - u32 nitems = 1024; - vl_api_trace_which_t which = VL_API_TRACE_RX; - api_main_t *am = vlibapi_get_main (); - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx")) - goto configure; - else if (unformat (input, "tx nitems %u", &nitems) - || unformat (input, "tx")) - { - which = VL_API_TRACE_RX; - goto configure; - } - else if (unformat (input, "on rx")) - { - vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1); - } - else if (unformat (input, "on tx")) - { - vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1); - } - else if (unformat (input, "on")) - { - vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1); - } - else if (unformat (input, "off")) - { - vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0); - vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0); - } - else if (unformat (input, "free")) - { - vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0); - vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0); - vl_msg_api_trace_free (am, VL_API_TRACE_RX); - vl_msg_api_trace_free (am, VL_API_TRACE_TX); - } - else if (unformat (input, "debug on")) - { - am->msg_print_flag = 1; - } - else if (unformat (input, "debug off")) - { - am->msg_print_flag = 0; - } - else - return clib_error_return (0, "unknown input `%U'", - format_unformat_error, input); - } - return 0; - -configure: - if (vl_msg_api_trace_configure (am, which, nitems)) - { - vlib_cli_output (vm, "warning: trace configure error (%d, %d)", - which, nitems); - } - - return 0; -} - -/*? - * Control the binary API trace mechanism -?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (trace, static) = -{ - .path = "set api-trace", - .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]", - .function = vl_api_trace_command, -}; -/* *INDENT-ON* */ - static clib_error_t * api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input) { diff --git a/src/vnet/srmpls/sr_mpls_api.c b/src/vnet/srmpls/sr_mpls_api.c index d6216c68391..7d42f1ba451 100644 --- a/src/vnet/srmpls/sr_mpls_api.c +++ b/src/vnet/srmpls/sr_mpls_api.c @@ -190,10 +190,11 @@ sr_mpls_api_hookup (vlib_main_t * vm) vec_free (name); #define _(N, n) \ - vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_##N, #n, \ - vl_api_##n##_t_handler, vl_noop_handler, \ - vl_api_##n##_t_endian, vl_api_##n##_t_print, \ - sizeof (vl_api_##n##_t), 1); + vl_msg_api_set_handlers ( \ + REPLY_MSG_ID_BASE + VL_API_##N, #n, vl_api_##n##_t_handler, \ + vl_noop_handler, vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_print_json, \ + vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson); foreach_vpe_api_msg; #undef _ @@ -201,21 +202,23 @@ sr_mpls_api_hookup (vlib_main_t * vm) * Manually register the sr policy add msg, so we trace enough bytes * to capture a typical segment list */ - vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_ADD, - "sr_mpls_policy_add", - vl_api_sr_mpls_policy_add_t_handler, - vl_noop_handler, vl_api_sr_mpls_policy_add_t_endian, - vl_api_sr_mpls_policy_add_t_print, 256, 1); + vl_msg_api_set_handlers ( + REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_ADD, "sr_mpls_policy_add", + vl_api_sr_mpls_policy_add_t_handler, vl_noop_handler, + vl_api_sr_mpls_policy_add_t_endian, vl_api_sr_mpls_policy_add_t_print, 256, + 1, vl_api_sr_mpls_policy_add_t_print_json, + vl_api_sr_mpls_policy_mod_t_tojson, vl_api_sr_mpls_policy_mod_t_fromjson); /* * Manually register the sr policy mod msg, so we trace enough bytes * to capture a typical segment list */ - vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_MOD, - "sr_mpls_policy_mod", - vl_api_sr_mpls_policy_mod_t_handler, - vl_noop_handler, vl_api_sr_mpls_policy_mod_t_endian, - vl_api_sr_mpls_policy_mod_t_print, 256, 1); + vl_msg_api_set_handlers ( + REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_MOD, "sr_mpls_policy_mod", + vl_api_sr_mpls_policy_mod_t_handler, vl_noop_handler, + vl_api_sr_mpls_policy_mod_t_endian, vl_api_sr_mpls_policy_mod_t_print, 256, + 1, vl_api_sr_mpls_policy_mod_t_print_json, + vl_api_sr_mpls_policy_mod_t_tojson, vl_api_sr_mpls_policy_mod_t_fromjson); /* * Set up the (msg_name, crc, message-id) table diff --git a/src/vpp-api/client/client.c b/src/vpp-api/client/client.c index 542df9d414b..7a30792402c 100644 --- a/src/vpp-api/client/client.c +++ b/src/vpp-api/client/client.c @@ -101,14 +101,6 @@ cleanup (void) clib_memset(pm, 0, sizeof(*pm)); } -/* - * Satisfy external references when -lvlib is not available. - */ -void vlib_cli_output (struct vlib_main_t * vm, char * fmt, ...) -{ - clib_warning ("vlib_cli_output called..."); -} - void vac_free (void * msg) { diff --git a/src/vppinfra/CMakeLists.txt b/src/vppinfra/CMakeLists.txt index 4be291e1e8d..1114092e246 100644 --- a/src/vppinfra/CMakeLists.txt +++ b/src/vppinfra/CMakeLists.txt @@ -39,7 +39,7 @@ install( add_definitions(-fvisibility=hidden) # Ensure symbols from cJSON are exported -set_source_files_properties( cJSON.c PROPERTIES +set_source_files_properties( cJSON.c jsonformat.c PROPERTIES COMPILE_DEFINITIONS " CJSON_API_VISIBILITY " ) @@ -62,6 +62,7 @@ set(VPPINFRA_SRCS hash.c heap.c interrupt.c + jsonformat.c longjmp.S macros.c maplog.c @@ -140,6 +141,7 @@ set(VPPINFRA_HEADERS hash.h heap.h interrupt.h + jsonformat.h lb_hash_hash.h llist.h lock.h diff --git a/src/vppinfra/cJSON.c b/src/vppinfra/cJSON.c index 5b26a4be4e1..448435de4dc 100644 --- a/src/vppinfra/cJSON.c +++ b/src/vppinfra/cJSON.c @@ -157,7 +157,7 @@ typedef struct internal_hooks { void *(CJSON_CDECL *allocate)(size_t size); void (CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t new_size, size_t old_size); } internal_hooks; #if defined(_MSC_VER) @@ -170,16 +170,20 @@ static void CJSON_CDECL internal_free(void *pointer) { free(pointer); } -static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) -{ - return realloc(pointer, size); -} #else #define internal_malloc malloc #define internal_free free -#define internal_realloc realloc #endif +static void * CJSON_CDECL internal_realloc(void *pointer, size_t new_size, + size_t old_size) +{ + return realloc(pointer, new_size); +} + +static void * +cjson_realloc_internal (void *ptr, size_t new_size, size_t old_size); + /* strlen of character literals resolved at compile time */ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) @@ -213,7 +217,7 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) /* Reset hooks */ global_hooks.allocate = malloc; global_hooks.deallocate = free; - global_hooks.reallocate = realloc; + global_hooks.reallocate = internal_realloc; return; } @@ -233,7 +237,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) global_hooks.reallocate = NULL; if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { - global_hooks.reallocate = realloc; + global_hooks.reallocate = internal_realloc; + } + else + { + global_hooks.reallocate = cjson_realloc_internal; } } @@ -435,6 +443,27 @@ typedef struct internal_hooks hooks; } printbuffer; +static void * +cjson_realloc_internal (void *ptr, size_t new_size, size_t old_size) +{ + size_t copy_size; + if (old_size < new_size) + copy_size = old_size; + else + copy_size = new_size; + + unsigned char *newbuffer = global_hooks.allocate(new_size); + if (!newbuffer) + { + global_hooks.deallocate(ptr); + return NULL; + } + + memcpy (newbuffer, ptr, copy_size); + global_hooks.deallocate (ptr); + return newbuffer; +} + /* realloc printbuffer if necessary to have at least "needed" bytes more */ static unsigned char* ensure(printbuffer * const p, size_t needed) { @@ -486,34 +515,13 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) newsize = needed * 2; } - if (p->hooks.reallocate != NULL) + newbuffer = p->hooks.reallocate (p->buffer, newsize, p->length); + if (newbuffer == NULL) { - /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - } - else - { - /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->hooks.allocate(newsize); - if (!newbuffer) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - - memcpy (newbuffer, p->buffer, p->offset + 1); - p->hooks.deallocate (p->buffer); + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + return NULL; } p->length = newsize; p->buffer = newbuffer; @@ -1208,7 +1216,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* check if reallocate is available */ if (hooks->reallocate != NULL) { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1, default_buffer_size); if (printed == NULL) { goto fail; } @@ -3112,3 +3120,8 @@ CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); } + +CJSON_PUBLIC(void *) cJSON_realloc(void *object, size_t new_size, size_t old_size) +{ + return global_hooks.reallocate(object, new_size, old_size); +} diff --git a/src/vppinfra/cJSON.h b/src/vppinfra/cJSON.h index e97e5f4cdc4..1474c4e5c49 100644 --- a/src/vppinfra/cJSON.h +++ b/src/vppinfra/cJSON.h @@ -127,6 +127,8 @@ typedef struct cJSON_Hooks /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ void *(CJSON_CDECL *malloc_fn)(size_t sz); void (CJSON_CDECL *free_fn)(void *ptr); + void *(CJSON_CDECL *realloc_fn) (void *ptr, size_t new_size, + size_t old_size); } cJSON_Hooks; typedef int cJSON_bool; @@ -285,6 +287,8 @@ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ CJSON_PUBLIC(void *) cJSON_malloc(size_t size); CJSON_PUBLIC(void) cJSON_free(void *object); +CJSON_PUBLIC (void *) +cJSON_realloc (void *object, size_t new_size, size_t old_size); #ifdef __cplusplus } diff --git a/src/vppinfra/jsonformat.c b/src/vppinfra/jsonformat.c new file mode 100644 index 00000000000..17b3ee9d3f8 --- /dev/null +++ b/src/vppinfra/jsonformat.c @@ -0,0 +1,522 @@ +/* + * 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 +#include +#include +#include +#include +#include "jsonformat.h" + +#define _(T) \ + int vl_api_##T##_fromjson (cJSON *o, T *d) \ + { \ + if (!cJSON_IsNumber (o)) \ + return -1; \ + d[0] = (T) cJSON_GetNumberValue (o); \ + return 0; \ + } +foreach_type_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)); + if (!unformat (&input, "0x%U", unformat_hex_string, s)) + return -1; + 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)); + if (!unformat (&input, "0x%U", unformat_hex_string, &s)) + return 0; + return s; +} + +int +u8string_fromjson2(cJSON *o, char *fieldname, u8 *data) +{ + u8 *s = u8string_fromjson(o, fieldname); + if (!s) + return -1; + 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; +} + +int +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 -1; + unformat_init_string (&input, p, strlen(p)); + if (!unformat (&input, "%U", unformat_ip4_address, a)) + return -1; + return 0; +} + +int +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 -1; + unformat_init_string (&input, p, strlen(p)); + if (!unformat (&input, "%U/%d", unformat_ip4_address, &a->address, + &a->len)) + return -1; + return 0; +} + +int +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); +} +int +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 -1; + unformat_init_string (&input, p, strlen(p)); + if (!unformat (&input, "%U", unformat_ip6_address, a)) + return -1; + return 0; +} + +int +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 -1; + unformat_init_string (&input, p, strlen(p)); + if (!unformat (&input, "%U/%d", unformat_ip6_address, &a->address, &a->len)) + return -1; + return 0; +} + +int +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); +} + +int +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 -1; + unformat_init_string (&input, p, strlen(p)); + if (unformat (&input, "%U", unformat_ip4_address, &a->un.ip4)) + a->af = ADDRESS_IP4; + else if (unformat (&input, "%U", unformat_ip6_address, &a->un.ip6)) + a->af = ADDRESS_IP6; + else + return -1; + return 0; +} + +int +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 -1; + unformat_init_string (&input, p, strlen(p)); + int plen; + if (unformat (&input, "%U/%d", unformat_ip4_address, &a->address.un.ip4, &plen)) + a->address.af = ADDRESS_IP4; + else if (unformat (&input, "%U/%d", unformat_ip6_address, &a->address.un.ip6, &plen)) + a->address.af = ADDRESS_IP6; + else + return -1; + a->len = plen; + return 0; +} + +int +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); +} + +int +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)); + if (!unformat (&input, "%U", unformat_mac_address, a)) + return -1; + return 0; +} + +/* 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); +} + +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; +} +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) { + return vl_api_ip4_prefix_t_tojson (a); +} +cJSON * +vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a) { + return vl_api_ip6_prefix_t_tojson (a); +} +cJSON * +vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a) { + return vl_api_prefix_t_tojson (a); +} +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_type_tojson +#undef _ diff --git a/src/vppinfra/jsonformat.h b/src/vppinfra/jsonformat.h new file mode 100644 index 00000000000..d27785f0058 --- /dev/null +++ b/src/vppinfra/jsonformat.h @@ -0,0 +1,116 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#define foreach_type_fromjson \ + _ (i8) \ + _ (u8) \ + _ (i16) \ + _ (u16) \ + _ (i32) \ + _ (u32) \ + _ (u64) \ + _ (f64) + +#define _(T) CJSON_PUBLIC (int) vl_api_##T##_fromjson (cJSON *o, T *d); +foreach_type_fromjson +#undef _ + +/* Prototypes */ +CJSON_PUBLIC (int) vl_api_bool_fromjson (cJSON *o, bool *d); +CJSON_PUBLIC (int) +vl_api_ip4_address_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_ip4_address_t *a); +CJSON_PUBLIC (int) +vl_api_ip4_prefix_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_ip4_prefix_t *a); +CJSON_PUBLIC (int) +vl_api_ip4_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_ip4_prefix_t *a); +CJSON_PUBLIC (int) +vl_api_ip6_address_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_ip6_address_t *a); +CJSON_PUBLIC (int) +vl_api_ip6_prefix_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_ip6_prefix_t *a); +CJSON_PUBLIC (int) +vl_api_ip6_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_ip6_prefix_t *a); +CJSON_PUBLIC (int) +vl_api_address_t_fromjson (void **mp, int *len, cJSON *o, vl_api_address_t *a); +CJSON_PUBLIC (int) +vl_api_prefix_t_fromjson (void **mp, int *len, cJSON *o, vl_api_prefix_t *a); +CJSON_PUBLIC (int) +vl_api_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_prefix_t *a); +CJSON_PUBLIC (int) +vl_api_mac_address_t_fromjson (void **mp, int *len, cJSON *o, + vl_api_mac_address_t *a); + +CJSON_PUBLIC (uword) +unformat_ip4_address (unformat_input_t *input, va_list *args); +CJSON_PUBLIC (uword) +unformat_ip6_address (unformat_input_t *input, va_list *args); +CJSON_PUBLIC (u8 *) format_ip6_address (u8 *s, va_list *args); +CJSON_PUBLIC (uword) +unformat_mac_address (unformat_input_t *input, va_list *args); +CJSON_PUBLIC (u8 *) format_ip4_address (u8 *s, va_list *args); +CJSON_PUBLIC (uword) +unformat_vl_api_timedelta_t (unformat_input_t *input, va_list *args); +CJSON_PUBLIC (uword) +unformat_vl_api_timestamp_t (unformat_input_t *input, va_list *args); +CJSON_PUBLIC (uword) +unformat_vl_api_gbp_scope_t (unformat_input_t *input, va_list *args); + +CJSON_PUBLIC (int) +vl_api_c_string_to_api_string (const char *buf, vl_api_string_t *str); +CJSON_PUBLIC (void) +vl_api_string_cJSON_AddToObject (cJSON *const object, const char *const name, + vl_api_string_t *astr); + +CJSON_PUBLIC (u8 *) u8string_fromjson (cJSON *o, char *fieldname); +CJSON_PUBLIC (int) u8string_fromjson2 (cJSON *o, char *fieldname, u8 *data); +CJSON_PUBLIC (int) vl_api_u8_string_fromjson (cJSON *o, u8 *s, int len); + +#define foreach_type_tojson \ + _ (ip4_address) \ + _ (ip4_prefix) \ + _ (ip6_address) \ + _ (ip6_prefix) \ + _ (address) \ + _ (prefix) \ + _ (mac_address) + +#define _(T) CJSON_PUBLIC (cJSON *) vl_api_##T##_t_tojson (vl_api_##T##_t *); +foreach_type_tojson +#undef _ + +CJSON_PUBLIC (cJSON *) + vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a); +CJSON_PUBLIC (cJSON *) +vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a); +CJSON_PUBLIC (cJSON *) +vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a); + +#endif -- cgit 1.2.3-korg