/* * Copyright (c) 2020 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <ctype.h> #include <getopt.h> #include <string.h> #include <vlib/vlib.h> #include <vlibapi/api_types.h> #include <vppinfra/hash.h> #include <vppinfra/cJSON.h> /* VPP API client includes */ #include <vpp-api/client/vppapiclient.h> #include <limits.h> #include "vat2.h" bool vat2_debug; /* * 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; static u8 * vat2_find_plugin_path (void) { char *p, path[PATH_MAX]; int rv; u8 *s; /* find executable path */ if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1) return 0; /* readlink doesn't provide null termination */ path[rv] = 0; /* strip filename */ if ((p = strrchr (path, '/')) == 0) return 0; *p = 0; /* strip bin/ */ if ((p = strrchr (path, '/')) == 0) return 0; *p = 0; s = format (0, "%s/" CLIB_LIB_DIR "/vat2_plugins", path, path); vec_add1 (s, 0); return s; } void vac_callback (unsigned char *data, int len) { u16 result_msg_id = ntohs (*((u16 *) data)); DBG ("Received something async: %d\n", result_msg_id); } int vat2_load_plugins (u8 *path, char *filter, int *loaded); static int register_function (char *pluginpath) { int loaded; u8 *vat2_plugin_path = 0; if (pluginpath == 0) { vat2_plugin_path = vat2_find_plugin_path (); } else { vat2_plugin_path = format (0, "%s", pluginpath); vec_add1 (vat2_plugin_path, 0); } DBG ("Plugin Path %s\n", vat2_plugin_path); int rv = vat2_load_plugins (vat2_plugin_path, 0, &loaded); DBG ("Loaded %u plugins\n", loaded); vec_free (vat2_plugin_path); return rv; } 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 *), u32 crc) { 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); } static int vat2_exec_command_by_name (char *msgname, cJSON *o) { u32 crc = 0; if (filter_message (msgname)) return 0; cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc"); if (crc_obj) { char *crc_str = cJSON_GetStringValue (crc_obj); crc = (u32) strtol (crc_str, NULL, 16); } uword *p = hash_get_mem (function_by_name, msgname); if (!p) { fprintf (stderr, "No such command %s\n", msgname); return -1; } if (crc && crc != apifuncs[p[0]].crc) { fprintf (stderr, "API CRC does not match: %s!\n", msgname); } cJSON *(*fp) (cJSON *); fp = (void *) apifuncs[p[0]].f; cJSON *r = (*fp) (o); if (r) { char *output = cJSON_Print (r); cJSON_Delete (r); printf ("%s\n", output); free (output); } else { fprintf (stderr, "Call failed: %s\n", msgname); return -1; } return 0; } static int vat2_exec_command (cJSON *o) { cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname"); if (!msg_id_obj) { fprintf (stderr, "Missing '_msgname' element!\n"); return -1; } char *name = cJSON_GetStringValue (msg_id_obj); return vat2_exec_command_by_name (name, o); } static void print_template (char *msgname) { uword *p = hash_get_mem (function_by_name, msgname); if (!p) goto error; cJSON *(*fp) (void *); fp = (void *) apifuncs[p[0]].tojson; if (!fp) goto error; void *scratch = malloc (2048); if (!scratch) goto error; memset (scratch, 0, 2048); cJSON *t = fp (scratch); if (!t) goto error; free (scratch); char *output = cJSON_Print (t); if (!output) goto error; cJSON_Delete (t); printf ("%s\n", output); free (output); return; error: fprintf (stderr, "error printing template for: %s\n", msgname); } static void dump_apis (void) { char *name; u32 *i; hash_foreach_mem (name, i, function_by_name, ({ printf ("%s\n", name); })); } static void print_help (void) { char *help_string = "Usage: vat2 [OPTION] <message-name> <JSON object>\n" "Send API message to VPP and print reply\n" "\n" "-d, --debug Print additional information\n" "-p, --prefix <prefix> Specify shared memory prefix to connect " "to a given VPP instance\n" "-f, --file <filename> File containing a JSON object with the " "arguments for the message to send\n" "-t, --template <message-name> Print a template JSON object for given API" " message\n" "--dump-apis List all APIs available in VAT2 (might " "not reflect running VPP)\n" "--plugin-path Pluing path" "\n"; printf ("%s", help_string); } int main (int argc, char **argv) { /* Create a heap of 64MB */ clib_mem_init (0, 64 << 20); char *filename = 0, *prefix = 0, *template = 0, *pluginpath = 0; int index; int c; opterr = 0; cJSON *o = 0; int option_index = 0; bool dump_api = false; char *msgname = 0; static struct option long_options[] = { { "debug", no_argument, 0, 'd' }, { "prefix", required_argument, 0, 's' }, { "file", required_argument, 0, 'f' }, { "dump-apis", no_argument, 0, 0 }, { "template", required_argument, 0, 't' }, { "plugin-path", required_argument, 0, 'p' }, { 0, 0, 0, 0 } }; while ((c = getopt_long (argc, argv, "hdp:f:t:", long_options, &option_index)) != -1) { switch (c) { case 0: if (option_index == 3) dump_api = true; break; case 'd': vat2_debug = true; break; case 't': template = optarg; break; case 's': prefix = optarg; break; case 'f': filename = optarg; break; case 'p': pluginpath = optarg; break; case '?': print_help (); return 1; default: abort (); } } DBG ("debug = %d, filename = %s, template = %s, shared memory prefix: %s\n", vat2_debug, filename, template, prefix); for (index = optind; index < argc; index++) DBG ("Non-option argument %s\n", argv[index]); index = optind; if (argc > index + 2) { fprintf (stderr, "%s: Too many arguments\n", argv[0]); exit (-1); } /* Load plugins */ function_by_name = hash_create_string (0, sizeof (uword)); int res = register_function (pluginpath); if (res < 0) { fprintf (stderr, "%s: loading plugins failed\n", argv[0]); exit (-1); } if (template) { print_template (template); exit (0); } if (dump_api) { dump_apis (); exit (0); } /* Read message arguments from command line */ if (argc >= (index + 1)) { msgname = argv[index]; } if (argc == (index + 2)) { o = cJSON_Parse (argv[index + 1]); if (!o) { fprintf (stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr ()); exit (-1); } } if (!msgname && !filename) { print_help (); exit (-1); } /* Read message from file */ if (filename) { if (argc > index) { fprintf (stderr, "%s: Superfluous arguments when filename given\n", argv[0]); exit (-1); } FILE *f = fopen (filename, "r"); size_t chunksize, bufsize; size_t n_read = 0; size_t n; if (!f) { 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))) { n_read += n; if (n == chunksize) { bufsize += chunksize; buf = realloc (buf, bufsize); } } fclose (f); if (n_read) { o = cJSON_Parse (buf); if (!o) { fprintf (stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr ()); exit (-1); } } free (buf); } if (!o) { fprintf (stderr, "%s: Failed parsing JSON input\n", argv[0]); exit (-1); } if (vac_connect ("vat2", prefix, 0, 1024)) { fprintf (stderr, "Failed connecting to VPP\n"); exit (-1); } if (msgname) { vat2_exec_command_by_name (msgname, o); } else { if (cJSON_IsArray (o)) { size_t size = cJSON_GetArraySize (o); for (int i = 0; i < size; i++) vat2_exec_command (cJSON_GetArrayItem (o, i)); } } cJSON_Delete (o); vac_disconnect (); exit (0); }