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