blob: 0600e621a4ebf65dc522ea216206b4d12ae704ec [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 *------------------------------------------------------------------
3 * api_shared.c - API message handling, common code for both clients
4 * and the vlib process itself.
5 *
6 *
7 * Copyright (c) 2009 Cisco and/or its affiliates.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at:
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *------------------------------------------------------------------
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/mman.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <vppinfra/format.h>
31#include <vppinfra/byte_order.h>
32#include <vppinfra/error.h>
33#include <vlib/vlib.h>
34#include <vlib/unix/unix.h>
35#include <vlibapi/api.h>
36#include <vppinfra/elog.h>
37
38api_main_t api_main;
39
40void vl_msg_api_barrier_sync(void) __attribute__((weak));
41void vl_msg_api_barrier_sync(void) { }
42
43void vl_msg_api_barrier_release(void) __attribute__((weak));
44void vl_msg_api_barrier_release(void) { }
45
46void vl_msg_api_increment_missing_client_counter(void)
47{
48 api_main_t * am = &api_main;
49 am->missing_clients++;
50}
51
52typedef enum {
53 DUMP,
54 CUSTOM_DUMP,
55 REPLAY,
56 INITIALIZERS,
57} vl_api_replay_t;
58
59int vl_msg_api_rx_trace_enabled(api_main_t *am)
60{
61 return (am->rx_trace && am->rx_trace->enabled);
62}
63
64int vl_msg_api_tx_trace_enabled(api_main_t *am)
65{
66 return (am->tx_trace && am->tx_trace->enabled);
67}
68
69/*
70 * vl_msg_api_trace
71 */
72void vl_msg_api_trace(api_main_t *am, vl_api_trace_t *tp, void *msg)
73{
74 u8 **this_trace;
75 u8 **old_trace;
76 u8 *msg_copy;
77 trace_cfg_t *cfgp;
78 u16 msg_id = ntohs(*((u16 *)msg));
79
80 cfgp = am->api_trace_cfg + msg_id;
81
82 if (!cfgp || !cfgp->trace_enable)
83 return;
84
85 msg_copy = 0;
86
87 if (tp->nitems == 0) {
88 clib_warning ("tp->nitems is 0");
89 return;
90 }
91
92 if (vec_len(tp->traces) < tp->nitems) {
93 vec_add1(tp->traces, 0);
94 this_trace = tp->traces + vec_len(tp->traces) - 1;
95 } else {
96 tp->wrapped = 1;
97 old_trace = tp->traces + tp->curindex++;
98 if (tp->curindex == tp->nitems)
99 tp->curindex = 0;
100 vec_free(*old_trace);
101 this_trace = old_trace;
102 }
103
104 vec_validate(msg_copy, cfgp->size - 1);
Damjan Marionf1213b82016-03-13 02:22:06 +0100105 clib_memcpy(msg_copy, msg, cfgp->size);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700106 *this_trace = msg_copy;
107}
108
109int vl_msg_api_trace_onoff(api_main_t *am, vl_api_trace_which_t which,
110 int onoff)
111{
112 vl_api_trace_t *tp;
113 int rv;
114
115 switch(which)
116 {
117 case VL_API_TRACE_TX:
118 tp = am->tx_trace;
119 if (tp == 0) {
120 vl_msg_api_trace_configure (am, which, 1024);
121 tp = am->tx_trace;
122 }
123 break;
124
125 case VL_API_TRACE_RX:
126 tp = am->rx_trace;
127 if (tp == 0) {
128 vl_msg_api_trace_configure (am, which, 1024);
129 tp = am->rx_trace;
130 }
131 break;
132
133 default:
134 /* duh? */
135 return -1;
136 }
137
138 /* Configured? */
139 if (tp == 0 || tp->nitems == 0)
140 return -1;
141
142 rv = tp->enabled;
143 tp->enabled = onoff;
144
145 return rv;
146}
147
148int vl_msg_api_trace_free(api_main_t *am, vl_api_trace_which_t which)
149{
150 vl_api_trace_t *tp;
151 int i;
152
153 switch(which)
154 {
155 case VL_API_TRACE_TX:
156 tp = am->tx_trace;
157 break;
158
159 case VL_API_TRACE_RX:
160 tp = am->rx_trace;
161 break;
162
163 default:
164 /* duh? */
165 return -1;
166 }
167
168 /* Configured? */
169 if (!tp || tp->nitems == 0)
170 return -1;
171
172 tp->curindex = 0;
173 tp->wrapped = 0;
174
175 for (i = 0; i < vec_len(tp->traces); i++) {
176 vec_free(tp->traces[i]);
177 }
178 vec_free(tp->traces);
179
180 return 0;
181}
182
183int vl_msg_api_trace_save(api_main_t *am,
184 vl_api_trace_which_t which, FILE *fp)
185{
186 vl_api_trace_t *tp;
187 vl_api_trace_file_header_t fh;
188 int i;
189 u8 *msg;
190
191 switch(which)
192 {
193 case VL_API_TRACE_TX:
194 tp = am->tx_trace;
195 break;
196
197 case VL_API_TRACE_RX:
198 tp = am->rx_trace;
199 break;
200
201 default:
202 /* duh? */
203 return -1;
204 }
205
206 /* Configured, data present? */
207 if (tp == 0 || tp->nitems == 0 || vec_len(tp->traces) == 0)
208 return -1;
209
210 /* "Dare to be stupid" check */
211 if (fp == 0) {
212 return -2;
213 }
214
215 /* Write the file header */
216 fh.nitems = vec_len(tp->traces);
217 fh.endian = tp->endian;
218 fh.wrapped = tp->wrapped;
219
220 if (fwrite(&fh, sizeof(fh), 1, fp) != 1) {
221 return (-10);
222 }
223
224 /* No-wrap case */
225 if (tp->wrapped == 0) {
226 /*
227 * Note: vec_len return 0 when fed a NULL pointer.
228 * Unfortunately, the static analysis tool doesn't
229 * figure it out, hence the suppressed warnings.
230 * What a great use of my time.
231 */
232 for (i = 0; i < vec_len(tp->traces); i++) {
233 /*sa_ignore NO_NULL_CHK*/
234 msg = tp->traces[i];
235 /*
236 * This retarded check required to pass
237 * [sic] SA-checking.
238 */
239 if (!msg)
240 continue;
241 if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
242 return (-11);
243 }
244 }
245 } else {
246 /* Wrap case: write oldest -> end of buffer */
247 for (i = tp->curindex; i < vec_len(tp->traces); i++) {
248 msg = tp->traces[i];
249 /*
250 * This retarded check required to pass
251 * [sic] SA-checking
252 */
253 if (!msg)
254 continue;
255
256 if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
257 return (-12);
258 }
259 }
260 /* write beginning of buffer -> oldest-1 */
261 for (i = 0; i < tp->curindex; i++) {
262 /*sa_ignore NO_NULL_CHK*/
263 msg = tp->traces[i];
264 /*
265 * This retarded check required to pass
266 * [sic] SA-checking
267 */
268 if (!msg)
269 continue;
270
271 if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
272 return (-13);
273 }
274 }
275 }
276 return 0;
277}
278
279int vl_msg_api_trace_configure(api_main_t *am, vl_api_trace_which_t which,
280 u32 nitems)
281{
282 vl_api_trace_t *tp;
283 int was_on = 0;
284
285 switch(which)
286 {
287 case VL_API_TRACE_TX:
288 tp = am->tx_trace;
289 if (tp == 0) {
290 vec_validate(am->tx_trace, 0);
291 tp = am->tx_trace;
292 }
293 break;
294
295 case VL_API_TRACE_RX:
296 tp = am->rx_trace;
297 if (tp == 0) {
298 vec_validate(am->rx_trace, 0);
299 tp = am->rx_trace;
300 }
301
302 break;
303
304 default:
305 return -1;
306
307 }
308
309 if (tp->enabled) {
310 was_on = vl_msg_api_trace_onoff(am, which, 0);
311 }
312 if (tp->traces) {
313 vl_msg_api_trace_free(am, which);
314 }
315
316 memset(tp, 0, sizeof(*tp));
317
318 if (clib_arch_is_big_endian) {
319 tp->endian = VL_API_BIG_ENDIAN;
320 } else {
321 tp->endian = VL_API_LITTLE_ENDIAN;
322 }
323
324 tp->nitems = nitems;
325 if (was_on) {
326 (void)vl_msg_api_trace_onoff(am, which, was_on);
327 }
328 return 0;
329}
330
331always_inline void msg_handler_internal (api_main_t *am,
332 void *the_msg,
333 int trace_it,
334 int do_it,
335 int free_it)
336{
337 u16 id = ntohs(*((u16 *)the_msg));
338 u8 *(*print_fp)(void *, void *);
339
340 if (id < vec_len(am->msg_handlers) &&
341 am->msg_handlers[id]) {
342 if (trace_it)
343 vl_msg_api_trace(am, am->rx_trace, the_msg);
344
345 if (am->msg_print_flag) {
346 fformat (stdout, "[%d]: %s\n", id,
347 am->msg_names[id]);
348 print_fp = (void *)am->msg_print_handlers[id];
349 if (print_fp == 0) {
350 fformat(stdout, " [no registered print fn]\n");
351 } else {
352 (*print_fp)(the_msg, stdout);
353 }
354 }
355
356 if (do_it) {
357 if (!am->is_mp_safe[id])
358 vl_msg_api_barrier_sync();
359 (*am->msg_handlers[id])(the_msg);
360 if (!am->is_mp_safe[id])
361 vl_msg_api_barrier_release();
362 }
363 } else {
364 clib_warning("no handler for msg id %d", id);
365 }
366
367 if (free_it)
368 vl_msg_api_free(the_msg);
369}
370
371/* set to 1 if you want before/after message handler event logging */
372#define ELOG_API_MESSAGE_HANDLERS 0
373
374#if ELOG_API_MESSAGE_HANDLERS > 0
375static u32 elog_id_for_msg_name (vlib_main_t * vm, char *msg_name)
376{
377 uword * p, r;
378 static uword * h;
379 u8 *name_copy;
380
381 if (! h)
382 h = hash_create_string (0, sizeof (uword));
383
384 p = hash_get_mem (h, msg_name);
385 if (p)
386 return p[0];
387 r = elog_string (&vm->elog_main, "%s", msg_name);
388
389 name_copy = format (0, "%s%c", msg_name, 0);
390
391 hash_set_mem (h, name_copy, r);
392
393 return r;
394}
395#endif
396
397/* This is only to be called from a vlib/vnet app */
398void vl_msg_api_handler_with_vm_node (api_main_t *am,
399 void *the_msg, vlib_main_t *vm,
400 vlib_node_runtime_t *node)
401{
402 u16 id = ntohs(*((u16 *)the_msg));
403 u8 *(*handler)(void *, void *, void *);
404
405#if ELOG_API_MESSAGE_HANDLERS > 0
406 {
407 ELOG_TYPE_DECLARE (e) = {
408 .format = "api-msg: %s",
409 .format_args = "T4",
410 };
411 struct { u32 c; } * ed;
412 ed = ELOG_DATA (&vm->elog_main, e);
413 if (id < vec_len (am->msg_names))
414 ed->c = elog_id_for_msg_name (vm, am->msg_names[id]);
415 else
416 ed->c = elog_id_for_msg_name (vm, "BOGUS");
417 }
418#endif
419
420 if (id < vec_len(am->msg_handlers) &&
421 am->msg_handlers[id]) {
422 handler = (void *)am->msg_handlers[id];
423
424 if (am->rx_trace && am->rx_trace->enabled)
425 vl_msg_api_trace(am, am->rx_trace, the_msg);
426
427 if (!am->is_mp_safe[id])
428 vl_msg_api_barrier_sync();
429 (*handler)(the_msg, vm, node);
430 if (!am->is_mp_safe[id])
431 vl_msg_api_barrier_release();
432 } else {
433 clib_warning("no hander for msg id %d", id);
434 }
435 /*
436 * Special-case, so we can e.g. bounce messages off the vnet
437 * main thread without copying them...
438 */
439 if (!(am->message_bounce[id]))
440 vl_msg_api_free(the_msg);
441
442#if ELOG_API_MESSAGE_HANDLERS > 0
443 {
444 ELOG_TYPE_DECLARE (e) = {
445 .format = "api-msg-done: %s",
446 .format_args = "T4",
447 };
448 struct { u32 c; } * ed;
449 ed = ELOG_DATA (&vm->elog_main, e);
450 if (id < vec_len (am->msg_names))
451 ed->c = elog_id_for_msg_name (vm, am->msg_names[id]);
452 else
453 ed->c = elog_id_for_msg_name (vm, "BOGUS");
454 }
455#endif
456}
457
458void vl_msg_api_handler (void *the_msg)
459{
460 api_main_t *am = &api_main;
461
462 msg_handler_internal (am, the_msg,
463 (am->rx_trace
464 && am->rx_trace->enabled) /* trace_it */,
465 1 /* do_it */, 1 /* free_it */);
466}
467
468void vl_msg_api_handler_no_free (void *the_msg)
469{
470 api_main_t *am = &api_main;
471 msg_handler_internal (am, the_msg,
472 (am->rx_trace
473 && am->rx_trace->enabled) /* trace_it */,
474 1 /* do_it */, 0 /* free_it */);
475}
476
477void vl_msg_api_handler_no_trace_no_free (void *the_msg)
478{
479 api_main_t *am = &api_main;
480 msg_handler_internal (am, the_msg, 0/* trace_it */, 1 /* do_it */,
481 0 /* free_it */);
482}
483
484/*
485 * Add a trace record to the API message trace buffer, if
486 * API message tracing is enabled. Handy for adding sufficient
487 * data to the trace to reproduce autonomous state, as opposed to
488 * state downloaded via control-plane API messages. Example: the NAT
489 * application creates database entries based on packet traffic, not
490 * control-plane messages.
491 *
492 */
493void vl_msg_api_trace_only (void *the_msg)
494{
495 api_main_t *am = &api_main;
496
497 msg_handler_internal (am, the_msg,
498 (am->rx_trace
499 && am->rx_trace->enabled) /* trace_it */,
500 0 /* do_it */, 0 /* free_it */);
501}
502
503void vl_msg_api_cleanup_handler (void *the_msg)
504{
505 api_main_t *am = &api_main;
506 u16 id = ntohs(*((u16 *)the_msg));
507
508 if (PREDICT_FALSE(id >= vec_len(am->msg_cleanup_handlers))) {
509 clib_warning ("_vl_msg_id too large: %d\n", id);
510 return;
511 }
512 if (am->msg_cleanup_handlers[id])
513 (*am->msg_cleanup_handlers[id])(the_msg);
514
515 vl_msg_api_free(the_msg);
516}
517
518/*
519 * vl_msg_api_replay_handler
520 */
521void vl_msg_api_replay_handler(void *the_msg)
522{
523 api_main_t *am = &api_main;
524
525 u16 id = ntohs(*((u16 *)the_msg));
526
527 if (PREDICT_FALSE(id >= vec_len(am->msg_handlers))) {
528 clib_warning ("_vl_msg_id too large: %d\n", id);
529 return;
530 }
531 /* do NOT trace the message... */
532 if (am->msg_handlers[id])
533 (*am->msg_handlers[id])(the_msg);
534 /* do NOT free the message buffer... */
535}
536/*
537 * vl_msg_api_socket_handler
538 */
539void vl_msg_api_socket_handler(void *the_msg)
540{
541 api_main_t *am = &api_main;
542
543 msg_handler_internal (am, the_msg,
544 (am->rx_trace
545 && am->rx_trace->enabled) /* trace_it */,
546 1 /* do_it */, 0 /* free_it */);
547}
548
549#define foreach_msg_api_vector \
550_(msg_names) \
551_(msg_handlers) \
552_(msg_cleanup_handlers) \
553_(msg_endian_handlers) \
554_(msg_print_handlers) \
555_(api_trace_cfg) \
556_(message_bounce) \
557_(is_mp_safe)
558
559void vl_msg_api_config (vl_msg_api_msg_config_t *c)
560{
561 api_main_t *am = &api_main;
562
563 ASSERT(c->id > 0);
564
565#define _(a) vec_validate (am->a, c->id);
566 foreach_msg_api_vector;
567#undef _
568
569 am->msg_names[c->id] = c->name;
570 am->msg_handlers[c->id] = c->handler;
571 am->msg_cleanup_handlers[c->id] = c->cleanup;
572 am->msg_endian_handlers[c->id] = c->endian;
573 am->msg_print_handlers[c->id] = c->print;
574 am->message_bounce[c->id] = c->message_bounce;
575 am->is_mp_safe[c->id] = c->is_mp_safe;
576
577 am->api_trace_cfg[c->id].size = c->size;
578 am->api_trace_cfg[c->id].trace_enable = c->traced;
579 am->api_trace_cfg[c->id].replay_enable = c->replay;
580}
581
582/*
583 * vl_msg_api_set_handlers
584 * preserve the old API for a while
585 */
586void vl_msg_api_set_handlers(int id, char *name, void *handler, void *cleanup,
587 void *endian, void *print, int size, int traced)
588{
589 vl_msg_api_msg_config_t cfg;
590 vl_msg_api_msg_config_t *c = &cfg;
591
592 c->id = id;
593 c->name = name;
594 c->handler = handler;
595 c->cleanup = cleanup;
596 c->endian = endian;
597 c->print = print;
598 c->size = size;
599 c->traced = traced;
600 c->replay = 1;
601 c->message_bounce = 0;
602 c->is_mp_safe = 0;
603 vl_msg_api_config (c);
604}
605
606void vl_msg_api_set_cleanup_handler(int msg_id, void *fp)
607{
608 api_main_t *am = &api_main;
609 ASSERT(msg_id > 0);
610
611 vec_validate(am->msg_cleanup_handlers, msg_id);
612 am->msg_cleanup_handlers[msg_id] = fp;
613}
614
615void vl_msg_api_queue_handler(unix_shared_memory_queue_t *q)
616{
617 uword msg;
618
619 while (!unix_shared_memory_queue_sub(q, (u8 *)&msg, 0))
620 vl_msg_api_handler((void *)msg);
621}
622
623vl_api_trace_t *vl_msg_api_trace_get(api_main_t *am, vl_api_trace_which_t which)
624{
625 switch(which)
626 {
627 case VL_API_TRACE_RX:
628 return am->rx_trace;
629 case VL_API_TRACE_TX:
630 return am->tx_trace;
631 default:
632 return 0;
633 }
634}
635
636void vl_noop_handler (void *mp) { }
637
638clib_error_t *
639vl_api_init (vlib_main_t *vm)
640{
641 static u8 once;
642 api_main_t *am = &api_main;
643
644 if (once)
645 return 0;
646
647 once = 1;
648
649 am->region_name = "/unset";
Dave Barach16c75df2016-05-31 14:05:46 -0400650 /*
651 * Eventually passed to fchown, -1 => "current user"
652 * instead of 0 => "root". A very fine disctinction at best.
653 */
654 if (am->api_uid == 0)
655 am->api_uid = -1;
656 if (am->api_gid == 0)
657 am->api_gid = -1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700658
659 return (0);
660}
661
662void vl_msg_api_custom_dump_configure (api_main_t *am) __attribute__((weak));
663void vl_msg_api_custom_dump_configure (api_main_t *am) { }
664
665VLIB_INIT_FUNCTION (vl_api_init);
666
667static void vl_msg_api_process_file (vlib_main_t *vm, u8 *filename,
668 u32 first_index, u32 last_index,
669 vl_api_replay_t which)
670{
671 vl_api_trace_file_header_t * hp;
672 int i, fd;
673 struct stat statb;
674 size_t file_size;
675 u8 *msg;
676 u8 endian_swap_needed = 0;
677 api_main_t * am = &api_main;
678 static u8 *tmpbuf;
679 u32 nitems;
680 void **saved_print_handlers = 0;
681
682 fd = open ((char *) filename, O_RDONLY);
683
684 if (fd < 0) {
685 vlib_cli_output (vm, "Couldn't open %s\n", filename);
686 return;
687 }
688
689 if (fstat(fd, &statb) < 0) {
690 vlib_cli_output (vm, "Couldn't stat %s\n", filename);
691 return;
692 }
693
694 if (! (statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp))) {
695 vlib_cli_output (vm, "File not plausible: %s\n", filename);
696 return;
697 }
698
699 file_size = statb.st_size;
700 file_size = (file_size + 4095) & ~(4096);
701
702 hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
703
704 if (hp == (vl_api_trace_file_header_t *)MAP_FAILED) {
705 vlib_cli_output (vm, "mmap failed: %s\n", filename);
706 close(fd);
707 return;
708 }
709 close(fd);
710
711 if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
712 || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
713 endian_swap_needed = 1;
714
715 if (endian_swap_needed)
716 nitems = ntohl(hp->nitems);
717 else
718 nitems = hp->nitems;
719
720 if (last_index == (u32) ~0) {
721 last_index = nitems - 1;
722 }
723
724 if (first_index >= nitems || last_index >= nitems) {
725 vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
726 first_index, last_index, nitems-1);
727 return;
728 }
729 if (hp->wrapped)
730 vlib_cli_output (vm,
731 "Note: wrapped/incomplete trace, results may vary\n");
732
733 if (which == CUSTOM_DUMP) {
734 saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
735 vl_msg_api_custom_dump_configure (am);
736 }
737
738
739 msg = (u8 *)(hp+1);
740
741 for (i = 0; i < first_index; i++) {
742 trace_cfg_t *cfgp;
743 int size;
744 u16 msg_id;
745
746 if (clib_arch_is_little_endian)
747 msg_id = ntohs(*((u16 *)msg));
748 else
749 msg_id = *((u16 *)msg);
750
751 cfgp = am->api_trace_cfg + msg_id;
752 if (!cfgp) {
753 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
754 return;
755 }
756 size = cfgp->size;
757 msg += size;
758 }
759
760 for (; i <= last_index; i++) {
761 trace_cfg_t *cfgp;
762 u16 *msg_idp;
763 u16 msg_id;
764 int size;
765
766 if (which == DUMP)
767 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
768
769 if (clib_arch_is_little_endian)
770 msg_id = ntohs(*((u16 *)msg));
771 else
772 msg_id = *((u16 *)msg);
773
774 cfgp = am->api_trace_cfg + msg_id;
775 if (!cfgp) {
776 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
777 return;
778 }
779 size = cfgp->size;
780
781 /* Copy the buffer (from the read-only mmap'ed file) */
782 vec_validate (tmpbuf, size-1 + sizeof(uword));
Damjan Marionf1213b82016-03-13 02:22:06 +0100783 clib_memcpy (tmpbuf+sizeof(uword), msg, size);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700784 memset (tmpbuf, 0xf, sizeof(uword));
785
786 /*
787 * Endian swap if needed. All msg data is supposed to be
788 * in network byte order. All msg handlers are supposed to
789 * know that. The generic message dumpers don't know that.
790 * One could fix apigen, I suppose.
791 */
792 if ((which == DUMP && clib_arch_is_little_endian)
793 || endian_swap_needed) {
794 void (*endian_fp)(void *);
795 if (msg_id >= vec_len (am->msg_endian_handlers)
796 || (am->msg_endian_handlers[msg_id] == 0)) {
797 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
798 return;
799 }
800 endian_fp = am->msg_endian_handlers[msg_id];
801 (*endian_fp)(tmpbuf+sizeof(uword));
802 }
803
804 /* msg_id always in network byte order */
805 if (clib_arch_is_little_endian) {
806 msg_idp = (u16 *)(tmpbuf+sizeof(uword));
807 *msg_idp = msg_id;
808 }
809
810 switch (which) {
811 case CUSTOM_DUMP:
812 case DUMP:
813 if (msg_id < vec_len(am->msg_print_handlers) &&
814 am->msg_print_handlers [msg_id]) {
815 u8 *(*print_fp)(void *, void *);
816
817 print_fp = (void *)am->msg_print_handlers[msg_id];
818 (*print_fp)(tmpbuf+sizeof(uword), vm);
819 } else {
820 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
821 msg_id);
822 break;
823 }
824 break;
825
826 case INITIALIZERS:
827 if (msg_id < vec_len(am->msg_print_handlers) &&
828 am->msg_print_handlers [msg_id]) {
829 u8 * s;
830 int j;
831 u8 *(*print_fp)(void *, void *);
832
833 print_fp = (void *)am->msg_print_handlers[msg_id];
834
835 vlib_cli_output (vm, "/*");
836
837 (*print_fp)(tmpbuf+sizeof(uword), vm);
838 vlib_cli_output (vm, "*/\n");
839
840 s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
841 am->msg_names[msg_id], i,
842 am->api_trace_cfg[msg_id].size);
843
844 for (j = 0; j < am->api_trace_cfg[msg_id].size; j++) {
845 if ((j & 7) == 0)
846 s = format (s, "\n ");
847 s = format (s, "0x%02x,", tmpbuf[sizeof(uword)+j]);
848 }
849 s = format (s, "\n};\n%c", 0);
850 vlib_cli_output (vm, (char *)s);
851 vec_free(s);
852 }
853 break;
854
855 case REPLAY:
856 if (msg_id < vec_len(am->msg_print_handlers) &&
857 am->msg_print_handlers [msg_id] && cfgp->replay_enable) {
858 void (*handler)(void *);
859
860 handler = (void *)am->msg_handlers[msg_id];
861
862 if (!am->is_mp_safe[msg_id])
863 vl_msg_api_barrier_sync();
864 (*handler)(tmpbuf+sizeof(uword));
865 if (!am->is_mp_safe[msg_id])
866 vl_msg_api_barrier_release();
867 } else {
868 if (cfgp->replay_enable)
869 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
870 msg_id);
871 break;
872 }
873 break;
874 }
875
876 _vec_len(tmpbuf) = 0;
877 msg += size;
878 }
879
880 if (saved_print_handlers) {
Damjan Marionf1213b82016-03-13 02:22:06 +0100881 clib_memcpy (am->msg_print_handlers, saved_print_handlers,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700882 vec_len(am->msg_print_handlers) * sizeof (void *));
883 vec_free (saved_print_handlers);
884 }
885
886 munmap (hp, file_size);
887}
888
889u8 * format_vl_msg_api_trace_status (u8 * s, va_list * args)
890{
891 api_main_t * am = va_arg (*args, api_main_t *);
892 vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
893 vl_api_trace_t *tp;
894 char *trace_name;
895
896 switch(which)
897 {
898 case VL_API_TRACE_TX:
899 tp = am->tx_trace;
900 trace_name = "TX trace";
901 break;
902
903 case VL_API_TRACE_RX:
904 tp = am->rx_trace;
905 trace_name = "RX trace";
906 break;
907
908 default:
909 abort();
910 }
911
912 if (tp == 0) {
913 s = format (s, "%s: not yet configured.\n", trace_name);
914 return s;
915 }
916
917 s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
918 trace_name, vec_len (tp->traces), tp->nitems,
919 tp->enabled ? "is" : "is not",
920 tp->wrapped ? "has" : "has not");
921 return s;
922}
923
924static u8 post_mortem_dump_enabled;
925
926static clib_error_t *
927api_trace_command_fn (vlib_main_t * vm,
928 unformat_input_t * input,
929 vlib_cli_command_t * cmd)
930{
931 u32 nitems = 256<<10;
932 api_main_t * am = &api_main;
933 vl_api_trace_which_t which = VL_API_TRACE_RX;
934 u8 *filename;
935 u32 first = 0;
936 u32 last = (u32)~0;
937 FILE *fp;
938 int rv;
939
940 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
941 if (unformat (input, "on") || unformat (input, "enable")) {
942 if (unformat (input, "nitems %d", &nitems))
943 ;
944 vl_msg_api_trace_configure (am, which, nitems);
945 vl_msg_api_trace_onoff (am, which, 1 /* on */);
946 } else if (unformat (input, "off")) {
947 vl_msg_api_trace_onoff (am, which, 0);
948 } else if (unformat (input, "save %s", &filename)) {
949 u8 * chroot_filename;
950 if (strstr((char *)filename, "..")
951 || index((char *)filename, '/'))
952 {
953 vlib_cli_output (vm, "illegal characters in filename '%s'",
954 filename);
955 return 0;
956 }
957
958 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
959
960 vec_free(filename);
961
962 fp = fopen ((char *)chroot_filename, "w");
963 if (fp == NULL) {
964 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
965 return 0;
966 }
967 rv = vl_msg_api_trace_save (am, which, fp);
968 fclose (fp);
969 if (rv < 0)
970 vlib_cli_output (vm, "ERROR: %d", rv);
971 else
972 vlib_cli_output (vm, "API trace saved to %s\n",
973 chroot_filename);
974 vec_free (chroot_filename);
975 } else if (unformat (input, "dump %s", &filename)) {
976 vl_msg_api_process_file (vm, filename, first, last, DUMP);
977 } else if (unformat (input, "custom-dump %s", &filename)) {
978 vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
979 } else if (unformat (input, "replay %s", &filename)) {
980 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
981 } else if (unformat (input, "initializers %s", &filename)) {
982 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
983 } else if (unformat (input, "tx")) {
984 which = VL_API_TRACE_TX;
985 } else if (unformat (input, "first %d", &first)) {
986 ;
987 } else if (unformat (input, "last %d", &last)) {
988 ;
989 } else if (unformat (input, "status")) {
990 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
991 am, which);
992 } else if (unformat (input, "free")) {
993 vl_msg_api_trace_onoff (am, which, 0);
994 vl_msg_api_trace_free (am, which);
995 } else if (unformat (input, "post-mortem-on"))
996 post_mortem_dump_enabled = 1;
997 else if (unformat (input, "post-mortem-off"))
998 post_mortem_dump_enabled = 0;
999 else
1000 return clib_error_return (0, "unknown input `%U'",
1001 format_unformat_error, input);
1002 }
1003 return 0;
1004}
1005
1006VLIB_CLI_COMMAND (api_trace_command, static) = {
1007 .path = "api trace",
1008 .short_help =
1009 "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
1010 .function = api_trace_command_fn,
1011};
1012
1013static clib_error_t *
1014api_config_fn (vlib_main_t * vm, unformat_input_t * input)
1015{
1016 u32 nitems = 256<<10;
1017 vl_api_trace_which_t which = VL_API_TRACE_RX;
1018 api_main_t * am = &api_main;
1019
1020 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1021 if (unformat (input, "on") || unformat (input, "enable")) {
1022 if (unformat (input, "nitems %d", &nitems))
1023 ;
1024 vl_msg_api_trace_configure (am, which, nitems);
1025 vl_msg_api_trace_onoff (am, which, 1 /* on */);
1026 post_mortem_dump_enabled = 1;
1027 } else
1028 return clib_error_return (0, "unknown input `%U'",
1029 format_unformat_error, input);
1030 }
1031 return 0;
1032}
1033
1034VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");
1035
1036void vl_msg_api_post_mortem_dump (void)
1037{
1038 api_main_t * am = &api_main;
1039 FILE *fp;
1040 char filename[64];
1041 int rv;
1042
1043 if (post_mortem_dump_enabled == 0)
1044 return;
1045
1046 snprintf (filename, sizeof(filename), "/tmp/api_post_mortem.%d",
1047 getpid());
1048
1049 fp = fopen (filename, "w");
1050 if (fp == NULL) {
1051 rv = write (2, "Couldn't create ", 16);
1052 rv = write (2, filename, strlen(filename));
1053 rv = write (2, "\n", 1);
1054 return;
1055 }
1056 rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp);
1057 fclose (fp);
1058 if (rv < 0) {
1059 rv = write (2, "Failed to save post-mortem API trace to ", 40);
1060 rv = write (2, filename, strlen(filename));
1061 rv = write (2, "\n", 1);
1062 }
1063
1064}
1065
1066/* Layered message handling support */
1067
1068void vl_msg_api_register_pd_handler (void *fp, u16 msg_id_host_byte_order)
1069{
1070 api_main_t * am = &api_main;
1071
1072 /* Mild idiot proofing */
1073 if (msg_id_host_byte_order > 10000)
1074 clib_warning ("msg_id_host_byte_order endian issue? %d arg vs %d",
1075 msg_id_host_byte_order,
1076 clib_net_to_host_u16 (msg_id_host_byte_order));
1077 vec_validate (am->pd_msg_handlers, msg_id_host_byte_order);
1078 am->pd_msg_handlers[msg_id_host_byte_order] = fp;
1079}
1080
1081int vl_msg_api_pd_handler (void *mp, int rv)
1082{
1083 api_main_t * am = &api_main;
1084 int (*fp)(void *, int);
1085 u16 msg_id;
1086
1087 if (clib_arch_is_little_endian)
1088 msg_id = clib_net_to_host_u16(*((u16 *)mp));
1089 else
1090 msg_id = *((u16 *)mp);
1091
1092 if (msg_id >= vec_len (am->pd_msg_handlers)
1093 || am->pd_msg_handlers[msg_id] == 0)
1094 return rv;
1095
1096 fp = am->pd_msg_handlers [msg_id];
1097 rv = (*fp)(mp, rv);
1098 return rv;
1099}
1100
1101void vl_msg_api_set_first_available_msg_id (u16 first_avail)
1102{
1103 api_main_t * am = &api_main;
1104
1105 am->first_available_msg_id = first_avail;
1106}
1107
1108u16 vl_msg_api_get_msg_ids (char * name, int n)
1109{
1110 api_main_t * am = &api_main;
1111 u8 * name_copy;
1112 vl_api_msg_range_t * rp;
1113 uword * p;
1114 u16 rv;
1115
1116 if (am->msg_range_by_name == 0)
1117 am->msg_range_by_name = hash_create_string (0, sizeof(uword));
1118
1119 name_copy = format (0, "%s%c", name, 0);
1120
1121 p = hash_get_mem (am->msg_range_by_name, name_copy);
1122 if (p) {
1123 clib_warning ("WARNING: duplicate message range registration for '%s'",
1124 name_copy);
1125 vec_free(name_copy);
1126 return ((u16) ~0);
1127 }
1128
1129 if (n < 0 || n > 1024) {
1130 clib_warning
1131 ("WARNING: bad number of message-IDs (%d) requested by '%s'",
1132 n, name_copy);
1133 vec_free(name_copy);
1134 return ((u16) ~0);
1135 }
1136
1137 vec_add2 (am->msg_ranges, rp, 1);
1138
1139 rv = rp->first_msg_id = am->first_available_msg_id;
1140 am->first_available_msg_id += n;
1141 rp->last_msg_id = am->first_available_msg_id - 1;
1142 rp->name = name_copy;
1143
1144 hash_set_mem (am->msg_range_by_name, name_copy, rp - am->msg_ranges);
1145
1146 return rv;
1147}