ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / src / vlibmemory / vlib_api_cli.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2018 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25
26 static clib_error_t *
27 vl_api_show_histogram_command (vlib_main_t * vm,
28                                unformat_input_t * input,
29                                vlib_cli_command_t * cli_cmd)
30 {
31   u64 total_counts = 0;
32   int i;
33
34   for (i = 0; i < SLEEP_N_BUCKETS; i++)
35     {
36       total_counts += vector_rate_histogram[i];
37     }
38
39   if (total_counts == 0)
40     {
41       vlib_cli_output (vm, "No control-plane activity.");
42       return 0;
43     }
44
45 #define _(n)                                                    \
46     do {                                                        \
47         f64 percent;                                            \
48         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
49             / (f64) total_counts;                               \
50         percent *= 100.0;                                       \
51         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
52                          vector_rate_histogram[SLEEP_##n##_US], \
53                          percent);                              \
54     } while (0);
55   foreach_histogram_bucket;
56 #undef _
57
58   return 0;
59 }
60
61 /*?
62  * Display the binary api sleep-time histogram
63 ?*/
64 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) =
65 {
66   .path = "show api histogram",
67   .short_help = "show api histogram",
68   .function = vl_api_show_histogram_command,
69 };
70
71 static clib_error_t *
72 vl_api_clear_histogram_command (vlib_main_t * vm,
73                                 unformat_input_t * input,
74                                 vlib_cli_command_t * cli_cmd)
75 {
76   int i;
77
78   for (i = 0; i < SLEEP_N_BUCKETS; i++)
79     vector_rate_histogram[i] = 0;
80   return 0;
81 }
82
83 /*?
84  * Clear the binary api sleep-time histogram
85 ?*/
86 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) =
87 {
88   .path = "clear api histogram",
89   .short_help = "clear api histogram",
90   .function = vl_api_clear_histogram_command,
91 };
92
93 static clib_error_t *
94 vl_api_client_command (vlib_main_t * vm,
95                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
96 {
97   vl_api_registration_t **regpp, *regp;
98   svm_queue_t *q;
99   char *health;
100   api_main_t *am = vlibapi_get_main ();
101   u32 *confused_indices = 0;
102
103   if (!pool_elts (am->vl_clients))
104     goto socket_clients;
105   vlib_cli_output (vm, "Shared memory clients");
106   vlib_cli_output (vm, "%20s %8s %14s %18s %s",
107                    "Name", "PID", "Queue Length", "Queue VA", "Health");
108
109   pool_foreach (regpp, am->vl_clients)
110    {
111     regp = *regpp;
112
113     if (regp)
114       {
115         if (regp->unanswered_pings > 0)
116           health = "questionable";
117         else
118           health = "OK";
119
120         q = regp->vl_input_queue;
121
122         vlib_cli_output (vm, "%20s %8d %14d 0x%016llx %s\n",
123                          regp->name, q->consumer_pid, q->cursize,
124                          q, health);
125       }
126     else
127       {
128         clib_warning ("NULL client registration index %d",
129                       regpp - am->vl_clients);
130         vec_add1 (confused_indices, regpp - am->vl_clients);
131       }
132   }
133
134   /* This should "never happen," but if it does, fix it... */
135   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
136     {
137       int i;
138       for (i = 0; i < vec_len (confused_indices); i++)
139         {
140           pool_put_index (am->vl_clients, confused_indices[i]);
141         }
142     }
143   vec_free (confused_indices);
144
145   if (am->missing_clients)
146     vlib_cli_output (vm, "%u messages with missing clients",
147                      am->missing_clients);
148 socket_clients:
149   vl_sock_api_dump_clients (vm, am);
150
151   return 0;
152 }
153
154 static clib_error_t *
155 vl_api_status_command (vlib_main_t * vm,
156                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
157 {
158   api_main_t *am = vlibapi_get_main ();
159
160   /* check if rx_trace and tx_trace are not null pointers */
161   if (am->rx_trace == 0)
162     {
163       vlib_cli_output (vm, "RX Trace disabled\n");
164     }
165   else
166     {
167       if (am->rx_trace->enabled == 0)
168         vlib_cli_output (vm, "RX Trace disabled\n");
169       else
170         vlib_cli_output (vm, "RX Trace enabled\n");
171     }
172
173   if (am->tx_trace == 0)
174     {
175       vlib_cli_output (vm, "TX Trace disabled\n");
176     }
177   else
178     {
179       if (am->tx_trace->enabled == 0)
180         vlib_cli_output (vm, "TX Trace disabled\n");
181       else
182         vlib_cli_output (vm, "TX Trace enabled\n");
183     }
184
185   return 0;
186 }
187
188 VLIB_CLI_COMMAND (cli_show_api_command, static) =
189 {
190   .path = "show api",
191   .short_help = "Show API information",
192 };
193
194 /*?
195  * Display current api client connections
196 ?*/
197 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) =
198 {
199   .path = "show api clients",
200   .short_help = "Client information",
201   .function = vl_api_client_command,
202 };
203
204 /*?
205  * Display the current api message tracing status
206 ?*/
207 VLIB_CLI_COMMAND (cli_show_api_status_command, static) =
208 {
209   .path = "show api trace-status",
210   .short_help = "Display API trace status",
211   .function = vl_api_status_command,
212 };
213
214 static clib_error_t *
215 vl_api_message_table_command (vlib_main_t * vm,
216                               unformat_input_t * input,
217                               vlib_cli_command_t * cli_cmd)
218 {
219   api_main_t *am = vlibapi_get_main ();
220   int i;
221   int verbose = 0;
222
223   if (unformat (input, "verbose"))
224     verbose = 1;
225
226
227   if (verbose == 0)
228     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
229   else
230     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
231                      "MP-safe");
232
233   for (i = 1; i < vec_len (am->msg_data); i++)
234     {
235       vl_api_msg_data_t *m = vl_api_get_msg_data (am, i);
236       if (verbose == 0)
237         {
238           vlib_cli_output (vm, "%-4d %s", i,
239                            m->name ? m->name : "  [no handler]");
240         }
241       else
242         {
243           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
244                            m->name ? m->name : "  [no handler]", m->bounce,
245                            m->is_mp_safe);
246         }
247     }
248
249   return 0;
250 }
251
252 /*?
253  * Display the current api message decode tables
254 ?*/
255 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
256 {
257   .path = "show api message-table",
258   .short_help = "Message Table",
259   .function = vl_api_message_table_command,
260 };
261
262 static int
263 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
264 {
265   int len0, len1, clen;
266
267   len0 = vec_len (a0->name);
268   len1 = vec_len (a1->name);
269   clen = len0 < len1 ? len0 : len1;
270   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
271 }
272
273 static u8 *
274 format_api_msg_range (u8 * s, va_list * args)
275 {
276   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
277
278   if (rp == 0)
279     s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
280   else
281     s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
282                 rp->last_msg_id);
283
284   return s;
285 }
286
287 static clib_error_t *
288 vl_api_show_plugin_command (vlib_main_t * vm,
289                             unformat_input_t * input,
290                             vlib_cli_command_t * cli_cmd)
291 {
292   api_main_t *am = vlibapi_get_main ();
293   vl_api_msg_range_t *rp = 0;
294   int i;
295
296   if (vec_len (am->msg_ranges) == 0)
297     {
298       vlib_cli_output (vm, "No plugin API message ranges configured...");
299       return 0;
300     }
301
302   rp = vec_dup (am->msg_ranges);
303
304   vec_sort_with_function (rp, range_compare);
305
306   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
307   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
308
309   for (i = 0; i < vec_len (rp); i++)
310     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
311
312   vec_free (rp);
313
314   return 0;
315 }
316
317 /*?
318  * Display the plugin binary API message range table
319 ?*/
320 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
321 {
322   .path = "show api plugin",
323   .short_help = "show api plugin",
324   .function = vl_api_show_plugin_command,
325 };
326
327 typedef enum
328 {
329   DUMP,
330   DUMP_JSON,
331   REPLAY,
332   INITIALIZERS,
333 } vl_api_replay_t;
334
335 u8 *
336 format_vl_msg_api_trace_status (u8 * s, va_list * args)
337 {
338   api_main_t *am = va_arg (*args, api_main_t *);
339   vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
340   vl_api_trace_t *tp;
341   char *trace_name;
342
343   switch (which)
344     {
345     case VL_API_TRACE_TX:
346       tp = am->tx_trace;
347       trace_name = "TX trace";
348       break;
349
350     case VL_API_TRACE_RX:
351       tp = am->rx_trace;
352       trace_name = "RX trace";
353       break;
354
355     default:
356       abort ();
357     }
358
359   if (tp == 0)
360     {
361       s = format (s, "%s: not yet configured.\n", trace_name);
362       return s;
363     }
364
365   s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
366               trace_name, vec_len (tp->traces), tp->nitems,
367               tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
368   return s;
369 }
370
371 static void
372 vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
373                          u32 first_index, u32 last_index,
374                          vl_api_replay_t which)
375 {
376   vl_api_trace_file_header_t *hp;
377   int i, fd;
378   u16 *msgid_vec = 0;
379   struct stat statb;
380   size_t file_size;
381   u8 *msg;
382   api_main_t *am = vlibapi_get_main ();
383   u8 *tmpbuf = 0;
384   u32 nitems, nitems_msgtbl;
385
386   fd = open ((char *) filename, O_RDONLY);
387
388   if (fd < 0)
389     {
390       vlib_cli_output (vm, "Couldn't open %s\n", filename);
391       return;
392     }
393
394   if (fstat (fd, &statb) < 0)
395     {
396       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
397       close (fd);
398       return;
399     }
400
401   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
402     {
403       vlib_cli_output (vm, "File not plausible: %s\n", filename);
404       close (fd);
405       return;
406     }
407
408   file_size = statb.st_size;
409   file_size = (file_size + 4095) & ~(4095);
410
411   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
412
413   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
414     {
415       vlib_cli_output (vm, "mmap failed: %s\n", filename);
416       close (fd);
417       return;
418     }
419   close (fd);
420
421   clib_mem_unpoison (hp, file_size);
422
423   nitems = ntohl (hp->nitems);
424
425   if (last_index == (u32) ~ 0)
426     {
427       last_index = nitems - 1;
428     }
429
430   if (first_index >= nitems || last_index >= nitems)
431     {
432       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
433                        first_index, last_index, nitems - 1);
434       munmap (hp, file_size);
435       return;
436     }
437   if (hp->wrapped)
438     vlib_cli_output (vm,
439                      "Note: wrapped/incomplete trace, results may vary\n");
440
441   size_t file_size_left = file_size;
442
443 #define assert_size(size_left, s)                                             \
444   do                                                                          \
445     {                                                                         \
446       if ((s) >= size_left)                                                   \
447         {                                                                     \
448           vlib_cli_output (vm, "corrupted file");                             \
449           munmap (hp, file_size);                                             \
450           vec_free (msgid_vec);                                               \
451           return;                                                             \
452         }                                                                     \
453       size_left -= s;                                                         \
454     }                                                                         \
455   while (0);
456
457   assert_size (file_size_left, sizeof (hp[0]));
458   msg = (u8 *) (hp + 1);
459
460   serialize_main_t _sm, *sm = &_sm;
461   u32 msgtbl_size = ntohl (hp->msgtbl_size);
462   u8 *name_and_crc;
463
464   assert_size (file_size_left, msgtbl_size);
465
466   unserialize_open_data (sm, msg, msgtbl_size);
467   unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
468
469   for (i = 0; i < nitems_msgtbl; i++)
470     {
471       u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
472       unserialize_cstring (sm, (char **) &name_and_crc);
473       u32 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
474       ASSERT (~0 == msg_index2 || msg_index2 <= 65535);
475       if (~0 == msg_index2)
476         vlib_cli_output (vm, "warning: can't find msg index for id %d\n",
477                          msg_index);
478       vec_validate (msgid_vec, msg_index);
479       msgid_vec[msg_index] = msg_index2;
480     }
481
482   msg += msgtbl_size;
483
484   for (i = 0; i < first_index; i++)
485     {
486       int size;
487       u16 msg_id;
488
489       assert_size (file_size_left, sizeof (u32));
490       size = clib_host_to_net_u32 (*(u32 *) msg);
491       msg += sizeof (u32);
492
493       assert_size (file_size_left, clib_max (size, sizeof (u16)));
494       msg_id = ntohs (*((u16 *) msg));
495       if (msg_id >= vec_len (msgid_vec) ||
496           msgid_vec[msg_id] >= vec_len (am->msg_data))
497         vlib_cli_output (vm, "warning: unknown msg id %d for msg number %d\n",
498                          msg_id, i);
499
500       msg += size;
501     }
502
503   if (which == REPLAY)
504     am->replay_in_progress = 1;
505
506   for (; i <= last_index; i++)
507     {
508       vl_api_msg_data_t *m;
509       u16 msg_id;
510       int size;
511
512       if (which == DUMP)
513         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
514
515       assert_size (file_size_left, sizeof (u32));
516       size = clib_host_to_net_u32 (*(u32 *) msg);
517       msg += sizeof (u32);
518
519       assert_size (file_size_left, clib_max (size, sizeof (u16)));
520       msg_id = ntohs (*((u16 *) msg));
521
522       if (msg_id >= vec_len (msgid_vec) ||
523           msgid_vec[msg_id] >= vec_len (am->msg_data))
524         {
525           vlib_cli_output (
526             vm, "warning: unknown msg id %d for msg number %d, skipping\n",
527             msg_id, i);
528           msg += size;
529           continue;
530         }
531
532       msg_id = msgid_vec[msg_id];
533       m = vl_api_get_msg_data (am, msg_id);
534
535       /* Copy the buffer (from the read-only mmap'ed file) */
536       vec_validate (tmpbuf, size - 1 + sizeof (uword));
537       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
538       clib_memset (tmpbuf, 0xf, sizeof (uword));
539
540       /*
541        * Endian swap if needed. All msg data is supposed to be in
542        * network byte order.
543        */
544       if (((which == DUMP || which == DUMP_JSON) &&
545            clib_arch_is_little_endian))
546         {
547           if (m && m->endian_handler == 0)
548             {
549               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
550               munmap (hp, file_size);
551               vec_free (tmpbuf);
552               am->replay_in_progress = 0;
553               return;
554             }
555           if (m)
556             {
557               m->endian_handler (tmpbuf + sizeof (uword), 1 /* to network */);
558             }
559         }
560
561       /* msg_id always in network byte order */
562       if (clib_arch_is_little_endian)
563         {
564           u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
565           *msg_idp = msg_id;
566         }
567
568       switch (which)
569         {
570         case DUMP_JSON:
571           vlib_cli_output (vm, "%U", format_vl_api_msg_json, am, msg_id,
572                            tmpbuf + sizeof (uword));
573           break;
574
575         case DUMP:
576           vlib_cli_output (vm, "%U", format_vl_api_msg_text, am, msg_id,
577                            tmpbuf + sizeof (uword));
578           break;
579
580         case INITIALIZERS:
581           if (m)
582             {
583               u8 *s;
584               int j;
585
586               vlib_cli_output (vm, "/*%U*/", format_vl_api_msg_text, am,
587                                msg_id, tmpbuf + sizeof (uword));
588
589               vlib_cli_output (vm, "*/\n");
590
591               s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
592                           m->trace_size);
593
594               for (j = 0; j < m->trace_size; j++)
595                 {
596                   if ((j & 7) == 0)
597                     s = format (s, "\n    ");
598                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
599                 }
600               s = format (s, "\n};\n%c", 0);
601               vlib_cli_output (vm, (char *) s);
602               vec_free (s);
603             }
604           break;
605
606         case REPLAY:
607           if (m && m->handler && m->replay_allowed)
608             {
609               if (!m->is_mp_safe)
610                 vl_msg_api_barrier_sync ();
611               m->handler (tmpbuf + sizeof (uword));
612               if (!m->is_mp_safe)
613                 vl_msg_api_barrier_release ();
614             }
615           else
616             {
617               if (m && m->replay_allowed)
618                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
619                                  msg_id);
620               break;
621             }
622           break;
623         }
624
625       vec_set_len (tmpbuf, 0);
626       msg += size;
627     }
628
629   munmap (hp, file_size);
630   vec_free (tmpbuf);
631   vec_free (msgid_vec);
632   am->replay_in_progress = 0;
633 }
634
635 static int
636 file_exists (u8 *fname)
637 {
638   FILE *fp = 0;
639   fp = fopen ((char *) fname, "r");
640   if (fp)
641     {
642       fclose (fp);
643       return 1;
644     }
645   return 0;
646 }
647
648 typedef struct
649 {
650   vlib_main_t *vm;
651   u8 is_json;
652 } vl_msg_print_args;
653
654 static int
655 vl_msg_print_trace (u8 *msg, void *ctx)
656 {
657   vl_msg_print_args *a = ctx;
658   api_main_t *am = vlibapi_get_main ();
659   u16 msg_id = ntohs (*((u16 *) msg));
660   vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
661   u8 is_json = a->is_json;
662   u8 *tmpbuf = 0;
663
664   if (!m)
665     {
666       vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
667       return 0;
668     }
669
670   if (clib_arch_is_little_endian && (m->endian_handler != NULL))
671     {
672       u32 msg_length = vec_len (msg);
673       vec_validate (tmpbuf, msg_length - 1);
674       clib_memcpy_fast (tmpbuf, msg, msg_length);
675       msg = tmpbuf;
676
677       m->endian_handler (tmpbuf, 0 /* from network */);
678     }
679
680   vlib_cli_output (a->vm, "%U\n",
681                    is_json ? format_vl_api_msg_json : format_vl_api_msg_text,
682                    am, msg_id, msg);
683
684   vec_free (tmpbuf);
685   return 0;
686 }
687
688 static int
689 vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
690 {
691   api_main_t *am = vlibapi_get_main ();
692   vl_api_trace_t *tp;
693
694   switch (which)
695     {
696     case VL_API_TRACE_TX:
697       tp = am->tx_trace;
698       break;
699     case VL_API_TRACE_RX:
700       tp = am->rx_trace;
701       break;
702     default:
703       return -1;
704     }
705
706   if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
707     return -1;
708
709   vl_msg_print_args args;
710   clib_memset (&args, 0, sizeof (args));
711   args.is_json = is_json;
712   args.vm = vm;
713   vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
714
715   return 0;
716 }
717
718 static char *
719 vl_msg_read_file (FILE *f)
720 {
721   const size_t bufsize = 1024;
722   char *buf[bufsize], *v = 0;
723   size_t n;
724
725   while ((n = fread (buf, 1, bufsize, f)))
726     vec_add (v, buf, n);
727
728   /* most callers expect a NULL-terminated C-string */
729   if (v)
730     vec_add1 (v, 0);
731
732   return v;
733 }
734
735 static u16
736 vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
737 {
738   uword *p;
739   p = hash_get_mem (am->msg_index_by_name_and_crc, name);
740   if (!p)
741     return (u16) ~0;
742
743   return p[0];
744 }
745
746 static u16
747 vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
748 {
749   uword *p;
750
751   if (!am->msg_id_by_name)
752     {
753       vlib_cli_output (vm, "message id table not yet initialized!\n");
754       return (u16) ~0;
755     }
756
757   p = hash_get_mem (am->msg_id_by_name, name);
758   if (!p)
759     return (u16) ~0;
760
761   return p[0];
762 }
763
764 static int
765 vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
766 {
767   api_main_t *am = vlibapi_get_main ();
768   u16 msg_id;
769   int len = 0, rv = -1;
770   vl_api_msg_data_t *m;
771   u8 *msg = 0;
772
773   cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
774   if (!msg_id_obj)
775     {
776       vlib_cli_output (vm, "Missing '_msgname' element!\n");
777       return rv;
778     }
779   char *name = cJSON_GetStringValue (msg_id_obj);
780
781   cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
782   if (!crc_obj)
783     {
784       vlib_cli_output (vm, "Missing '_crc' element!\n");
785       return rv;
786     }
787   char *crc = cJSON_GetStringValue (crc_obj);
788   u8 proc_warning = 0;
789
790   u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
791   msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
792   m = vl_api_get_msg_data (am, msg_id);
793   if (msg_id == (u16) ~0)
794     {
795       msg_id = vl_msg_find_id_by_name (vm, am, name);
796       if (msg_id == (u16) ~0)
797         {
798           vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
799           vec_free (name_crc);
800           return rv;
801         }
802       proc_warning = 1;
803     }
804   vec_free (name_crc);
805
806   if (m->replay_allowed)
807     {
808       if (proc_warning)
809         vlib_cli_output (vm, "warning: msg %d has different signature\n");
810
811       if (!m->fromjson_handler)
812         {
813           vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
814                            msg_id);
815           return rv;
816         }
817
818       msg = (u8 *) m->fromjson_handler (o, &len);
819       if (!msg)
820         {
821           vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
822                            msg_id);
823           return rv;
824         }
825
826       if (clib_arch_is_little_endian)
827         m->endian_handler (msg, 1 /* to network */);
828
829       if (!m->handler)
830         {
831           vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
832           goto end;
833         }
834
835       if (m->handler)
836         {
837           if (!m->is_mp_safe)
838             vl_msg_api_barrier_sync ();
839           m->handler (msg);
840           if (!m->is_mp_safe)
841             vl_msg_api_barrier_release ();
842         }
843     }
844
845   rv = 0;
846 end:
847   if (msg)
848     cJSON_free (msg);
849   return rv;
850 }
851
852 static void
853 vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
854 {
855   api_main_t *am = vlibapi_get_main ();
856   cJSON *o = 0;
857   int rv = 0;
858   FILE *f = fopen ((char *) filename, "r");
859
860   if (!f)
861     {
862       vlib_cli_output (vm, "failed to open %s!\n", filename);
863       return;
864     }
865
866   char *buf = vl_msg_read_file (f);
867   fclose (f);
868
869   o = cJSON_Parse (buf);
870   vec_free (buf);
871   if (!o)
872     {
873       vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
874                        cJSON_GetErrorPtr ());
875       return;
876     }
877
878   if (cJSON_IsArray (o))
879     {
880       am->replay_in_progress = 1;
881       size_t size = cJSON_GetArraySize (o);
882       for (int i = 0; i < size; i++)
883         {
884           rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
885           if (rv < 0)
886             {
887               am->replay_in_progress = 0;
888               break;
889             }
890         }
891     }
892   else
893     {
894       rv = vl_msg_exec_json_command (vm, o);
895     }
896
897   if (rv < 0)
898     vlib_cli_output (vm, "error during replaying API trace");
899
900   cJSON_Delete (o);
901 }
902
903 static void
904 vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
905 {
906   FILE *f = fopen ((char *) filename, "r");
907   char *buf;
908
909   if (!f)
910     {
911       vlib_cli_output (vm, "failed to open %s!\n", filename);
912       return;
913     }
914
915   buf = vl_msg_read_file (f);
916   fclose (f);
917
918   if (!buf)
919     {
920       vlib_cli_output (vm, "no content in %s!\n", filename);
921       return;
922     }
923
924   vlib_cli_output (vm, buf);
925   vec_free (buf);
926 }
927
928 /** api_trace_command_fn - control the binary API trace / replay feature
929
930     Note: this command MUST be marked thread-safe. Replay with
931     multiple worker threads depends in many cases on worker thread
932     graph replica maintenance. If we (implicitly) assert a worker
933     thread barrier at the debug CLI level, all graph replica changes
934     are deferred until the replay operation completes. If an interface
935     is deleted, the wheels fall off.
936  */
937
938 static clib_error_t *
939 api_trace_command_fn (vlib_main_t * vm,
940                       unformat_input_t * input, vlib_cli_command_t * cmd)
941 {
942   unformat_input_t _line_input, *line_input = &_line_input;
943   u32 nitems = 256 << 10;
944   api_main_t *am = vlibapi_get_main ();
945   vl_api_trace_which_t which = VL_API_TRACE_RX;
946   u8 *filename = 0;
947   u8 *chroot_filename = 0;
948   u32 first = 0;
949   u32 last = (u32) ~ 0;
950   FILE *fp;
951   int rv;
952
953   /* Get a line of input. */
954   if (!unformat_user (input, unformat_line_input, line_input))
955     return 0;
956
957   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
958     {
959       if (unformat (line_input, "on") || unformat (line_input, "enable"))
960         {
961           if (unformat (line_input, "nitems %d", &nitems))
962             ;
963           vlib_worker_thread_barrier_sync (vm);
964           vl_msg_api_trace_configure (am, which, nitems);
965           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
966           vlib_worker_thread_barrier_release (vm);
967         }
968       else if (unformat (line_input, "off"))
969         {
970           vlib_worker_thread_barrier_sync (vm);
971           vl_msg_api_trace_onoff (am, which, 0);
972           vlib_worker_thread_barrier_release (vm);
973         }
974       else if (unformat (line_input, "save-json %s", &filename))
975         {
976           if (strstr ((char *) filename, "..") ||
977               index ((char *) filename, '/'))
978             {
979               vlib_cli_output (vm, "illegal characters in filename '%s'",
980                                filename);
981               goto out;
982             }
983
984           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
985
986           vec_free (filename);
987
988           if (file_exists (chroot_filename))
989             {
990               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
991               goto out;
992             }
993
994           fp = fopen ((char *) chroot_filename, "w");
995           if (fp == NULL)
996             {
997               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
998               goto out;
999             }
1000           vlib_worker_thread_barrier_sync (vm);
1001           rv = vl_msg_api_trace_save (am, which, fp, 1);
1002           if (rv == -1)
1003             vlib_cli_output (vm, "API Trace data not present\n");
1004           else if (rv < 0)
1005             vlib_cli_output (vm, "failed to save api trace\n");
1006           else
1007             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1008           vlib_worker_thread_barrier_release (vm);
1009           fclose (fp);
1010         }
1011       else if (unformat (line_input, "save %s", &filename))
1012         {
1013           if (strstr ((char *) filename, "..")
1014               || index ((char *) filename, '/'))
1015             {
1016               vlib_cli_output (vm, "illegal characters in filename '%s'",
1017                                filename);
1018               goto out;
1019             }
1020
1021           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1022
1023           vec_free (filename);
1024
1025           if (file_exists (chroot_filename))
1026             {
1027               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1028               goto out;
1029             }
1030
1031           fp = fopen ((char *) chroot_filename, "w");
1032           if (fp == NULL)
1033             {
1034               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1035               goto out;
1036             }
1037           vlib_worker_thread_barrier_sync (vm);
1038           rv = vl_msg_api_trace_save (am, which, fp, 0);
1039           vlib_worker_thread_barrier_release (vm);
1040           fclose (fp);
1041           if (rv == -1)
1042             vlib_cli_output (vm, "API Trace data not present\n");
1043           else if (rv == -2)
1044             vlib_cli_output (vm, "File for writing is closed\n");
1045           else if (rv == -10)
1046             vlib_cli_output (vm, "Error while writing header to file\n");
1047           else if (rv == -11)
1048             vlib_cli_output (vm, "Error while writing trace to file\n");
1049           else if (rv == -12)
1050             vlib_cli_output (vm,
1051                              "Error while writing end of buffer trace to file\n");
1052           else if (rv == -13)
1053             vlib_cli_output (vm,
1054                              "Error while writing start of buffer trace to file\n");
1055           else if (rv < 0)
1056             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
1057           else
1058             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1059           goto out;
1060         }
1061       else if (unformat (line_input, "tojson %s", &filename))
1062         {
1063           vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1064         }
1065       else if (unformat (line_input, "dump-file-json %s", &filename))
1066         {
1067           vl_msg_dump_file_json (vm, filename);
1068         }
1069       else if (unformat (line_input, "dump-file %s", &filename))
1070         {
1071           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1072         }
1073       else if (unformat (line_input, "dump-json"))
1074         {
1075           vl_msg_api_dump_trace (vm, which, 1);
1076         }
1077       else if (unformat (line_input, "dump"))
1078         {
1079           vl_msg_api_dump_trace (vm, which, 0);
1080         }
1081       else if (unformat (line_input, "replay-json %s", &filename))
1082         {
1083           vl_msg_replay_json (vm, filename);
1084         }
1085       else if (unformat (line_input, "replay %s", &filename))
1086         {
1087           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1088         }
1089       else if (unformat (line_input, "initializers %s", &filename))
1090         {
1091           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1092         }
1093       else if (unformat (line_input, "tx"))
1094         {
1095           which = VL_API_TRACE_TX;
1096         }
1097       else if (unformat (line_input, "first %d", &first))
1098         {
1099           ;
1100         }
1101       else if (unformat (line_input, "last %d", &last))
1102         {
1103           ;
1104         }
1105       else if (unformat (line_input, "status"))
1106         {
1107           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1108                            am, which);
1109         }
1110       else if (unformat (line_input, "free"))
1111         {
1112           vlib_worker_thread_barrier_sync (vm);
1113           vl_msg_api_trace_onoff (am, which, 0);
1114           vl_msg_api_trace_free (am, which);
1115           vlib_worker_thread_barrier_release (vm);
1116         }
1117       else if (unformat (line_input, "post-mortem-on"))
1118         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1119       else if (unformat (line_input, "post-mortem-off"))
1120         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1121       else
1122         return clib_error_return (0, "unknown input `%U'",
1123                                   format_unformat_error, input);
1124     }
1125 out:
1126   vec_free (filename);
1127   vec_free (chroot_filename);
1128   unformat_free (line_input);
1129   return 0;
1130 }
1131
1132 /*?
1133  * Display, replay, or save a binary API trace
1134 ?*/
1135
1136 VLIB_CLI_COMMAND (api_trace_command, static) = {
1137   .path = "api trace",
1138   .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1139                 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1140                 "json|replay <file>|replay-json <file>][nitems <n>]"
1141                 "[initializers <file>]",
1142   .function = api_trace_command_fn,
1143   .is_mp_safe = 1,
1144 };
1145
1146 static clib_error_t *
1147 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1148 {
1149   u32 nitems = 256 << 10;
1150   vl_api_trace_which_t which = VL_API_TRACE_RX;
1151   api_main_t *am = vlibapi_get_main ();
1152
1153   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1154     {
1155       if (unformat (input, "on") || unformat (input, "enable"))
1156         {
1157           if (unformat (input, "nitems %d", &nitems))
1158             ;
1159           vl_msg_api_trace_configure (am, which, nitems);
1160           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1161           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1162         }
1163       else if (unformat (input, "save-api-table %s",
1164                          &am->save_msg_table_filename))
1165         ;
1166       else
1167         return clib_error_return (0, "unknown input `%U'",
1168                                   format_unformat_error, input);
1169     }
1170   return 0;
1171 }
1172
1173 /*?
1174  * This module has three configuration parameters:
1175  * "on" or "enable" - enables binary api tracing
1176  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1177  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1178 ?*/
1179 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1180
1181 static clib_error_t *
1182 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1183 {
1184   api_main_t *am = vlibapi_get_main ();
1185   u32 nitems;
1186
1187   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1188     {
1189       if (unformat (input, "length %d", &nitems) ||
1190           (unformat (input, "len %d", &nitems)))
1191         {
1192           if (nitems >= 1024)
1193             am->vlib_input_queue_length = nitems;
1194           else
1195             clib_warning ("vlib input queue length %d too small, ignored",
1196                           nitems);
1197         }
1198       else
1199         return clib_error_return (0, "unknown input `%U'",
1200                                   format_unformat_error, input);
1201     }
1202   return 0;
1203 }
1204
1205 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1206
1207 static u8 *
1208 extract_name (u8 * s)
1209 {
1210   u8 *rv;
1211
1212   rv = vec_dup (s);
1213
1214   while (vec_len (rv) && rv[vec_len (rv)] != '_')
1215     vec_dec_len (rv, 1);
1216
1217   rv[vec_len (rv)] = 0;
1218
1219   return rv;
1220 }
1221
1222 static u8 *
1223 extract_crc (u8 * s)
1224 {
1225   int i;
1226   u8 *rv;
1227
1228   rv = vec_dup (s);
1229
1230   for (i = vec_len (rv) - 1; i >= 0; i--)
1231     {
1232       if (rv[i] == '_')
1233         {
1234           vec_delete (rv, i + 1, 0);
1235           break;
1236         }
1237     }
1238   return rv;
1239 }
1240
1241 typedef struct
1242 {
1243   u8 *name_and_crc;
1244   u8 *name;
1245   u8 *crc;
1246   u32 msg_index;
1247   int which;
1248 } msg_table_unserialize_t;
1249
1250 static int
1251 table_id_cmp (void *a1, void *a2)
1252 {
1253   msg_table_unserialize_t *n1 = a1;
1254   msg_table_unserialize_t *n2 = a2;
1255
1256   return (n1->msg_index - n2->msg_index);
1257 }
1258
1259 static int
1260 table_name_and_crc_cmp (void *a1, void *a2)
1261 {
1262   msg_table_unserialize_t *n1 = a1;
1263   msg_table_unserialize_t *n2 = a2;
1264
1265   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1266 }
1267
1268 static clib_error_t *
1269 dump_api_table_file_command_fn (vlib_main_t * vm,
1270                                 unformat_input_t * input,
1271                                 vlib_cli_command_t * cmd)
1272 {
1273   u8 *filename = 0;
1274   api_main_t *am = vlibapi_get_main ();
1275   serialize_main_t _sm, *sm = &_sm;
1276   clib_error_t *error;
1277   u32 nmsgs;
1278   u32 msg_index;
1279   u8 *name_and_crc;
1280   int compare_current = 0;
1281   int numeric_sort = 0;
1282   msg_table_unserialize_t *table = 0, *item;
1283   u32 i;
1284   u32 ndifferences = 0;
1285
1286   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1287     {
1288       if (unformat (input, "file %s", &filename))
1289         ;
1290       else if (unformat (input, "compare-current")
1291                || unformat (input, "compare"))
1292         compare_current = 1;
1293       else if (unformat (input, "numeric"))
1294         numeric_sort = 1;
1295       else
1296         return clib_error_return (0, "unknown input `%U'",
1297                                   format_unformat_error, input);
1298     }
1299
1300   if (numeric_sort && compare_current)
1301     return clib_error_return
1302       (0, "Comparison and numeric sorting are incompatible");
1303
1304   if (filename == 0)
1305     return clib_error_return (0, "File not specified");
1306
1307   /* Load the serialized message table from the table dump */
1308
1309   error = unserialize_open_clib_file (sm, (char *) filename);
1310
1311   if (error)
1312     return error;
1313
1314   unserialize_integer (sm, &nmsgs, sizeof (u32));
1315
1316   for (i = 0; i < nmsgs; i++)
1317     {
1318       msg_index = unserialize_likely_small_unsigned_integer (sm);
1319       unserialize_cstring (sm, (char **) &name_and_crc);
1320       vec_add2 (table, item, 1);
1321       item->msg_index = msg_index;
1322       item->name_and_crc = name_and_crc;
1323       item->name = extract_name (name_and_crc);
1324       item->crc = extract_crc (name_and_crc);
1325       item->which = 0;          /* file */
1326     }
1327   unserialize_close (sm);
1328
1329   /* Compare with the current image? */
1330   if (compare_current)
1331     {
1332       /* Append the current message table */
1333       u8 *tblv = vl_api_serialize_message_table (am, 0);
1334
1335       serialize_open_vector (sm, tblv);
1336       unserialize_integer (sm, &nmsgs, sizeof (u32));
1337
1338       for (i = 0; i < nmsgs; i++)
1339         {
1340           msg_index = unserialize_likely_small_unsigned_integer (sm);
1341           unserialize_cstring (sm, (char **) &name_and_crc);
1342
1343           vec_add2 (table, item, 1);
1344           item->msg_index = msg_index;
1345           item->name_and_crc = name_and_crc;
1346           item->name = extract_name (name_and_crc);
1347           item->crc = extract_crc (name_and_crc);
1348           item->which = 1;      /* current_image */
1349         }
1350       vec_free (tblv);
1351     }
1352
1353   /* Sort the table. */
1354   if (numeric_sort)
1355     vec_sort_with_function (table, table_id_cmp);
1356   else
1357     vec_sort_with_function (table, table_name_and_crc_cmp);
1358
1359   if (compare_current)
1360     {
1361       u8 *dashes = 0;
1362       ndifferences = 0;
1363
1364       /*
1365        * In this case, the recovered table will have two entries per
1366        * API message. So, if entries i and i+1 match, the message definitions
1367        * are identical. Otherwise, the crc is different, or a message is
1368        * present in only one of the tables.
1369        */
1370       vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1371       vec_validate_init_empty (dashes, 60, '-');
1372       vec_terminate_c_string (dashes);
1373       vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1374       vec_free (dashes);
1375       for (i = 0; i < vec_len (table);)
1376         {
1377           /* Last message lonely? */
1378           if (i == vec_len (table) - 1)
1379             {
1380               ndifferences++;
1381               goto last_unique;
1382             }
1383
1384           /* Identical pair? */
1385           if (!strncmp
1386               ((char *) table[i].name_and_crc,
1387                (char *) table[i + 1].name_and_crc,
1388                vec_len (table[i].name_and_crc)))
1389             {
1390               i += 2;
1391               continue;
1392             }
1393
1394           ndifferences++;
1395
1396           /* Only in one of two tables? */
1397           if (i + 1 == vec_len (table)
1398               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1399             {
1400             last_unique:
1401               vlib_cli_output (vm, "%-60s | only in %s",
1402                                table[i].name, table[i].which ?
1403                                "image" : "file");
1404               i++;
1405               continue;
1406             }
1407           /* In both tables, but with different signatures */
1408           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1409           i += 2;
1410         }
1411       if (ndifferences == 0)
1412         vlib_cli_output (vm, "No api message signature differences found.");
1413       else
1414         vlib_cli_output (vm, "\nFound %u api message signature differences",
1415                          ndifferences);
1416       goto cleanup;
1417     }
1418
1419   /* Dump the table, sorted as shown above */
1420   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1421
1422   for (i = 0; i < vec_len (table); i++)
1423     {
1424       item = table + i;
1425       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1426                        item->msg_index, item->crc);
1427     }
1428
1429 cleanup:
1430   for (i = 0; i < vec_len (table); i++)
1431     {
1432       vec_free (table[i].name_and_crc);
1433       vec_free (table[i].name);
1434       vec_free (table[i].crc);
1435     }
1436
1437   vec_free (table);
1438
1439   return 0;
1440 }
1441
1442 /*?
1443  * Displays a serialized API message decode table, sorted by message name
1444  *
1445  * @cliexpar
1446  * @cliexstart{show api dump file <filename>}
1447  *                                                Message name    MsgID        CRC
1448  * accept_session                                                    407   8e2a127e
1449  * accept_session_reply                                              408   67d8c22a
1450  * add_node_next                                                     549   e4202993
1451  * add_node_next_reply                                               550   e89d6eed
1452  * etc.
1453  * @cliexend
1454 ?*/
1455
1456 /*?
1457  * Compares a serialized API message decode table with the current image
1458  *
1459  * @cliexpar
1460  * @cliexstart{show api dump file <filename> compare}
1461  * ip_add_del_route                                             definition changed
1462  * ip_table_add_del                                             definition changed
1463  * l2_macs_event                                                only in image
1464  * vnet_ip4_fib_counters                                        only in file
1465  * vnet_ip4_nbr_counters                                        only in file
1466  * @cliexend
1467 ?*/
1468
1469 /*?
1470  * Display a serialized API message decode table, compare a saved
1471  * decode table with the current image, to establish API differences.
1472  *
1473 ?*/
1474 VLIB_CLI_COMMAND (dump_api_table_file, static) =
1475 {
1476   .path = "show api dump",
1477   .short_help = "show api dump file <filename> [numeric | compare-current]",
1478   .function = dump_api_table_file_command_fn,
1479 };
1480
1481 /*
1482  * fd.io coding-style-patch-verification: ON
1483  *
1484  * Local Variables:
1485  * eval: (c-set-style "gnu")
1486  * End:
1487  */