Line data Source code
1 : /*
2 : *------------------------------------------------------------------
3 : * Copyright (c) 2017 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 <stdlib.h>
19 : #include <stdio.h>
20 : #include <stdint.h>
21 : #include <arpa/inet.h>
22 : #include <stddef.h>
23 : #include <assert.h>
24 :
25 : #include <vpp-api/vapi/vapi_dbg.h>
26 : #include <vpp-api/vapi/vapi.h>
27 : #include <vpp-api/vapi/vapi_internal.h>
28 : #include <vppinfra/types.h>
29 : #include <vppinfra/pool.h>
30 : #include <vlib/vlib.h>
31 : #include <vlibapi/api_common.h>
32 : #include <vlibmemory/memory_client.h>
33 : #include <vlibmemory/memory_api.h>
34 : #include <vlibmemory/api.h>
35 :
36 : #include <vapi/memclnt.api.vapi.h>
37 : #include <vapi/vlib.api.vapi.h>
38 :
39 : #include <vlibmemory/vl_memory_msg_enum.h>
40 :
41 : #define vl_typedefs /* define message structures */
42 : #include <vlibmemory/vl_memory_api_h.h>
43 : #undef vl_typedefs
44 :
45 : /* we need to use control pings for some stuff and because we're forced to put
46 : * the code in headers, we need a way to be able to grab the ids of these
47 : * messages - so declare them here as extern */
48 : vapi_msg_id_t vapi_msg_id_control_ping = 0;
49 : vapi_msg_id_t vapi_msg_id_control_ping_reply = 0;
50 :
51 : DEFINE_VAPI_MSG_IDS_MEMCLNT_API_JSON;
52 : DEFINE_VAPI_MSG_IDS_VLIB_API_JSON;
53 :
54 : struct
55 : {
56 : size_t count;
57 : vapi_message_desc_t **msgs;
58 : size_t max_len_name_with_crc;
59 : } __vapi_metadata;
60 :
61 : typedef struct
62 : {
63 : u32 context;
64 : vapi_cb_t callback;
65 : void *callback_ctx;
66 : bool is_dump;
67 : } vapi_req_t;
68 :
69 : static const u32 context_counter_mask = (1 << 31);
70 :
71 : typedef struct
72 : {
73 : vapi_error_e (*cb) (vapi_ctx_t ctx, void *callback_ctx, vapi_msg_id_t id,
74 : void *payload);
75 : void *ctx;
76 : } vapi_generic_cb_with_ctx;
77 :
78 : typedef struct
79 : {
80 : vapi_error_e (*cb) (vapi_ctx_t ctx, void *callback_ctx, void *payload);
81 : void *ctx;
82 : } vapi_event_cb_with_ctx;
83 :
84 : struct vapi_ctx_s
85 : {
86 : vapi_mode_e mode;
87 : int requests_size; /* size of the requests array (circular queue) */
88 : int requests_start; /* index of first request */
89 : int requests_count; /* number of used slots */
90 : vapi_req_t *requests;
91 : u32 context_counter;
92 : vapi_generic_cb_with_ctx generic_cb;
93 : vapi_event_cb_with_ctx *event_cbs;
94 : u16 *vapi_msg_id_t_to_vl_msg_id;
95 : u16 vl_msg_id_max;
96 : vapi_msg_id_t *vl_msg_id_to_vapi_msg_t;
97 : bool connected;
98 : bool handle_keepalives;
99 : pthread_mutex_t requests_mutex;
100 :
101 : svm_queue_t *vl_input_queue;
102 : u32 my_client_index;
103 : /** client message index hash table */
104 : uword *msg_index_by_name_and_crc;
105 : };
106 :
107 : u32
108 289 : vapi_gen_req_context (vapi_ctx_t ctx)
109 : {
110 289 : ++ctx->context_counter;
111 289 : ctx->context_counter %= context_counter_mask;
112 289 : return ctx->context_counter | context_counter_mask;
113 : }
114 :
115 : size_t
116 0 : vapi_get_request_count (vapi_ctx_t ctx)
117 : {
118 0 : return ctx->requests_count;
119 : }
120 :
121 : bool
122 310 : vapi_requests_full (vapi_ctx_t ctx)
123 : {
124 310 : return (ctx->requests_count == ctx->requests_size);
125 : }
126 :
127 : bool
128 297428 : vapi_requests_empty (vapi_ctx_t ctx)
129 : {
130 297428 : return (0 == ctx->requests_count);
131 : }
132 :
133 : static int
134 1353 : vapi_requests_end (vapi_ctx_t ctx)
135 : {
136 1353 : return (ctx->requests_start + ctx->requests_count) % ctx->requests_size;
137 : }
138 :
139 : void
140 289 : vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump,
141 : vapi_cb_t callback, void *callback_ctx)
142 : {
143 289 : assert (!vapi_requests_full (ctx));
144 : /* if the mutex is not held, bad things will happen */
145 289 : assert (0 != pthread_mutex_trylock (&ctx->requests_mutex));
146 289 : const int requests_end = vapi_requests_end (ctx);
147 289 : vapi_req_t *slot = &ctx->requests[requests_end];
148 289 : slot->is_dump = is_dump;
149 289 : slot->context = context;
150 289 : slot->callback = callback;
151 289 : slot->callback_ctx = callback_ctx;
152 : VAPI_DBG ("stored@%d: context:%x (start is @%d)", requests_end, context,
153 : ctx->requests_start);
154 289 : ++ctx->requests_count;
155 289 : assert (!vapi_requests_empty (ctx));
156 289 : }
157 :
158 : #if VAPI_DEBUG_ALLOC
159 : struct to_be_freed_s;
160 : struct to_be_freed_s
161 : {
162 : void *v;
163 : struct to_be_freed_s *next;
164 : };
165 :
166 : static struct to_be_freed_s *to_be_freed = NULL;
167 :
168 : void
169 : vapi_add_to_be_freed (void *v)
170 : {
171 : struct to_be_freed_s *prev = NULL;
172 : struct to_be_freed_s *tmp;
173 : tmp = to_be_freed;
174 : while (tmp && tmp->v)
175 : {
176 : prev = tmp;
177 : tmp = tmp->next;
178 : }
179 : if (!tmp)
180 : {
181 : if (!prev)
182 : {
183 : tmp = to_be_freed = calloc (1, sizeof (*to_be_freed));
184 : }
185 : else
186 : {
187 : tmp = prev->next = calloc (1, sizeof (*to_be_freed));
188 : }
189 : }
190 : VAPI_DBG ("To be freed %p", v);
191 : tmp->v = v;
192 : }
193 :
194 : void
195 : vapi_trace_free (void *v)
196 : {
197 : struct to_be_freed_s *tmp = to_be_freed;
198 : while (tmp && tmp->v != v)
199 : {
200 : tmp = tmp->next;
201 : }
202 : if (tmp && tmp->v == v)
203 : {
204 : VAPI_DBG ("Freed %p", v);
205 : tmp->v = NULL;
206 : }
207 : else
208 : {
209 : VAPI_ERR ("Trying to free untracked pointer %p", v);
210 : abort ();
211 : }
212 : }
213 :
214 : void
215 : vapi_to_be_freed_validate ()
216 : {
217 : struct to_be_freed_s *tmp = to_be_freed;
218 : while (tmp)
219 : {
220 : if (tmp->v)
221 : {
222 : VAPI_ERR ("Unfreed msg %p!", tmp->v);
223 : }
224 : tmp = tmp->next;
225 : }
226 : }
227 :
228 : #endif
229 :
230 : void *
231 457 : vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
232 : {
233 457 : if (!ctx->connected)
234 : {
235 1 : return NULL;
236 : }
237 456 : void *rv = vl_msg_api_alloc_as_if_client_or_null (size);
238 456 : if (rv)
239 : {
240 456 : clib_memset (rv, 0, size);
241 : }
242 456 : return rv;
243 : }
244 :
245 : void
246 1107 : vapi_msg_free (vapi_ctx_t ctx, void *msg)
247 : {
248 1107 : if (!ctx->connected)
249 : {
250 0 : return;
251 : }
252 : #if VAPI_DEBUG_ALLOC
253 : vapi_trace_free (msg);
254 : #endif
255 1107 : vl_msg_api_free (msg);
256 : }
257 :
258 : vapi_msg_id_t
259 40 : vapi_lookup_vapi_msg_id_t (vapi_ctx_t ctx, u16 vl_msg_id)
260 : {
261 40 : if (vl_msg_id <= ctx->vl_msg_id_max)
262 : {
263 40 : return ctx->vl_msg_id_to_vapi_msg_t[vl_msg_id];
264 : }
265 0 : return VAPI_INVALID_MSG_ID;
266 : }
267 :
268 : vapi_error_e
269 19 : vapi_ctx_alloc (vapi_ctx_t * result)
270 : {
271 19 : vapi_ctx_t ctx = calloc (1, sizeof (struct vapi_ctx_s));
272 19 : if (!ctx)
273 : {
274 0 : return VAPI_ENOMEM;
275 : }
276 19 : ctx->context_counter = 0;
277 19 : ctx->vapi_msg_id_t_to_vl_msg_id =
278 19 : malloc (__vapi_metadata.count *
279 : sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
280 19 : if (!ctx->vapi_msg_id_t_to_vl_msg_id)
281 : {
282 0 : goto fail;
283 : }
284 19 : clib_memset (ctx->vapi_msg_id_t_to_vl_msg_id, ~0,
285 : __vapi_metadata.count *
286 : sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
287 19 : ctx->event_cbs = calloc (__vapi_metadata.count, sizeof (*ctx->event_cbs));
288 19 : if (!ctx->event_cbs)
289 : {
290 0 : goto fail;
291 : }
292 19 : pthread_mutex_init (&ctx->requests_mutex, NULL);
293 19 : *result = ctx;
294 19 : return VAPI_OK;
295 0 : fail:
296 0 : vapi_ctx_free (ctx);
297 0 : return VAPI_ENOMEM;
298 : }
299 :
300 : void
301 19 : vapi_ctx_free (vapi_ctx_t ctx)
302 : {
303 19 : assert (!ctx->connected);
304 19 : free (ctx->requests);
305 19 : free (ctx->vapi_msg_id_t_to_vl_msg_id);
306 19 : free (ctx->event_cbs);
307 19 : free (ctx->vl_msg_id_to_vapi_msg_t);
308 19 : pthread_mutex_destroy (&ctx->requests_mutex);
309 19 : free (ctx);
310 19 : }
311 :
312 : bool
313 120 : vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t id)
314 : {
315 120 : return vapi_lookup_vl_msg_id (ctx, id) != UINT16_MAX;
316 : }
317 :
318 : /* Cut and paste to avoid adding dependency to client library */
319 : __clib_nosanitize_addr static void
320 18 : VL_API_VEC_UNPOISON (const void *v)
321 : {
322 18 : const vec_header_t *vh = &((vec_header_t *) v)[-1];
323 18 : clib_mem_unpoison (vh, sizeof (*vh) + vec_len (v));
324 18 : }
325 :
326 : static void
327 36 : vapi_api_name_and_crc_free (vapi_ctx_t ctx)
328 : {
329 : int i;
330 36 : u8 **keys = 0;
331 : hash_pair_t *hp;
332 :
333 36 : if (!ctx->msg_index_by_name_and_crc)
334 18 : return;
335 105084 : hash_foreach_pair (hp, ctx->msg_index_by_name_and_crc,
336 : ({ vec_add1 (keys, (u8 *) hp->key); }));
337 31356 : for (i = 0; i < vec_len (keys); i++)
338 31338 : vec_free (keys[i]);
339 18 : vec_free (keys);
340 18 : hash_free (ctx->msg_index_by_name_and_crc);
341 : }
342 :
343 : static void
344 18 : vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx,
345 : vl_api_memclnt_create_v2_reply_t *mp)
346 : {
347 18 : serialize_main_t _sm, *sm = &_sm;
348 : u8 *tblv;
349 : u32 nmsgs;
350 : int i;
351 : u8 *name_and_crc;
352 : u32 msg_index;
353 :
354 18 : ctx->my_client_index = mp->index;
355 :
356 : /* Clean out any previous hash table (unlikely) */
357 18 : vapi_api_name_and_crc_free (ctx);
358 :
359 18 : ctx->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword));
360 :
361 : /* Recreate the vnet-side API message handler table */
362 18 : tblv = uword_to_pointer (mp->message_table, u8 *);
363 18 : unserialize_open_data (sm, tblv, vec_len (tblv));
364 18 : unserialize_integer (sm, &nmsgs, sizeof (u32));
365 :
366 18 : VL_API_VEC_UNPOISON (tblv);
367 :
368 31356 : for (i = 0; i < nmsgs; i++)
369 : {
370 31338 : msg_index = unserialize_likely_small_unsigned_integer (sm);
371 31338 : unserialize_cstring (sm, (char **) &name_and_crc);
372 62676 : hash_set_mem (ctx->msg_index_by_name_and_crc, name_and_crc, msg_index);
373 : }
374 18 : }
375 :
376 : static void
377 18 : vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx,
378 : vl_api_memclnt_delete_reply_t *mp)
379 : {
380 : void *oldheap;
381 18 : oldheap = vl_msg_push_heap ();
382 18 : svm_queue_free (ctx->vl_input_queue);
383 18 : vl_msg_pop_heap (oldheap);
384 :
385 18 : ctx->my_client_index = ~0;
386 18 : ctx->vl_input_queue = 0;
387 18 : }
388 :
389 : static int
390 18 : vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
391 : int input_queue_size, bool keepalive)
392 : {
393 : vl_api_memclnt_create_v2_t *mp;
394 : vl_api_memclnt_create_v2_reply_t *rp;
395 : svm_queue_t *vl_input_queue;
396 : vl_shmem_hdr_t *shmem_hdr;
397 18 : int rv = 0;
398 : void *oldheap;
399 18 : api_main_t *am = vlibapi_get_main ();
400 :
401 18 : shmem_hdr = am->shmem_hdr;
402 :
403 18 : if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0)
404 : {
405 0 : clib_warning ("shmem_hdr / input queue NULL");
406 0 : return -1;
407 : }
408 :
409 18 : clib_mem_unpoison (shmem_hdr, sizeof (*shmem_hdr));
410 18 : VL_MSG_API_SVM_QUEUE_UNPOISON (shmem_hdr->vl_input_queue);
411 :
412 18 : oldheap = vl_msg_push_heap ();
413 : vl_input_queue =
414 18 : svm_queue_alloc_and_init (input_queue_size, sizeof (uword), getpid ());
415 18 : vl_msg_pop_heap (oldheap);
416 :
417 18 : ctx->my_client_index = ~0;
418 18 : ctx->vl_input_queue = vl_input_queue;
419 :
420 18 : mp = vl_msg_api_alloc_as_if_client (sizeof (vl_api_memclnt_create_v2_t));
421 18 : clib_memset (mp, 0, sizeof (*mp));
422 18 : mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_V2);
423 18 : mp->ctx_quota = ctx_quota;
424 18 : mp->input_queue = (uword) vl_input_queue;
425 18 : strncpy ((char *) mp->name, name, sizeof (mp->name) - 1);
426 18 : mp->keepalive = keepalive;
427 :
428 18 : vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
429 :
430 : while (1)
431 0 : {
432 : int qstatus;
433 : struct timespec ts, tsrem;
434 : int i;
435 :
436 : /* Wait up to 10 seconds */
437 37 : for (i = 0; i < 1000; i++)
438 : {
439 : qstatus =
440 37 : svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0);
441 37 : if (qstatus == 0)
442 18 : goto read_one_msg;
443 19 : ts.tv_sec = 0;
444 19 : ts.tv_nsec = 10000 * 1000; /* 10 ms */
445 19 : while (nanosleep (&ts, &tsrem) < 0)
446 0 : ts = tsrem;
447 : }
448 : /* Timeout... */
449 0 : return -1;
450 :
451 18 : read_one_msg:
452 18 : VL_MSG_API_UNPOISON (rp);
453 18 : if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_V2_REPLY)
454 : {
455 0 : clib_warning ("unexpected reply: id %d", ntohs (rp->_vl_msg_id));
456 0 : continue;
457 : }
458 18 : rv = clib_net_to_host_u32 (rp->response);
459 18 : vapi_memclnt_create_v2_reply_t_handler (ctx, rp);
460 18 : break;
461 : }
462 18 : return (rv);
463 : }
464 :
465 : static void
466 18 : vapi_client_send_disconnect (vapi_ctx_t ctx, u8 do_cleanup)
467 : {
468 : vl_api_memclnt_delete_t *mp;
469 : vl_shmem_hdr_t *shmem_hdr;
470 18 : api_main_t *am = vlibapi_get_main ();
471 :
472 18 : ASSERT (am->vlib_rp);
473 18 : shmem_hdr = am->shmem_hdr;
474 18 : ASSERT (shmem_hdr && shmem_hdr->vl_input_queue);
475 :
476 18 : mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_delete_t));
477 18 : clib_memset (mp, 0, sizeof (*mp));
478 18 : mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE);
479 18 : mp->index = ctx->my_client_index;
480 18 : mp->do_cleanup = do_cleanup;
481 :
482 18 : vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
483 18 : }
484 :
485 : static int
486 0 : vapi_client_disconnect (vapi_ctx_t ctx)
487 : {
488 : vl_api_memclnt_delete_reply_t *rp;
489 : svm_queue_t *vl_input_queue;
490 : time_t begin;
491 : msgbuf_t *msgbuf;
492 :
493 0 : vl_input_queue = ctx->vl_input_queue;
494 0 : vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
495 :
496 : /*
497 : * Have to be careful here, in case the client is disconnecting
498 : * because e.g. the vlib process died, or is unresponsive.
499 : */
500 0 : begin = time (0);
501 : while (1)
502 0 : {
503 : time_t now;
504 :
505 0 : now = time (0);
506 :
507 0 : if (now >= (begin + 2))
508 : {
509 0 : clib_warning ("peer unresponsive, give up");
510 0 : ctx->my_client_index = ~0;
511 0 : return -1;
512 : }
513 0 : if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
514 0 : continue;
515 :
516 0 : VL_MSG_API_UNPOISON (rp);
517 :
518 : /* drain the queue */
519 0 : if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
520 : {
521 0 : clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
522 0 : msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
523 0 : vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
524 0 : continue;
525 : }
526 0 : msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
527 0 : vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
528 0 : break;
529 : }
530 :
531 0 : vapi_api_name_and_crc_free (ctx);
532 0 : return 0;
533 : }
534 :
535 : u32
536 2819 : vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc)
537 : {
538 : uword *p;
539 :
540 2819 : if (ctx->msg_index_by_name_and_crc)
541 : {
542 2819 : p = hash_get_mem (ctx->msg_index_by_name_and_crc, name_and_crc);
543 2819 : if (p)
544 2751 : return p[0];
545 : }
546 68 : return ~0;
547 : }
548 :
549 : vapi_error_e
550 17 : vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
551 : int max_outstanding_requests, int response_queue_size,
552 : vapi_mode_e mode, bool handle_keepalives)
553 : {
554 : int rv;
555 :
556 17 : if (response_queue_size <= 0 || max_outstanding_requests <= 0)
557 : {
558 0 : return VAPI_EINVAL;
559 : }
560 17 : if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024 * 1024 * 32))
561 : {
562 0 : return VAPI_ENOMEM;
563 : }
564 :
565 17 : ctx->requests_size = max_outstanding_requests;
566 17 : const size_t size = ctx->requests_size * sizeof (*ctx->requests);
567 17 : void *tmp = realloc (ctx->requests, size);
568 17 : if (!tmp)
569 : {
570 0 : return VAPI_ENOMEM;
571 : }
572 17 : ctx->requests = tmp;
573 17 : clib_memset (ctx->requests, 0, size);
574 : /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
575 17 : ctx->requests_start = ctx->requests_count = 0;
576 :
577 17 : if (chroot_prefix)
578 : {
579 : VAPI_DBG ("set memory root path `%s'", chroot_prefix);
580 17 : vl_set_memory_root_path ((char *) chroot_prefix);
581 : }
582 : static char api_map[] = "/vpe-api";
583 : VAPI_DBG ("client api map `%s'", api_map);
584 17 : if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0)
585 : {
586 0 : return VAPI_EMAP_FAIL;
587 : }
588 : VAPI_DBG ("connect client `%s'", name);
589 17 : if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size, true) <
590 : 0)
591 : {
592 0 : vl_client_api_unmap ();
593 0 : return VAPI_ECON_FAIL;
594 : }
595 : #if VAPI_DEBUG_CONNECT
596 : VAPI_DBG ("start probing messages");
597 : #endif
598 :
599 : int i;
600 2791 : for (i = 0; i < __vapi_metadata.count; ++i)
601 2774 : {
602 2774 : vapi_message_desc_t *m = __vapi_metadata.msgs[i];
603 2774 : u8 scratch[m->name_with_crc_len + 1];
604 2774 : memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
605 2774 : u32 id = vapi_api_get_msg_index (ctx, scratch);
606 :
607 2774 : if (VAPI_INVALID_MSG_ID != id)
608 : {
609 2706 : if (id > UINT16_MAX)
610 : {
611 : VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
612 : UINT16_MAX);
613 0 : rv = VAPI_EINVAL;
614 0 : goto fail;
615 : }
616 2706 : if (id > ctx->vl_msg_id_max)
617 : {
618 : vapi_msg_id_t *tmp =
619 391 : realloc (ctx->vl_msg_id_to_vapi_msg_t,
620 391 : sizeof (*ctx->vl_msg_id_to_vapi_msg_t) * (id + 1));
621 391 : if (!tmp)
622 : {
623 0 : rv = VAPI_ENOMEM;
624 0 : goto fail;
625 : }
626 391 : ctx->vl_msg_id_to_vapi_msg_t = tmp;
627 391 : ctx->vl_msg_id_max = id;
628 : }
629 2706 : ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
630 2706 : ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = id;
631 : #if VAPI_DEBUG_CONNECT
632 : VAPI_DBG ("Message `%s' has vl_msg_id `%u'", m->name_with_crc,
633 : (unsigned) id);
634 : #endif
635 : }
636 : else
637 : {
638 68 : ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = UINT16_MAX;
639 : VAPI_DBG ("Message `%s' not available", m->name_with_crc);
640 : }
641 : }
642 : #if VAPI_DEBUG_CONNECT
643 : VAPI_DBG ("finished probing messages");
644 : #endif
645 17 : if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
646 17 : !vapi_is_msg_available (ctx, vapi_msg_id_control_ping_reply))
647 : {
648 : VAPI_ERR (
649 : "control ping or control ping reply not available, cannot connect");
650 0 : rv = VAPI_EINCOMPATIBLE;
651 0 : goto fail;
652 : }
653 17 : ctx->mode = mode;
654 17 : ctx->connected = true;
655 17 : if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
656 : {
657 17 : ctx->handle_keepalives = handle_keepalives;
658 : }
659 : else
660 : {
661 0 : ctx->handle_keepalives = false;
662 : }
663 17 : return VAPI_OK;
664 0 : fail:
665 0 : vapi_client_disconnect (ctx);
666 0 : vl_client_api_unmap ();
667 0 : return rv;
668 : }
669 :
670 : /*
671 : * API client running in the same process as VPP
672 : */
673 : vapi_error_e
674 1 : vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name,
675 : int max_outstanding_requests, int response_queue_size,
676 : vapi_mode_e mode, bool handle_keepalives)
677 : {
678 : int rv;
679 :
680 1 : if (response_queue_size <= 0 || max_outstanding_requests <= 0)
681 : {
682 0 : return VAPI_EINVAL;
683 : }
684 :
685 1 : ctx->requests_size = max_outstanding_requests;
686 1 : const size_t size = ctx->requests_size * sizeof (*ctx->requests);
687 1 : void *tmp = realloc (ctx->requests, size);
688 1 : if (!tmp)
689 : {
690 0 : return VAPI_ENOMEM;
691 : }
692 1 : ctx->requests = tmp;
693 1 : clib_memset (ctx->requests, 0, size);
694 : /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
695 1 : ctx->requests_start = ctx->requests_count = 0;
696 :
697 : VAPI_DBG ("connect client `%s'", name);
698 1 : if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size,
699 : handle_keepalives) < 0)
700 : {
701 0 : return VAPI_ECON_FAIL;
702 : }
703 :
704 : int i;
705 46 : for (i = 0; i < __vapi_metadata.count; ++i)
706 45 : {
707 45 : vapi_message_desc_t *m = __vapi_metadata.msgs[i];
708 45 : u8 scratch[m->name_with_crc_len + 1];
709 45 : memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
710 45 : u32 id = vapi_api_get_msg_index (ctx, scratch);
711 45 : if (VAPI_INVALID_MSG_ID != id)
712 : {
713 45 : if (id > UINT16_MAX)
714 : {
715 : VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
716 : UINT16_MAX);
717 0 : rv = VAPI_EINVAL;
718 0 : goto fail;
719 : }
720 45 : if (id > ctx->vl_msg_id_max)
721 : {
722 : vapi_msg_id_t *tmp =
723 23 : realloc (ctx->vl_msg_id_to_vapi_msg_t,
724 23 : sizeof (*ctx->vl_msg_id_to_vapi_msg_t) * (id + 1));
725 23 : if (!tmp)
726 : {
727 0 : rv = VAPI_ENOMEM;
728 0 : goto fail;
729 : }
730 23 : ctx->vl_msg_id_to_vapi_msg_t = tmp;
731 23 : ctx->vl_msg_id_max = id;
732 : }
733 45 : ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
734 45 : ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = id;
735 : }
736 : else
737 : {
738 0 : ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = UINT16_MAX;
739 : VAPI_DBG ("Message `%s' not available", m->name_with_crc);
740 : }
741 : }
742 1 : if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
743 1 : !vapi_is_msg_available (ctx, vapi_msg_id_control_ping_reply))
744 : {
745 : VAPI_ERR (
746 : "control ping or control ping reply not available, cannot connect");
747 0 : rv = VAPI_EINCOMPATIBLE;
748 0 : goto fail;
749 : }
750 1 : ctx->mode = mode;
751 1 : ctx->connected = true;
752 1 : if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
753 : {
754 1 : ctx->handle_keepalives = handle_keepalives;
755 : }
756 : else
757 : {
758 0 : ctx->handle_keepalives = false;
759 : }
760 1 : return VAPI_OK;
761 0 : fail:
762 0 : vapi_client_disconnect (ctx);
763 0 : return rv;
764 : }
765 :
766 : vapi_error_e
767 1 : vapi_disconnect_from_vpp (vapi_ctx_t ctx)
768 : {
769 1 : if (!ctx->connected)
770 : {
771 0 : return VAPI_EINVAL;
772 : }
773 : vl_api_memclnt_delete_reply_t *rp;
774 : svm_queue_t *vl_input_queue;
775 : time_t begin;
776 1 : vl_input_queue = ctx->vl_input_queue;
777 1 : vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
778 :
779 : /*
780 : * Have to be careful here, in case the client is disconnecting
781 : * because e.g. the vlib process died, or is unresponsive.
782 : */
783 1 : begin = time (0);
784 1 : vapi_error_e rv = VAPI_OK;
785 : while (1)
786 220692 : {
787 : time_t now;
788 :
789 220693 : now = time (0);
790 :
791 220693 : if (now >= (begin + 2))
792 : {
793 0 : clib_warning ("peer unresponsive, give up");
794 0 : ctx->my_client_index = ~0;
795 0 : rv = VAPI_ENORESP;
796 0 : goto fail;
797 : }
798 220693 : if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
799 220692 : continue;
800 :
801 1 : VL_MSG_API_UNPOISON (rp);
802 :
803 : /* drain the queue */
804 1 : if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
805 : {
806 0 : clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
807 0 : vl_msg_api_free (rp);
808 0 : continue;
809 : }
810 1 : vapi_memclnt_delete_reply_t_handler (
811 : ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
812 1 : break;
813 : }
814 1 : fail:
815 1 : vapi_api_name_and_crc_free (ctx);
816 :
817 1 : ctx->connected = false;
818 1 : return rv;
819 : }
820 :
821 : vapi_error_e
822 17 : vapi_disconnect (vapi_ctx_t ctx)
823 : {
824 17 : if (!ctx->connected)
825 : {
826 0 : return VAPI_EINVAL;
827 : }
828 :
829 : vl_api_memclnt_delete_reply_t *rp;
830 : svm_queue_t *vl_input_queue;
831 : time_t begin;
832 17 : vl_input_queue = ctx->vl_input_queue;
833 17 : vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
834 :
835 : /*
836 : * Have to be careful here, in case the client is disconnecting
837 : * because e.g. the vlib process died, or is unresponsive.
838 : */
839 17 : begin = time (0);
840 17 : vapi_error_e rv = VAPI_OK;
841 : while (1)
842 385557 : {
843 : time_t now;
844 :
845 385574 : now = time (0);
846 :
847 385574 : if (now >= (begin + 2))
848 : {
849 0 : clib_warning ("peer unresponsive, give up");
850 0 : ctx->my_client_index = ~0;
851 0 : rv = VAPI_ENORESP;
852 0 : goto fail;
853 : }
854 385574 : if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
855 385557 : continue;
856 :
857 17 : VL_MSG_API_UNPOISON (rp);
858 :
859 : /* drain the queue */
860 17 : if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
861 : {
862 0 : clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
863 0 : vl_msg_api_free (rp);
864 0 : continue;
865 : }
866 17 : vapi_memclnt_delete_reply_t_handler (
867 : ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
868 17 : break;
869 : }
870 17 : fail:
871 17 : vapi_api_name_and_crc_free (ctx);
872 :
873 17 : vl_client_api_unmap ();
874 : #if VAPI_DEBUG_ALLOC
875 : vapi_to_be_freed_validate ();
876 : #endif
877 17 : ctx->connected = false;
878 17 : return rv;
879 : }
880 :
881 : vapi_error_e
882 0 : vapi_get_fd (vapi_ctx_t ctx, int *fd)
883 : {
884 0 : return VAPI_ENOTSUP;
885 : }
886 :
887 : vapi_error_e
888 185 : vapi_send (vapi_ctx_t ctx, void *msg)
889 : {
890 185 : vapi_error_e rv = VAPI_OK;
891 185 : if (!ctx || !msg || !ctx->connected)
892 : {
893 3 : rv = VAPI_EINVAL;
894 3 : goto out;
895 : }
896 : int tmp;
897 182 : svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
898 : #if VAPI_DEBUG
899 : unsigned msgid = be16toh (*(u16 *) msg);
900 : if (msgid <= ctx->vl_msg_id_max)
901 : {
902 : vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
903 : if (id < __vapi_metadata.count)
904 : {
905 : VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid,
906 : __vapi_metadata.msgs[id]->name);
907 : }
908 : else
909 : {
910 : VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
911 : }
912 : }
913 : else
914 : {
915 : VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
916 : }
917 : #endif
918 182 : tmp = svm_queue_add (q, (u8 *) & msg,
919 182 : VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
920 182 : if (tmp < 0)
921 : {
922 0 : rv = VAPI_EAGAIN;
923 : }
924 : else
925 182 : VL_MSG_API_POISON (msg);
926 185 : out:
927 : VAPI_DBG ("vapi_send() rv = %d", rv);
928 185 : return rv;
929 : }
930 :
931 : vapi_error_e
932 136 : vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
933 : {
934 136 : vapi_error_e rv = VAPI_OK;
935 136 : if (!ctx || !msg1 || !msg2 || !ctx->connected)
936 : {
937 0 : rv = VAPI_EINVAL;
938 0 : goto out;
939 : }
940 136 : svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
941 : #if VAPI_DEBUG
942 : unsigned msgid1 = be16toh (*(u16 *) msg1);
943 : unsigned msgid2 = be16toh (*(u16 *) msg2);
944 : const char *name1 = "UNKNOWN";
945 : const char *name2 = "UNKNOWN";
946 : if (msgid1 <= ctx->vl_msg_id_max)
947 : {
948 : vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid1];
949 : if (id < __vapi_metadata.count)
950 : {
951 : name1 = __vapi_metadata.msgs[id]->name;
952 : }
953 : }
954 : if (msgid2 <= ctx->vl_msg_id_max)
955 : {
956 : vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid2];
957 : if (id < __vapi_metadata.count)
958 : {
959 : name2 = __vapi_metadata.msgs[id]->name;
960 : }
961 : }
962 : VAPI_DBG ("send two: %u[%s], %u[%s]", msgid1, name1, msgid2, name2);
963 : #endif
964 136 : int tmp = svm_queue_add2 (q, (u8 *) & msg1, (u8 *) & msg2,
965 136 : VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
966 136 : if (tmp < 0)
967 : {
968 0 : rv = VAPI_EAGAIN;
969 : }
970 : else
971 136 : VL_MSG_API_POISON (msg1);
972 136 : out:
973 : VAPI_DBG ("vapi_send() rv = %d", rv);
974 136 : return rv;
975 : }
976 :
977 : vapi_error_e
978 308005 : vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size,
979 : svm_q_conditional_wait_t cond, u32 time)
980 : {
981 308005 : if (!ctx || !ctx->connected || !msg || !msg_size)
982 : {
983 3 : return VAPI_EINVAL;
984 : }
985 308002 : vapi_error_e rv = VAPI_OK;
986 : uword data;
987 :
988 308002 : svm_queue_t *q = ctx->vl_input_queue;
989 :
990 308002 : again:
991 : VAPI_DBG ("doing shm queue sub");
992 :
993 308002 : int tmp = svm_queue_sub (q, (u8 *) & data, cond, time);
994 :
995 308002 : if (tmp == 0)
996 : {
997 1107 : VL_MSG_API_UNPOISON ((void *) data);
998 : #if VAPI_DEBUG_ALLOC
999 : vapi_add_to_be_freed ((void *) data);
1000 : #endif
1001 1107 : msgbuf_t *msgbuf =
1002 1107 : (msgbuf_t *) ((u8 *) data - offsetof (msgbuf_t, data));
1003 1107 : if (!msgbuf->data_len)
1004 : {
1005 0 : vapi_msg_free (ctx, (u8 *) data);
1006 0 : return VAPI_EAGAIN;
1007 : }
1008 1107 : *msg = (u8 *) data;
1009 1107 : *msg_size = ntohl (msgbuf->data_len);
1010 : #if VAPI_DEBUG
1011 : unsigned msgid = be16toh (*(u16 *) * msg);
1012 : if (msgid <= ctx->vl_msg_id_max)
1013 : {
1014 : vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
1015 : if (id < __vapi_metadata.count)
1016 : {
1017 : VAPI_DBG ("recv msg@%p:%u[%s]", *msg, msgid,
1018 : __vapi_metadata.msgs[id]->name);
1019 : }
1020 : else
1021 : {
1022 : VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
1023 : }
1024 : }
1025 : else
1026 : {
1027 : VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
1028 : }
1029 : #endif
1030 1107 : if (ctx->handle_keepalives)
1031 : {
1032 1107 : unsigned msgid = be16toh (*(u16 *) * msg);
1033 1107 : if (msgid ==
1034 1107 : vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive))
1035 : {
1036 0 : vapi_msg_memclnt_keepalive_reply *reply = NULL;
1037 : do
1038 : {
1039 0 : reply = vapi_msg_alloc (ctx, sizeof (*reply));
1040 : }
1041 0 : while (!reply);
1042 0 : reply->header.context = vapi_get_client_index (ctx);
1043 0 : reply->header._vl_msg_id =
1044 0 : vapi_lookup_vl_msg_id (ctx,
1045 : vapi_msg_id_memclnt_keepalive_reply);
1046 0 : reply->payload.retval = 0;
1047 0 : vapi_msg_memclnt_keepalive_reply_hton (reply);
1048 0 : while (VAPI_EAGAIN == vapi_send (ctx, reply));
1049 0 : vapi_msg_free (ctx, *msg);
1050 0 : goto again;
1051 : }
1052 : }
1053 : }
1054 : else
1055 : {
1056 306895 : rv = VAPI_EAGAIN;
1057 : }
1058 308002 : return rv;
1059 : }
1060 :
1061 : vapi_error_e
1062 0 : vapi_wait (vapi_ctx_t ctx)
1063 : {
1064 0 : svm_queue_lock (ctx->vl_input_queue);
1065 0 : svm_queue_wait (ctx->vl_input_queue);
1066 0 : svm_queue_unlock (ctx->vl_input_queue);
1067 :
1068 0 : return VAPI_OK;
1069 : }
1070 :
1071 : static vapi_error_e
1072 1064 : vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id,
1073 : u32 context, void *msg)
1074 : {
1075 : int mrv;
1076 1064 : if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
1077 : {
1078 : VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
1079 0 : return VAPI_MUTEX_FAILURE;
1080 : }
1081 1064 : int tmp = ctx->requests_start;
1082 1064 : const int requests_end = vapi_requests_end (ctx);
1083 1065 : while (ctx->requests[tmp].context != context && tmp != requests_end)
1084 : {
1085 1 : ++tmp;
1086 1 : if (tmp == ctx->requests_size)
1087 : {
1088 0 : tmp = 0;
1089 : }
1090 : }
1091 : VAPI_DBG ("dispatch, search from %d, %s at %d", ctx->requests_start,
1092 : ctx->requests[tmp].context == context ? "matched" : "stopped",
1093 : tmp);
1094 1064 : vapi_error_e rv = VAPI_OK;
1095 1064 : if (ctx->requests[tmp].context == context)
1096 : {
1097 1065 : while (ctx->requests_start != tmp)
1098 : {
1099 : VAPI_ERR ("No response to req with context=%u",
1100 : (unsigned) ctx->requests[tmp].context);
1101 1 : ctx->requests[ctx->requests_start].callback (ctx, ctx->requests
1102 1 : [ctx->
1103 : requests_start].callback_ctx,
1104 : VAPI_ENORESP, true,
1105 : NULL);
1106 1 : clib_memset (&ctx->requests[ctx->requests_start], 0,
1107 : sizeof (ctx->requests[ctx->requests_start]));
1108 1 : ++ctx->requests_start;
1109 1 : --ctx->requests_count;
1110 1 : if (ctx->requests_start == ctx->requests_size)
1111 : {
1112 0 : ctx->requests_start = 0;
1113 : }
1114 : }
1115 : // now ctx->requests_start == tmp
1116 1064 : int payload_offset = vapi_get_payload_offset (id);
1117 1064 : void *payload = ((u8 *) msg) + payload_offset;
1118 1064 : bool is_last = true;
1119 1064 : if (ctx->requests[tmp].is_dump)
1120 : {
1121 908 : if (vapi_msg_id_control_ping_reply == id)
1122 : {
1123 132 : payload = NULL;
1124 : }
1125 : else
1126 : {
1127 776 : is_last = false;
1128 : }
1129 : }
1130 1064 : if (payload_offset != -1)
1131 : {
1132 1064 : rv = ctx->requests[tmp].callback (
1133 1064 : ctx, ctx->requests[tmp].callback_ctx, VAPI_OK, is_last, payload);
1134 : }
1135 : else
1136 : {
1137 : /* this is a message without payload, so bend the callback a little
1138 : */
1139 : rv =
1140 0 : ((vapi_error_e (*)(vapi_ctx_t, void *, vapi_error_e, bool))
1141 0 : ctx->requests[tmp].callback) (ctx,
1142 0 : ctx->requests[tmp].callback_ctx,
1143 : VAPI_OK, is_last);
1144 : }
1145 1064 : if (is_last)
1146 : {
1147 288 : clib_memset (&ctx->requests[ctx->requests_start], 0,
1148 : sizeof (ctx->requests[ctx->requests_start]));
1149 288 : ++ctx->requests_start;
1150 288 : --ctx->requests_count;
1151 288 : if (ctx->requests_start == ctx->requests_size)
1152 : {
1153 4 : ctx->requests_start = 0;
1154 : }
1155 : }
1156 : VAPI_DBG ("after dispatch, req start = %d, end = %d, count = %d",
1157 : ctx->requests_start, requests_end, ctx->requests_count);
1158 : }
1159 1064 : if (0 != (mrv = pthread_mutex_unlock (&ctx->requests_mutex)))
1160 : {
1161 : VAPI_DBG ("pthread_mutex_unlock() failed, rv=%d:%s", mrv,
1162 : strerror (mrv));
1163 0 : abort (); /* this really shouldn't happen */
1164 : }
1165 1064 : return rv;
1166 : }
1167 :
1168 : static vapi_error_e
1169 2 : vapi_dispatch_event (vapi_ctx_t ctx, vapi_msg_id_t id, void *msg)
1170 : {
1171 2 : if (ctx->event_cbs[id].cb)
1172 : {
1173 0 : return ctx->event_cbs[id].cb (ctx, ctx->event_cbs[id].ctx, msg);
1174 : }
1175 2 : else if (ctx->generic_cb.cb)
1176 : {
1177 1 : return ctx->generic_cb.cb (ctx, ctx->generic_cb.ctx, id, msg);
1178 : }
1179 : else
1180 : {
1181 : VAPI_DBG
1182 : ("No handler/generic handler for msg id %u[%s], message ignored",
1183 : (unsigned) id, __vapi_metadata.msgs[id]->name);
1184 : }
1185 1 : return VAPI_OK;
1186 : }
1187 :
1188 : bool
1189 1106 : vapi_msg_is_with_context (vapi_msg_id_t id)
1190 : {
1191 1106 : assert (id <= __vapi_metadata.count);
1192 1106 : return __vapi_metadata.msgs[id]->has_context;
1193 : }
1194 :
1195 : static int
1196 1066 : vapi_verify_msg_size (vapi_msg_id_t id, void *buf, uword buf_size)
1197 : {
1198 1066 : assert (id < __vapi_metadata.count);
1199 1066 : return __vapi_metadata.msgs[id]->verify_msg_size (buf, buf_size);
1200 : }
1201 :
1202 : vapi_error_e
1203 307961 : vapi_dispatch_one (vapi_ctx_t ctx)
1204 : {
1205 : VAPI_DBG ("vapi_dispatch_one()");
1206 : void *msg;
1207 : uword size;
1208 307961 : svm_q_conditional_wait_t cond =
1209 307961 : vapi_is_nonblocking (ctx) ? SVM_Q_NOWAIT : SVM_Q_WAIT;
1210 307961 : vapi_error_e rv = vapi_recv (ctx, &msg, &size, cond, 0);
1211 307961 : if (VAPI_OK != rv)
1212 : {
1213 : VAPI_DBG ("vapi_recv failed with rv=%d", rv);
1214 306895 : return rv;
1215 : }
1216 1066 : u16 vpp_id = be16toh (*(u16 *) msg);
1217 1066 : if (vpp_id > ctx->vl_msg_id_max)
1218 : {
1219 : VAPI_ERR ("Unknown msg ID received, id `%u', out of range <0,%u>",
1220 : (unsigned) vpp_id, (unsigned) ctx->vl_msg_id_max);
1221 0 : vapi_msg_free (ctx, msg);
1222 0 : return VAPI_EINVAL;
1223 : }
1224 1066 : if (VAPI_INVALID_MSG_ID == (unsigned) ctx->vl_msg_id_to_vapi_msg_t[vpp_id])
1225 : {
1226 : VAPI_ERR ("Unknown msg ID received, id `%u' marked as not supported",
1227 : (unsigned) vpp_id);
1228 0 : vapi_msg_free (ctx, msg);
1229 0 : return VAPI_EINVAL;
1230 : }
1231 1066 : const vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[vpp_id];
1232 1066 : if (vapi_verify_msg_size (id, msg, size))
1233 : {
1234 0 : vapi_msg_free (ctx, msg);
1235 0 : return VAPI_EINVAL;
1236 : }
1237 : u32 context;
1238 1066 : vapi_get_swap_to_host_func (id) (msg);
1239 1066 : if (vapi_msg_is_with_context (id))
1240 : {
1241 1066 : context = *(u32 *) (((u8 *) msg) + vapi_get_context_offset (id));
1242 : /* is this a message originating from VAPI? */
1243 : VAPI_DBG ("dispatch, context is %x", context);
1244 1066 : if (context & context_counter_mask)
1245 : {
1246 1064 : rv = vapi_dispatch_response (ctx, id, context, msg);
1247 1064 : goto done;
1248 : }
1249 : }
1250 2 : rv = vapi_dispatch_event (ctx, id, msg);
1251 :
1252 1066 : done:
1253 1066 : vapi_msg_free (ctx, msg);
1254 1066 : return rv;
1255 : }
1256 :
1257 : vapi_error_e
1258 296075 : vapi_dispatch (vapi_ctx_t ctx)
1259 : {
1260 296075 : vapi_error_e rv = VAPI_OK;
1261 297139 : while (!vapi_requests_empty (ctx))
1262 : {
1263 296861 : rv = vapi_dispatch_one (ctx);
1264 296861 : if (VAPI_OK != rv)
1265 : {
1266 295797 : return rv;
1267 : }
1268 : }
1269 278 : return rv;
1270 : }
1271 :
1272 : void
1273 0 : vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
1274 : vapi_event_cb callback, void *callback_ctx)
1275 : {
1276 0 : vapi_event_cb_with_ctx *c = &ctx->event_cbs[id];
1277 0 : c->cb = callback;
1278 0 : c->ctx = callback_ctx;
1279 0 : }
1280 :
1281 : void
1282 0 : vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id)
1283 : {
1284 0 : vapi_set_event_cb (ctx, id, NULL, NULL);
1285 0 : }
1286 :
1287 : void
1288 1 : vapi_set_generic_event_cb (vapi_ctx_t ctx, vapi_generic_event_cb callback,
1289 : void *callback_ctx)
1290 : {
1291 1 : ctx->generic_cb.cb = callback;
1292 1 : ctx->generic_cb.ctx = callback_ctx;
1293 1 : }
1294 :
1295 : void
1296 1 : vapi_clear_generic_event_cb (vapi_ctx_t ctx)
1297 : {
1298 1 : ctx->generic_cb.cb = NULL;
1299 1 : ctx->generic_cb.ctx = NULL;
1300 1 : }
1301 :
1302 : u16
1303 1683 : vapi_lookup_vl_msg_id (vapi_ctx_t ctx, vapi_msg_id_t id)
1304 : {
1305 1683 : assert (id < __vapi_metadata.count);
1306 1683 : return ctx->vapi_msg_id_t_to_vl_msg_id[id];
1307 : }
1308 :
1309 : int
1310 456 : vapi_get_client_index (vapi_ctx_t ctx)
1311 : {
1312 456 : return ctx->my_client_index;
1313 : }
1314 :
1315 : bool
1316 308539 : vapi_is_nonblocking (vapi_ctx_t ctx)
1317 : {
1318 308539 : return (VAPI_MODE_NONBLOCKING == ctx->mode);
1319 : }
1320 :
1321 : size_t
1322 0 : vapi_get_max_request_count (vapi_ctx_t ctx)
1323 : {
1324 0 : return ctx->requests_size - 1;
1325 : }
1326 :
1327 : int
1328 1064 : vapi_get_payload_offset (vapi_msg_id_t id)
1329 : {
1330 1064 : assert (id < __vapi_metadata.count);
1331 1064 : return __vapi_metadata.msgs[id]->payload_offset;
1332 : }
1333 :
1334 1066 : void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *msg)
1335 : {
1336 1066 : assert (id < __vapi_metadata.count);
1337 1066 : return __vapi_metadata.msgs[id]->swap_to_host;
1338 : }
1339 :
1340 0 : void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *msg)
1341 : {
1342 0 : assert (id < __vapi_metadata.count);
1343 0 : return __vapi_metadata.msgs[id]->swap_to_be;
1344 : }
1345 :
1346 : size_t
1347 1106 : vapi_get_context_offset (vapi_msg_id_t id)
1348 : {
1349 1106 : assert (id < __vapi_metadata.count);
1350 1106 : return __vapi_metadata.msgs[id]->context_offset;
1351 : }
1352 :
1353 : vapi_msg_id_t
1354 52318 : vapi_register_msg (vapi_message_desc_t * msg)
1355 : {
1356 52318 : int i = 0;
1357 1410050 : for (i = 0; i < __vapi_metadata.count; ++i)
1358 : {
1359 1380550 : if (!strcmp
1360 1380550 : (msg->name_with_crc, __vapi_metadata.msgs[i]->name_with_crc))
1361 : {
1362 : /* this happens if somebody is linking together several objects while
1363 : * using the static inline headers, just fill in the already
1364 : * assigned id here so that all the objects are in sync */
1365 22815 : msg->id = __vapi_metadata.msgs[i]->id;
1366 22815 : return msg->id;
1367 : }
1368 : }
1369 29503 : vapi_msg_id_t id = __vapi_metadata.count;
1370 29503 : ++__vapi_metadata.count;
1371 29503 : __vapi_metadata.msgs =
1372 29503 : realloc (__vapi_metadata.msgs,
1373 29503 : sizeof (*__vapi_metadata.msgs) * __vapi_metadata.count);
1374 29503 : __vapi_metadata.msgs[id] = msg;
1375 29503 : size_t s = strlen (msg->name_with_crc);
1376 29503 : if (s > __vapi_metadata.max_len_name_with_crc)
1377 : {
1378 2951 : __vapi_metadata.max_len_name_with_crc = s;
1379 : }
1380 29503 : msg->id = id;
1381 29503 : return id;
1382 : }
1383 :
1384 : vapi_error_e
1385 289 : vapi_producer_lock (vapi_ctx_t ctx)
1386 : {
1387 : int mrv;
1388 289 : if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
1389 : {
1390 : VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
1391 : (void) mrv; /* avoid warning if the above debug is not enabled */
1392 0 : return VAPI_MUTEX_FAILURE;
1393 : }
1394 289 : return VAPI_OK;
1395 : }
1396 :
1397 : vapi_error_e
1398 289 : vapi_producer_unlock (vapi_ctx_t ctx)
1399 : {
1400 : int mrv;
1401 289 : if (0 != (mrv = pthread_mutex_unlock (&ctx->requests_mutex)))
1402 : {
1403 : VAPI_DBG ("pthread_mutex_unlock() failed, rv=%d:%s", mrv,
1404 : strerror (mrv));
1405 : (void) mrv; /* avoid warning if the above debug is not enabled */
1406 0 : return VAPI_MUTEX_FAILURE;
1407 : }
1408 289 : return VAPI_OK;
1409 : }
1410 :
1411 : size_t
1412 6 : vapi_get_message_count ()
1413 : {
1414 6 : return __vapi_metadata.count;
1415 : }
1416 :
1417 : const char *
1418 0 : vapi_get_msg_name (vapi_msg_id_t id)
1419 : {
1420 0 : return __vapi_metadata.msgs[id]->name;
1421 : }
1422 :
1423 : void
1424 0 : vapi_stop_rx_thread (vapi_ctx_t ctx)
1425 : {
1426 0 : if (!ctx || !ctx->connected || !ctx->vl_input_queue)
1427 : {
1428 0 : return;
1429 : }
1430 :
1431 0 : vl_client_stop_rx_thread (ctx->vl_input_queue);
1432 : }
1433 : /*
1434 : * fd.io coding-style-patch-verification: ON
1435 : *
1436 : * Local Variables:
1437 : * eval: (c-set-style "gnu")
1438 : * End:
1439 : */
|