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