ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / src / vat2 / main.c
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdbool.h>
19 #include <ctype.h>
20 #include <getopt.h>
21 #include <string.h>
22 #include <vlib/vlib.h>
23 #include <vlibapi/api_types.h>
24 #include <vppinfra/hash.h>
25 #include <vppinfra/cJSON.h>
26
27 /* VPP API client includes */
28 #include <vpp-api/client/vppapiclient.h>
29
30 #include <limits.h>
31 #include "vat2.h"
32
33 bool vat2_debug;
34
35 /*
36  * Filter these messages as they are used to manage the API connection to VPP
37  */
38 char *filter_messages_strings[] = { "memclnt_create",
39                                     "memclnt_delete",
40                                     "sockclnt_create",
41                                     "sockclnt_delete",
42                                     "memclnt_rx_thread_suspend",
43                                     "memclnt_read_timeout",
44                                     "rx_thread_exit",
45                                     "trace_plugin_msg_ids",
46                                     0 };
47
48 static bool
49 filter_message (char *msgname)
50 {
51   char **p = filter_messages_strings;
52
53   while (*p)
54     {
55       if (strcmp (*p, msgname) == 0)
56         return true;
57       p++;
58     }
59   return false;
60 }
61
62 uword *function_by_name;
63 bool debug = false;
64
65 static u8 *
66 vat2_find_plugin_path (void)
67 {
68   char *p, path[PATH_MAX];
69   int rv;
70   u8 *s;
71
72   /* find executable path */
73   if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
74     return 0;
75
76   /* readlink doesn't provide null termination */
77   path[rv] = 0;
78
79   /* strip filename */
80   if ((p = strrchr (path, '/')) == 0)
81     return 0;
82   *p = 0;
83
84   /* strip bin/ */
85   if ((p = strrchr (path, '/')) == 0)
86     return 0;
87   *p = 0;
88
89   s = format (0, "%s/" CLIB_LIB_DIR "/vat2_plugins", path, path);
90   vec_add1 (s, 0);
91   return s;
92 }
93
94 void
95 vac_callback (unsigned char *data, int len)
96 {
97   u16 result_msg_id = ntohs (*((u16 *) data));
98   DBG ("Received something async: %d\n", result_msg_id);
99 }
100
101 int vat2_load_plugins (u8 *path, char *filter, int *loaded);
102
103 static int
104 register_function (char *pluginpath)
105 {
106   int loaded;
107   u8 *vat2_plugin_path = 0;
108
109   if (pluginpath == 0)
110     {
111       vat2_plugin_path = vat2_find_plugin_path ();
112     }
113   else
114     {
115       vat2_plugin_path = format (0, "%s", pluginpath);
116       vec_add1 (vat2_plugin_path, 0);
117     }
118   DBG ("Plugin Path %s\n", vat2_plugin_path);
119   int rv = vat2_load_plugins (vat2_plugin_path, 0, &loaded);
120   DBG ("Loaded %u plugins\n", loaded);
121
122   vec_free (vat2_plugin_path);
123
124   return rv;
125 }
126
127 struct apifuncs_s
128 {
129   cJSON (*f) (cJSON *);
130   cJSON (*tojson) (void *);
131   u32 crc;
132 };
133
134 struct apifuncs_s *apifuncs = 0;
135
136 void
137 vat2_register_function (char *name, cJSON (*f) (cJSON *),
138                         cJSON (*tojson) (void *), u32 crc)
139 {
140   struct apifuncs_s funcs = { .f = f, .tojson = tojson, .crc = crc };
141   vec_add1 (apifuncs, funcs);
142   hash_set_mem (function_by_name, name, vec_len (apifuncs) - 1);
143 }
144
145 static int
146 vat2_exec_command_by_name (char *msgname, cJSON *o)
147 {
148   u32 crc = 0;
149   if (filter_message (msgname))
150     return 0;
151
152   cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
153   if (crc_obj)
154     {
155       char *crc_str = cJSON_GetStringValue (crc_obj);
156       crc = (u32) strtol (crc_str, NULL, 16);
157     }
158
159   uword *p = hash_get_mem (function_by_name, msgname);
160   if (!p)
161     {
162       fprintf (stderr, "No such command %s\n", msgname);
163       return -1;
164     }
165   if (crc && crc != apifuncs[p[0]].crc)
166     {
167       fprintf (stderr, "API CRC does not match: %s!\n", msgname);
168     }
169
170   cJSON *(*fp) (cJSON *);
171   fp = (void *) apifuncs[p[0]].f;
172   cJSON *r = (*fp) (o);
173
174   if (r)
175     {
176       char *output = cJSON_Print (r);
177       cJSON_Delete (r);
178       printf ("%s\n", output);
179       free (output);
180     }
181   else
182     {
183       fprintf (stderr, "Call failed: %s\n", msgname);
184       return -1;
185     }
186   return 0;
187 }
188
189 static int
190 vat2_exec_command (cJSON *o)
191 {
192
193   cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
194   if (!msg_id_obj)
195     {
196       fprintf (stderr, "Missing '_msgname' element!\n");
197       return -1;
198     }
199
200   char *name = cJSON_GetStringValue (msg_id_obj);
201
202   return vat2_exec_command_by_name (name, o);
203 }
204
205 static void
206 print_template (char *msgname)
207 {
208   uword *p = hash_get_mem (function_by_name, msgname);
209   if (!p)
210     goto error;
211
212   cJSON *(*fp) (void *);
213   fp = (void *) apifuncs[p[0]].tojson;
214   if (!fp)
215     goto error;
216
217   void *scratch = malloc (2048);
218   if (!scratch)
219     goto error;
220
221   memset (scratch, 0, 2048);
222   cJSON *t = fp (scratch);
223   if (!t)
224     goto error;
225   free (scratch);
226   char *output = cJSON_Print (t);
227   if (!output)
228     goto error;
229
230   cJSON_Delete (t);
231   printf ("%s\n", output);
232   free (output);
233
234   return;
235
236 error:
237   fprintf (stderr, "error printing template for: %s\n", msgname);
238 }
239
240 static void
241 dump_apis (void)
242 {
243   char *name;
244   u32 *i;
245   hash_foreach_mem (name, i, function_by_name, ({ printf ("%s\n", name); }));
246 }
247
248 static void
249 print_help (void)
250 {
251   char *help_string =
252     "Usage: vat2 [OPTION] <message-name> <JSON object>\n"
253     "Send API message to VPP and print reply\n"
254     "\n"
255     "-d, --debug                    Print additional information\n"
256     "-p, --prefix <prefix>          Specify shared memory prefix to connect "
257     "to a given VPP instance\n"
258     "-f, --file <filename>          File containing a JSON object with the "
259     "arguments for the message to send\n"
260     "-t, --template <message-name>  Print a template JSON object for given API"
261     " message\n"
262     "--dump-apis                    List all APIs available in VAT2 (might "
263     "not reflect running VPP)\n"
264     "--plugin-path                  Pluing path"
265     "\n";
266   printf ("%s", help_string);
267 }
268
269 int
270 main (int argc, char **argv)
271 {
272   /* Create a heap of 64MB */
273   clib_mem_init (0, 64 << 20);
274   char *filename = 0, *prefix = 0, *template = 0, *pluginpath = 0;
275   int index;
276   int c;
277   opterr = 0;
278   cJSON *o = 0;
279   int option_index = 0;
280   bool dump_api = false;
281   char *msgname = 0;
282   static struct option long_options[] = {
283     { "debug", no_argument, 0, 'd' },
284     { "prefix", required_argument, 0, 's' },
285     { "file", required_argument, 0, 'f' },
286     { "dump-apis", no_argument, 0, 0 },
287     { "template", required_argument, 0, 't' },
288     { "plugin-path", required_argument, 0, 'p' },
289     { 0, 0, 0, 0 }
290   };
291
292   while ((c = getopt_long (argc, argv, "hdp:f:t:", long_options,
293                            &option_index)) != -1)
294     {
295       switch (c)
296         {
297         case 0:
298           if (option_index == 3)
299             dump_api = true;
300           break;
301         case 'd':
302           vat2_debug = true;
303           break;
304         case 't':
305           template = optarg;
306           break;
307         case 's':
308           prefix = optarg;
309           break;
310         case 'f':
311           filename = optarg;
312           break;
313         case 'p':
314           pluginpath = optarg;
315           break;
316         case '?':
317           print_help ();
318           return 1;
319         default:
320           abort ();
321         }
322     }
323   DBG ("debug = %d, filename = %s, template = %s, shared memory prefix: %s\n",
324        vat2_debug, filename, template, prefix);
325
326   for (index = optind; index < argc; index++)
327     DBG ("Non-option argument %s\n", argv[index]);
328
329   index = optind;
330
331   if (argc > index + 2)
332     {
333       fprintf (stderr, "%s: Too many arguments\n", argv[0]);
334       exit (-1);
335     }
336
337   /* Load plugins */
338   function_by_name = hash_create_string (0, sizeof (uword));
339   int res = register_function (pluginpath);
340   if (res < 0)
341     {
342       fprintf (stderr, "%s: loading plugins failed\n", argv[0]);
343       exit (-1);
344     }
345
346   if (template)
347     {
348       print_template (template);
349       exit (0);
350     }
351
352   if (dump_api)
353     {
354       dump_apis ();
355       exit (0);
356     }
357
358   /* Read message arguments from command line */
359   if (argc >= (index + 1))
360     {
361       msgname = argv[index];
362     }
363   if (argc == (index + 2))
364     {
365       o = cJSON_Parse (argv[index + 1]);
366       if (!o)
367         {
368           fprintf (stderr, "%s: Failed parsing JSON input: %s\n", argv[0],
369                    cJSON_GetErrorPtr ());
370           exit (-1);
371         }
372     }
373
374   if (!msgname && !filename)
375     {
376       print_help ();
377       exit (-1);
378     }
379
380   /* Read message from file */
381   if (filename)
382     {
383       if (argc > index)
384         {
385           fprintf (stderr, "%s: Superfluous arguments when filename given\n",
386                    argv[0]);
387           exit (-1);
388         }
389
390       FILE *f = fopen (filename, "r");
391       size_t chunksize, bufsize;
392       size_t n_read = 0;
393       size_t n;
394
395       if (!f)
396         {
397           fprintf (stderr, "%s: can't open file: %s\n", argv[0], filename);
398           exit (-1);
399         }
400
401       chunksize = bufsize = 1024;
402       char *buf = malloc (bufsize);
403       while ((n = fread (buf + n_read, 1, chunksize, f)))
404         {
405           n_read += n;
406           if (n == chunksize)
407             {
408               bufsize += chunksize;
409               buf = realloc (buf, bufsize);
410             }
411         }
412       fclose (f);
413       if (n_read)
414         {
415           o = cJSON_Parse (buf);
416           if (!o)
417             {
418               fprintf (stderr, "%s: Failed parsing JSON input: %s\n", argv[0],
419                        cJSON_GetErrorPtr ());
420               exit (-1);
421             }
422         }
423       free (buf);
424     }
425
426   if (!o)
427     {
428       fprintf (stderr, "%s: Failed parsing JSON input\n", argv[0]);
429       exit (-1);
430     }
431
432   if (vac_connect ("vat2", prefix, 0, 1024))
433     {
434       fprintf (stderr, "Failed connecting to VPP\n");
435       exit (-1);
436     }
437
438   if (msgname)
439     {
440       vat2_exec_command_by_name (msgname, o);
441     }
442   else
443     {
444       if (cJSON_IsArray (o))
445         {
446           size_t size = cJSON_GetArraySize (o);
447           for (int i = 0; i < size; i++)
448             vat2_exec_command (cJSON_GetArrayItem (o, i));
449         }
450     }
451   cJSON_Delete (o);
452   vac_disconnect ();
453   exit (0);
454 }