blob: 7700eb06d3a13cbea9a21ebbdc550a8ba7c421dc [file] [log] [blame]
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001/*
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>
Dave Barach59b25652017-09-10 15:04:27 -040029#include <vppinfra/pool.h>
30#include <vlib/vlib.h>
Klement Sekera8f2a4ea2017-05-04 06:15:18 +020031#include <vlibapi/api_common.h>
Florin Corase86a8ed2018-01-05 03:20:25 -080032#include <vlibmemory/memory_client.h>
Ole Troan2ca88ff2022-01-27 16:25:43 +010033#include <vlibmemory/memory_api.h>
34#include <vlibmemory/api.h>
Klement Sekera8f2a4ea2017-05-04 06:15:18 +020035
Klement Sekeradab732a2018-07-04 13:43:46 +020036#include <vapi/memclnt.api.vapi.h>
Filip Tehlarf0e67d72021-07-23 22:03:05 +000037#include <vapi/vlib.api.vapi.h>
Klement Sekeradab732a2018-07-04 13:43:46 +020038
Ole Troan2ca88ff2022-01-27 16:25:43 +010039#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
Klement Sekera8f2a4ea2017-05-04 06:15:18 +020045/* 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 */
48vapi_msg_id_t vapi_msg_id_control_ping = 0;
49vapi_msg_id_t vapi_msg_id_control_ping_reply = 0;
50
Klement Sekeradab732a2018-07-04 13:43:46 +020051DEFINE_VAPI_MSG_IDS_MEMCLNT_API_JSON;
Florin Corasa1400ce2021-09-15 09:02:08 -070052DEFINE_VAPI_MSG_IDS_VLIB_API_JSON;
Klement Sekeradab732a2018-07-04 13:43:46 +020053
Klement Sekera8f2a4ea2017-05-04 06:15:18 +020054struct
55{
56 size_t count;
57 vapi_message_desc_t **msgs;
58 size_t max_len_name_with_crc;
59} __vapi_metadata;
60
61typedef struct
62{
63 u32 context;
64 vapi_cb_t callback;
65 void *callback_ctx;
66 bool is_dump;
67} vapi_req_t;
68
69static const u32 context_counter_mask = (1 << 31);
70
71typedef 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
78typedef 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
84struct 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;
Klement Sekeradab732a2018-07-04 13:43:46 +020098 bool handle_keepalives;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +020099 pthread_mutex_t requests_mutex;
Ole Troan2ca88ff2022-01-27 16:25:43 +0100100
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;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200105};
106
107u32
108vapi_gen_req_context (vapi_ctx_t ctx)
109{
110 ++ctx->context_counter;
111 ctx->context_counter %= context_counter_mask;
112 return ctx->context_counter | context_counter_mask;
113}
114
115size_t
116vapi_get_request_count (vapi_ctx_t ctx)
117{
118 return ctx->requests_count;
119}
120
121bool
122vapi_requests_full (vapi_ctx_t ctx)
123{
124 return (ctx->requests_count == ctx->requests_size);
125}
126
Klement Sekeradc15be22017-06-12 06:49:33 +0200127bool
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200128vapi_requests_empty (vapi_ctx_t ctx)
129{
130 return (0 == ctx->requests_count);
131}
132
133static int
134vapi_requests_end (vapi_ctx_t ctx)
135{
136 return (ctx->requests_start + ctx->requests_count) % ctx->requests_size;
137}
138
139void
140vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump,
141 vapi_cb_t callback, void *callback_ctx)
142{
143 assert (!vapi_requests_full (ctx));
144 /* if the mutex is not held, bad things will happen */
145 assert (0 != pthread_mutex_trylock (&ctx->requests_mutex));
146 const int requests_end = vapi_requests_end (ctx);
147 vapi_req_t *slot = &ctx->requests[requests_end];
148 slot->is_dump = is_dump;
149 slot->context = context;
150 slot->callback = callback;
151 slot->callback_ctx = callback_ctx;
152 VAPI_DBG ("stored@%d: context:%x (start is @%d)", requests_end, context,
153 ctx->requests_start);
154 ++ctx->requests_count;
155 assert (!vapi_requests_empty (ctx));
156}
157
158#if VAPI_DEBUG_ALLOC
159struct to_be_freed_s;
160struct to_be_freed_s
161{
162 void *v;
163 struct to_be_freed_s *next;
164};
165
166static struct to_be_freed_s *to_be_freed = NULL;
167
168void
169vapi_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
194void
195vapi_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
214void
215vapi_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
230void *
231vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
232{
233 if (!ctx->connected)
234 {
235 return NULL;
236 }
Ole Troan2ca88ff2022-01-27 16:25:43 +0100237 void *rv = vl_msg_api_alloc_as_if_client_or_null (size);
Klement Sekera35418ba2020-06-09 14:17:45 +0000238 if (rv)
239 {
240 clib_memset (rv, 0, size);
241 }
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200242 return rv;
243}
244
245void
246vapi_msg_free (vapi_ctx_t ctx, void *msg)
247{
248 if (!ctx->connected)
249 {
250 return;
251 }
252#if VAPI_DEBUG_ALLOC
253 vapi_trace_free (msg);
254#endif
255 vl_msg_api_free (msg);
256}
257
Klement Sekeradc15be22017-06-12 06:49:33 +0200258vapi_msg_id_t
259vapi_lookup_vapi_msg_id_t (vapi_ctx_t ctx, u16 vl_msg_id)
260{
261 if (vl_msg_id <= ctx->vl_msg_id_max)
262 {
263 return ctx->vl_msg_id_to_vapi_msg_t[vl_msg_id];
264 }
Klement Sekeradab732a2018-07-04 13:43:46 +0200265 return VAPI_INVALID_MSG_ID;
Klement Sekeradc15be22017-06-12 06:49:33 +0200266}
267
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200268vapi_error_e
269vapi_ctx_alloc (vapi_ctx_t * result)
270{
271 vapi_ctx_t ctx = calloc (1, sizeof (struct vapi_ctx_s));
272 if (!ctx)
273 {
274 return VAPI_ENOMEM;
275 }
276 ctx->context_counter = 0;
277 ctx->vapi_msg_id_t_to_vl_msg_id =
278 malloc (__vapi_metadata.count *
279 sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
280 if (!ctx->vapi_msg_id_t_to_vl_msg_id)
281 {
282 goto fail;
283 }
Dave Barachb7b92992018-10-17 10:38:51 -0400284 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));
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200287 ctx->event_cbs = calloc (__vapi_metadata.count, sizeof (*ctx->event_cbs));
288 if (!ctx->event_cbs)
289 {
290 goto fail;
291 }
292 pthread_mutex_init (&ctx->requests_mutex, NULL);
293 *result = ctx;
294 return VAPI_OK;
295fail:
296 vapi_ctx_free (ctx);
297 return VAPI_ENOMEM;
298}
299
300void
301vapi_ctx_free (vapi_ctx_t ctx)
302{
303 assert (!ctx->connected);
304 free (ctx->requests);
305 free (ctx->vapi_msg_id_t_to_vl_msg_id);
306 free (ctx->event_cbs);
307 free (ctx->vl_msg_id_to_vapi_msg_t);
308 pthread_mutex_destroy (&ctx->requests_mutex);
309 free (ctx);
310}
311
312bool
313vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t id)
314{
315 return vapi_lookup_vl_msg_id (ctx, id) != UINT16_MAX;
316}
317
Ole Troan2ca88ff2022-01-27 16:25:43 +0100318/* Cut and paste to avoid adding dependency to client library */
319__clib_nosanitize_addr static void
320VL_API_VEC_UNPOISON (const void *v)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200321{
Ole Troan2ca88ff2022-01-27 16:25:43 +0100322 const vec_header_t *vh = &((vec_header_t *) v)[-1];
323 clib_mem_unpoison (vh, sizeof (*vh) + vec_len (v));
324}
325
326static void
327vapi_api_name_and_crc_free (vapi_ctx_t ctx)
328{
329 int i;
330 u8 **keys = 0;
331 hash_pair_t *hp;
332
333 if (!ctx->msg_index_by_name_and_crc)
334 return;
335 hash_foreach_pair (hp, ctx->msg_index_by_name_and_crc,
336 ({ vec_add1 (keys, (u8 *) hp->key); }));
337 for (i = 0; i < vec_len (keys); i++)
338 vec_free (keys[i]);
339 vec_free (keys);
340 hash_free (ctx->msg_index_by_name_and_crc);
341}
342
343static void
344vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx,
345 vl_api_memclnt_create_v2_reply_t *mp)
346{
347 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 ctx->my_client_index = mp->index;
355
356 /* Clean out any previous hash table (unlikely) */
357 vapi_api_name_and_crc_free (ctx);
358
359 ctx->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword));
360
361 /* Recreate the vnet-side API message handler table */
362 tblv = uword_to_pointer (mp->message_table, u8 *);
363 unserialize_open_data (sm, tblv, vec_len (tblv));
364 unserialize_integer (sm, &nmsgs, sizeof (u32));
365
366 VL_API_VEC_UNPOISON (tblv);
367
368 for (i = 0; i < nmsgs; i++)
369 {
370 msg_index = unserialize_likely_small_unsigned_integer (sm);
371 unserialize_cstring (sm, (char **) &name_and_crc);
372 hash_set_mem (ctx->msg_index_by_name_and_crc, name_and_crc, msg_index);
373 }
374}
375
376static void
377vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx,
378 vl_api_memclnt_delete_reply_t *mp)
379{
380 void *oldheap;
381 oldheap = vl_msg_push_heap ();
382 svm_queue_free (ctx->vl_input_queue);
383 vl_msg_pop_heap (oldheap);
384
385 ctx->my_client_index = ~0;
386 ctx->vl_input_queue = 0;
387}
388
Mauro Sardara8c626b42022-12-12 14:04:09 +0000389static int
Ole Troan2ca88ff2022-01-27 16:25:43 +0100390vapi_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 int rv = 0;
398 void *oldheap;
399 api_main_t *am = vlibapi_get_main ();
400
401 shmem_hdr = am->shmem_hdr;
402
403 if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0)
404 {
405 clib_warning ("shmem_hdr / input queue NULL");
406 return -1;
407 }
408
409 clib_mem_unpoison (shmem_hdr, sizeof (*shmem_hdr));
410 VL_MSG_API_SVM_QUEUE_UNPOISON (shmem_hdr->vl_input_queue);
411
412 oldheap = vl_msg_push_heap ();
413 vl_input_queue =
414 svm_queue_alloc_and_init (input_queue_size, sizeof (uword), getpid ());
415 vl_msg_pop_heap (oldheap);
416
417 ctx->my_client_index = ~0;
418 ctx->vl_input_queue = vl_input_queue;
419
420 mp = vl_msg_api_alloc_as_if_client (sizeof (vl_api_memclnt_create_v2_t));
421 clib_memset (mp, 0, sizeof (*mp));
422 mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_V2);
423 mp->ctx_quota = ctx_quota;
424 mp->input_queue = (uword) vl_input_queue;
425 strncpy ((char *) mp->name, name, sizeof (mp->name) - 1);
426 mp->keepalive = keepalive;
427
428 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
429
430 while (1)
431 {
432 int qstatus;
433 struct timespec ts, tsrem;
434 int i;
435
436 /* Wait up to 10 seconds */
437 for (i = 0; i < 1000; i++)
438 {
439 qstatus =
440 svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0);
441 if (qstatus == 0)
442 goto read_one_msg;
443 ts.tv_sec = 0;
444 ts.tv_nsec = 10000 * 1000; /* 10 ms */
445 while (nanosleep (&ts, &tsrem) < 0)
446 ts = tsrem;
447 }
448 /* Timeout... */
449 return -1;
450
451 read_one_msg:
452 VL_MSG_API_UNPOISON (rp);
453 if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_V2_REPLY)
454 {
455 clib_warning ("unexpected reply: id %d", ntohs (rp->_vl_msg_id));
456 continue;
457 }
458 rv = clib_net_to_host_u32 (rp->response);
459 vapi_memclnt_create_v2_reply_t_handler (ctx, rp);
460 break;
461 }
462 return (rv);
463}
464
Mauro Sardara8c626b42022-12-12 14:04:09 +0000465static void
466vapi_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 api_main_t *am = vlibapi_get_main ();
471
472 ASSERT (am->vlib_rp);
473 shmem_hdr = am->shmem_hdr;
474 ASSERT (shmem_hdr && shmem_hdr->vl_input_queue);
475
476 mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_delete_t));
477 clib_memset (mp, 0, sizeof (*mp));
478 mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE);
479 mp->index = ctx->my_client_index;
480 mp->do_cleanup = do_cleanup;
481
482 vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
483}
484
485static int
486vapi_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 vl_input_queue = ctx->vl_input_queue;
494 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 begin = time (0);
501 while (1)
502 {
503 time_t now;
504
505 now = time (0);
506
507 if (now >= (begin + 2))
508 {
509 clib_warning ("peer unresponsive, give up");
510 ctx->my_client_index = ~0;
511 return -1;
512 }
513 if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
514 continue;
515
516 VL_MSG_API_UNPOISON (rp);
517
518 /* drain the queue */
519 if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
520 {
521 clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
522 msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
523 vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
524 continue;
525 }
526 msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
527 vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
528 break;
529 }
530
531 vapi_api_name_and_crc_free (ctx);
532 return 0;
533}
534
Ole Troan2ca88ff2022-01-27 16:25:43 +0100535u32
536vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc)
537{
538 uword *p;
539
540 if (ctx->msg_index_by_name_and_crc)
541 {
542 p = hash_get_mem (ctx->msg_index_by_name_and_crc, name_and_crc);
543 if (p)
544 return p[0];
545 }
546 return ~0;
547}
548
549vapi_error_e
550vapi_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
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200556 if (response_queue_size <= 0 || max_outstanding_requests <= 0)
557 {
558 return VAPI_EINVAL;
559 }
Klement Sekera7ff0a262018-09-03 12:35:27 +0200560 if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024 * 1024 * 32))
561 {
562 return VAPI_ENOMEM;
563 }
Ole Troan2ca88ff2022-01-27 16:25:43 +0100564
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200565 ctx->requests_size = max_outstanding_requests;
566 const size_t size = ctx->requests_size * sizeof (*ctx->requests);
567 void *tmp = realloc (ctx->requests, size);
568 if (!tmp)
569 {
570 return VAPI_ENOMEM;
571 }
572 ctx->requests = tmp;
Dave Barachb7b92992018-10-17 10:38:51 -0400573 clib_memset (ctx->requests, 0, size);
Chris Luke879ace32017-09-26 13:15:16 -0400574 /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200575 ctx->requests_start = ctx->requests_count = 0;
Ole Troan2ca88ff2022-01-27 16:25:43 +0100576
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200577 if (chroot_prefix)
578 {
579 VAPI_DBG ("set memory root path `%s'", chroot_prefix);
580 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);
Ole Troan2ca88ff2022-01-27 16:25:43 +0100584 if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200585 {
586 return VAPI_EMAP_FAIL;
587 }
588 VAPI_DBG ("connect client `%s'", name);
Ole Troan2ca88ff2022-01-27 16:25:43 +0100589 if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size, true) <
590 0)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200591 {
592 vl_client_api_unmap ();
593 return VAPI_ECON_FAIL;
594 }
595#if VAPI_DEBUG_CONNECT
596 VAPI_DBG ("start probing messages");
597#endif
Ole Troan2ca88ff2022-01-27 16:25:43 +0100598
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200599 int i;
600 for (i = 0; i < __vapi_metadata.count; ++i)
601 {
602 vapi_message_desc_t *m = __vapi_metadata.msgs[i];
603 u8 scratch[m->name_with_crc_len + 1];
604 memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
Ole Troan2ca88ff2022-01-27 16:25:43 +0100605 u32 id = vapi_api_get_msg_index (ctx, scratch);
606
Klement Sekeradab732a2018-07-04 13:43:46 +0200607 if (VAPI_INVALID_MSG_ID != id)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200608 {
609 if (id > UINT16_MAX)
610 {
611 VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
612 UINT16_MAX);
613 rv = VAPI_EINVAL;
614 goto fail;
615 }
616 if (id > ctx->vl_msg_id_max)
617 {
Ole Troan2ca88ff2022-01-27 16:25:43 +0100618 vapi_msg_id_t *tmp =
619 realloc (ctx->vl_msg_id_to_vapi_msg_t,
620 sizeof (*ctx->vl_msg_id_to_vapi_msg_t) * (id + 1));
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200621 if (!tmp)
622 {
623 rv = VAPI_ENOMEM;
624 goto fail;
625 }
626 ctx->vl_msg_id_to_vapi_msg_t = tmp;
627 ctx->vl_msg_id_max = id;
628 }
629 ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
630 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 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 if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
646 !vapi_is_msg_available (ctx, vapi_msg_id_control_ping_reply))
647 {
Ole Troan2ca88ff2022-01-27 16:25:43 +0100648 VAPI_ERR (
649 "control ping or control ping reply not available, cannot connect");
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200650 rv = VAPI_EINCOMPATIBLE;
651 goto fail;
652 }
653 ctx->mode = mode;
654 ctx->connected = true;
Klement Sekeradab732a2018-07-04 13:43:46 +0200655 if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
656 {
657 ctx->handle_keepalives = handle_keepalives;
658 }
659 else
660 {
661 ctx->handle_keepalives = false;
662 }
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200663 return VAPI_OK;
664fail:
Mauro Sardara8c626b42022-12-12 14:04:09 +0000665 vapi_client_disconnect (ctx);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200666 vl_client_api_unmap ();
667 return rv;
668}
669
Ole Troan2ca88ff2022-01-27 16:25:43 +0100670/*
671 * API client running in the same process as VPP
672 */
673vapi_error_e
674vapi_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 if (response_queue_size <= 0 || max_outstanding_requests <= 0)
681 {
682 return VAPI_EINVAL;
683 }
684
685 ctx->requests_size = max_outstanding_requests;
686 const size_t size = ctx->requests_size * sizeof (*ctx->requests);
687 void *tmp = realloc (ctx->requests, size);
688 if (!tmp)
689 {
690 return VAPI_ENOMEM;
691 }
692 ctx->requests = tmp;
693 clib_memset (ctx->requests, 0, size);
694 /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
695 ctx->requests_start = ctx->requests_count = 0;
696
697 VAPI_DBG ("connect client `%s'", name);
698 if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size,
699 handle_keepalives) < 0)
700 {
701 return VAPI_ECON_FAIL;
702 }
703
704 int i;
705 for (i = 0; i < __vapi_metadata.count; ++i)
706 {
707 vapi_message_desc_t *m = __vapi_metadata.msgs[i];
708 u8 scratch[m->name_with_crc_len + 1];
709 memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
710 u32 id = vapi_api_get_msg_index (ctx, scratch);
711 if (VAPI_INVALID_MSG_ID != id)
712 {
713 if (id > UINT16_MAX)
714 {
715 VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
716 UINT16_MAX);
717 rv = VAPI_EINVAL;
718 goto fail;
719 }
720 if (id > ctx->vl_msg_id_max)
721 {
722 vapi_msg_id_t *tmp =
723 realloc (ctx->vl_msg_id_to_vapi_msg_t,
724 sizeof (*ctx->vl_msg_id_to_vapi_msg_t) * (id + 1));
725 if (!tmp)
726 {
727 rv = VAPI_ENOMEM;
728 goto fail;
729 }
730 ctx->vl_msg_id_to_vapi_msg_t = tmp;
731 ctx->vl_msg_id_max = id;
732 }
733 ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
734 ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = id;
735 }
736 else
737 {
738 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 if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
743 !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 rv = VAPI_EINCOMPATIBLE;
748 goto fail;
749 }
750 ctx->mode = mode;
751 ctx->connected = true;
752 if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
753 {
754 ctx->handle_keepalives = handle_keepalives;
755 }
756 else
757 {
758 ctx->handle_keepalives = false;
759 }
760 return VAPI_OK;
761fail:
Mauro Sardara8c626b42022-12-12 14:04:09 +0000762 vapi_client_disconnect (ctx);
Ole Troan2ca88ff2022-01-27 16:25:43 +0100763 return rv;
764}
765
766vapi_error_e
767vapi_disconnect_from_vpp (vapi_ctx_t ctx)
768{
769 if (!ctx->connected)
770 {
771 return VAPI_EINVAL;
772 }
773 vl_api_memclnt_delete_reply_t *rp;
774 svm_queue_t *vl_input_queue;
775 time_t begin;
776 vl_input_queue = ctx->vl_input_queue;
Mauro Sardara8c626b42022-12-12 14:04:09 +0000777 vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
Ole Troan2ca88ff2022-01-27 16:25:43 +0100778
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 begin = time (0);
784 vapi_error_e rv = VAPI_OK;
785 while (1)
786 {
787 time_t now;
788
789 now = time (0);
790
791 if (now >= (begin + 2))
792 {
793 clib_warning ("peer unresponsive, give up");
794 ctx->my_client_index = ~0;
795 rv = VAPI_ENORESP;
796 goto fail;
797 }
798 if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
799 continue;
800
801 VL_MSG_API_UNPOISON (rp);
802
803 /* drain the queue */
804 if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
805 {
806 clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
807 vl_msg_api_free (rp);
808 continue;
809 }
810 vapi_memclnt_delete_reply_t_handler (
811 ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
812 break;
813 }
814fail:
815 vapi_api_name_and_crc_free (ctx);
816
817 ctx->connected = false;
818 return rv;
819}
820
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200821vapi_error_e
822vapi_disconnect (vapi_ctx_t ctx)
823{
824 if (!ctx->connected)
825 {
826 return VAPI_EINVAL;
827 }
Ole Troan2ca88ff2022-01-27 16:25:43 +0100828
829 vl_api_memclnt_delete_reply_t *rp;
830 svm_queue_t *vl_input_queue;
831 time_t begin;
832 vl_input_queue = ctx->vl_input_queue;
Mauro Sardara8c626b42022-12-12 14:04:09 +0000833 vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
Ole Troan2ca88ff2022-01-27 16:25:43 +0100834
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 begin = time (0);
840 vapi_error_e rv = VAPI_OK;
841 while (1)
842 {
843 time_t now;
844
845 now = time (0);
846
847 if (now >= (begin + 2))
848 {
849 clib_warning ("peer unresponsive, give up");
850 ctx->my_client_index = ~0;
851 rv = VAPI_ENORESP;
852 goto fail;
853 }
854 if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
855 continue;
856
857 VL_MSG_API_UNPOISON (rp);
858
859 /* drain the queue */
860 if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
861 {
862 clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
863 vl_msg_api_free (rp);
864 continue;
865 }
866 vapi_memclnt_delete_reply_t_handler (
867 ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
868 break;
869 }
870fail:
871 vapi_api_name_and_crc_free (ctx);
872
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200873 vl_client_api_unmap ();
874#if VAPI_DEBUG_ALLOC
875 vapi_to_be_freed_validate ();
876#endif
877 ctx->connected = false;
Ole Troan2ca88ff2022-01-27 16:25:43 +0100878 return rv;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200879}
880
881vapi_error_e
882vapi_get_fd (vapi_ctx_t ctx, int *fd)
883{
884 return VAPI_ENOTSUP;
885}
886
887vapi_error_e
888vapi_send (vapi_ctx_t ctx, void *msg)
889{
890 vapi_error_e rv = VAPI_OK;
891 if (!ctx || !msg || !ctx->connected)
892 {
893 rv = VAPI_EINVAL;
894 goto out;
895 }
896 int tmp;
Dave Barach39d69112019-11-27 11:42:13 -0500897 svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200898#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 {
Klement Sekeradc15be22017-06-12 06:49:33 +0200905 VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid,
906 __vapi_metadata.msgs[id]->name);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200907 }
908 else
909 {
Klement Sekeradc15be22017-06-12 06:49:33 +0200910 VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200911 }
912 }
913 else
914 {
Klement Sekeradc15be22017-06-12 06:49:33 +0200915 VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200916 }
917#endif
Florin Corase86a8ed2018-01-05 03:20:25 -0800918 tmp = svm_queue_add (q, (u8 *) & msg,
919 VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200920 if (tmp < 0)
921 {
922 rv = VAPI_EAGAIN;
923 }
Benoît Ganne9fb6d402019-04-15 15:28:21 +0200924 else
925 VL_MSG_API_POISON (msg);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200926out:
927 VAPI_DBG ("vapi_send() rv = %d", rv);
928 return rv;
929}
930
931vapi_error_e
932vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
933{
934 vapi_error_e rv = VAPI_OK;
935 if (!ctx || !msg1 || !msg2 || !ctx->connected)
936 {
937 rv = VAPI_EINVAL;
938 goto out;
939 }
Dave Barach39d69112019-11-27 11:42:13 -0500940 svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200941#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
Florin Corase86a8ed2018-01-05 03:20:25 -0800964 int tmp = svm_queue_add2 (q, (u8 *) & msg1, (u8 *) & msg2,
965 VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200966 if (tmp < 0)
967 {
968 rv = VAPI_EAGAIN;
969 }
Benoît Ganne9fb6d402019-04-15 15:28:21 +0200970 else
971 VL_MSG_API_POISON (msg1);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200972out:
973 VAPI_DBG ("vapi_send() rv = %d", rv);
974 return rv;
975}
976
977vapi_error_e
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100978vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size,
979 svm_q_conditional_wait_t cond, u32 time)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200980{
981 if (!ctx || !ctx->connected || !msg || !msg_size)
982 {
983 return VAPI_EINVAL;
984 }
985 vapi_error_e rv = VAPI_OK;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200986 uword data;
987
Ole Troan2ca88ff2022-01-27 16:25:43 +0100988 svm_queue_t *q = ctx->vl_input_queue;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200989
Klement Sekeradab732a2018-07-04 13:43:46 +0200990again:
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200991 VAPI_DBG ("doing shm queue sub");
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100992
993 int tmp = svm_queue_sub (q, (u8 *) & data, cond, time);
994
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200995 if (tmp == 0)
996 {
Benoît Ganne9fb6d402019-04-15 15:28:21 +0200997 VL_MSG_API_UNPOISON ((void *) data);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200998#if VAPI_DEBUG_ALLOC
999 vapi_add_to_be_freed ((void *) data);
1000#endif
1001 msgbuf_t *msgbuf =
1002 (msgbuf_t *) ((u8 *) data - offsetof (msgbuf_t, data));
1003 if (!msgbuf->data_len)
1004 {
1005 vapi_msg_free (ctx, (u8 *) data);
1006 return VAPI_EAGAIN;
1007 }
1008 *msg = (u8 *) data;
1009 *msg_size = ntohl (msgbuf->data_len);
Klement Sekeradc15be22017-06-12 06:49:33 +02001010#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
Klement Sekeradab732a2018-07-04 13:43:46 +02001030 if (ctx->handle_keepalives)
1031 {
1032 unsigned msgid = be16toh (*(u16 *) * msg);
1033 if (msgid ==
1034 vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive))
1035 {
1036 vapi_msg_memclnt_keepalive_reply *reply = NULL;
1037 do
1038 {
1039 reply = vapi_msg_alloc (ctx, sizeof (*reply));
1040 }
1041 while (!reply);
1042 reply->header.context = vapi_get_client_index (ctx);
1043 reply->header._vl_msg_id =
1044 vapi_lookup_vl_msg_id (ctx,
1045 vapi_msg_id_memclnt_keepalive_reply);
1046 reply->payload.retval = 0;
1047 vapi_msg_memclnt_keepalive_reply_hton (reply);
1048 while (VAPI_EAGAIN == vapi_send (ctx, reply));
1049 vapi_msg_free (ctx, *msg);
Klement Sekeradab732a2018-07-04 13:43:46 +02001050 goto again;
1051 }
1052 }
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001053 }
1054 else
1055 {
1056 rv = VAPI_EAGAIN;
1057 }
1058 return rv;
1059}
1060
1061vapi_error_e
Matthew Smith4b9935c2022-12-02 20:46:16 +00001062vapi_wait (vapi_ctx_t ctx)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001063{
Matthew Smith4b9935c2022-12-02 20:46:16 +00001064 svm_queue_lock (ctx->vl_input_queue);
1065 svm_queue_wait (ctx->vl_input_queue);
1066 svm_queue_unlock (ctx->vl_input_queue);
1067
1068 return VAPI_OK;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001069}
1070
1071static vapi_error_e
1072vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id,
1073 u32 context, void *msg)
1074{
1075 int mrv;
1076 if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
1077 {
1078 VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
1079 return VAPI_MUTEX_FAILURE;
1080 }
1081 int tmp = ctx->requests_start;
1082 const int requests_end = vapi_requests_end (ctx);
1083 while (ctx->requests[tmp].context != context && tmp != requests_end)
1084 {
1085 ++tmp;
1086 if (tmp == ctx->requests_size)
1087 {
1088 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 vapi_error_e rv = VAPI_OK;
1095 if (ctx->requests[tmp].context == context)
1096 {
1097 while (ctx->requests_start != tmp)
1098 {
1099 VAPI_ERR ("No response to req with context=%u",
1100 (unsigned) ctx->requests[tmp].context);
Klement Sekeradab732a2018-07-04 13:43:46 +02001101 ctx->requests[ctx->requests_start].callback (ctx, ctx->requests
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001102 [ctx->
1103 requests_start].callback_ctx,
1104 VAPI_ENORESP, true,
1105 NULL);
Dave Barachb7b92992018-10-17 10:38:51 -04001106 clib_memset (&ctx->requests[ctx->requests_start], 0,
1107 sizeof (ctx->requests[ctx->requests_start]));
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001108 ++ctx->requests_start;
1109 --ctx->requests_count;
1110 if (ctx->requests_start == ctx->requests_size)
1111 {
1112 ctx->requests_start = 0;
1113 }
1114 }
1115 // now ctx->requests_start == tmp
1116 int payload_offset = vapi_get_payload_offset (id);
1117 void *payload = ((u8 *) msg) + payload_offset;
1118 bool is_last = true;
1119 if (ctx->requests[tmp].is_dump)
1120 {
1121 if (vapi_msg_id_control_ping_reply == id)
1122 {
1123 payload = NULL;
1124 }
1125 else
1126 {
1127 is_last = false;
1128 }
1129 }
1130 if (payload_offset != -1)
1131 {
Ole Troan2ca88ff2022-01-27 16:25:43 +01001132 rv = ctx->requests[tmp].callback (
1133 ctx, ctx->requests[tmp].callback_ctx, VAPI_OK, is_last, payload);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001134 }
1135 else
1136 {
1137 /* this is a message without payload, so bend the callback a little
1138 */
1139 rv =
1140 ((vapi_error_e (*)(vapi_ctx_t, void *, vapi_error_e, bool))
1141 ctx->requests[tmp].callback) (ctx,
1142 ctx->requests[tmp].callback_ctx,
1143 VAPI_OK, is_last);
1144 }
1145 if (is_last)
1146 {
Dave Barachb7b92992018-10-17 10:38:51 -04001147 clib_memset (&ctx->requests[ctx->requests_start], 0,
1148 sizeof (ctx->requests[ctx->requests_start]));
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001149 ++ctx->requests_start;
1150 --ctx->requests_count;
1151 if (ctx->requests_start == ctx->requests_size)
1152 {
1153 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 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 abort (); /* this really shouldn't happen */
1164 }
1165 return rv;
1166}
1167
1168static vapi_error_e
1169vapi_dispatch_event (vapi_ctx_t ctx, vapi_msg_id_t id, void *msg)
1170{
1171 if (ctx->event_cbs[id].cb)
1172 {
1173 return ctx->event_cbs[id].cb (ctx, ctx->event_cbs[id].ctx, msg);
1174 }
1175 else if (ctx->generic_cb.cb)
1176 {
1177 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 return VAPI_OK;
1186}
1187
Klement Sekeradc15be22017-06-12 06:49:33 +02001188bool
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001189vapi_msg_is_with_context (vapi_msg_id_t id)
1190{
1191 assert (id <= __vapi_metadata.count);
1192 return __vapi_metadata.msgs[id]->has_context;
1193}
1194
Klement Sekeraa25ce962021-11-15 15:52:37 +01001195static int
1196vapi_verify_msg_size (vapi_msg_id_t id, void *buf, uword buf_size)
1197{
1198 assert (id < __vapi_metadata.count);
1199 return __vapi_metadata.msgs[id]->verify_msg_size (buf, buf_size);
1200}
1201
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001202vapi_error_e
1203vapi_dispatch_one (vapi_ctx_t ctx)
1204{
1205 VAPI_DBG ("vapi_dispatch_one()");
1206 void *msg;
Klement Sekeraa25ce962021-11-15 15:52:37 +01001207 uword size;
Klement Sekera5f0106a2022-01-24 21:37:09 +00001208 svm_q_conditional_wait_t cond =
1209 vapi_is_nonblocking (ctx) ? SVM_Q_NOWAIT : SVM_Q_WAIT;
1210 vapi_error_e rv = vapi_recv (ctx, &msg, &size, cond, 0);
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001211 if (VAPI_OK != rv)
1212 {
1213 VAPI_DBG ("vapi_recv failed with rv=%d", rv);
1214 return rv;
1215 }
1216 u16 vpp_id = be16toh (*(u16 *) msg);
1217 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 vapi_msg_free (ctx, msg);
1222 return VAPI_EINVAL;
1223 }
Klement Sekeradab732a2018-07-04 13:43:46 +02001224 if (VAPI_INVALID_MSG_ID == (unsigned) ctx->vl_msg_id_to_vapi_msg_t[vpp_id])
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001225 {
1226 VAPI_ERR ("Unknown msg ID received, id `%u' marked as not supported",
1227 (unsigned) vpp_id);
1228 vapi_msg_free (ctx, msg);
1229 return VAPI_EINVAL;
1230 }
1231 const vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[vpp_id];
Klement Sekeraa25ce962021-11-15 15:52:37 +01001232 if (vapi_verify_msg_size (id, msg, size))
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001233 {
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001234 vapi_msg_free (ctx, msg);
1235 return VAPI_EINVAL;
1236 }
1237 u32 context;
1238 vapi_get_swap_to_host_func (id) (msg);
1239 if (vapi_msg_is_with_context (id))
1240 {
1241 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 if (context & context_counter_mask)
1245 {
1246 rv = vapi_dispatch_response (ctx, id, context, msg);
1247 goto done;
1248 }
1249 }
1250 rv = vapi_dispatch_event (ctx, id, msg);
1251
1252done:
1253 vapi_msg_free (ctx, msg);
1254 return rv;
1255}
1256
1257vapi_error_e
1258vapi_dispatch (vapi_ctx_t ctx)
1259{
1260 vapi_error_e rv = VAPI_OK;
1261 while (!vapi_requests_empty (ctx))
1262 {
1263 rv = vapi_dispatch_one (ctx);
1264 if (VAPI_OK != rv)
1265 {
1266 return rv;
1267 }
1268 }
1269 return rv;
1270}
1271
1272void
1273vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
1274 vapi_event_cb callback, void *callback_ctx)
1275{
1276 vapi_event_cb_with_ctx *c = &ctx->event_cbs[id];
1277 c->cb = callback;
1278 c->ctx = callback_ctx;
1279}
1280
1281void
1282vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id)
1283{
1284 vapi_set_event_cb (ctx, id, NULL, NULL);
1285}
1286
1287void
1288vapi_set_generic_event_cb (vapi_ctx_t ctx, vapi_generic_event_cb callback,
1289 void *callback_ctx)
1290{
1291 ctx->generic_cb.cb = callback;
1292 ctx->generic_cb.ctx = callback_ctx;
1293}
1294
1295void
1296vapi_clear_generic_event_cb (vapi_ctx_t ctx)
1297{
1298 ctx->generic_cb.cb = NULL;
1299 ctx->generic_cb.ctx = NULL;
1300}
1301
1302u16
1303vapi_lookup_vl_msg_id (vapi_ctx_t ctx, vapi_msg_id_t id)
1304{
1305 assert (id < __vapi_metadata.count);
1306 return ctx->vapi_msg_id_t_to_vl_msg_id[id];
1307}
1308
1309int
1310vapi_get_client_index (vapi_ctx_t ctx)
1311{
Ole Troan2ca88ff2022-01-27 16:25:43 +01001312 return ctx->my_client_index;
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001313}
1314
1315bool
1316vapi_is_nonblocking (vapi_ctx_t ctx)
1317{
1318 return (VAPI_MODE_NONBLOCKING == ctx->mode);
1319}
1320
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001321size_t
1322vapi_get_max_request_count (vapi_ctx_t ctx)
1323{
1324 return ctx->requests_size - 1;
1325}
1326
1327int
1328vapi_get_payload_offset (vapi_msg_id_t id)
1329{
1330 assert (id < __vapi_metadata.count);
1331 return __vapi_metadata.msgs[id]->payload_offset;
1332}
1333
1334void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *msg)
1335{
1336 assert (id < __vapi_metadata.count);
1337 return __vapi_metadata.msgs[id]->swap_to_host;
1338}
1339
1340void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *msg)
1341{
1342 assert (id < __vapi_metadata.count);
1343 return __vapi_metadata.msgs[id]->swap_to_be;
1344}
1345
1346size_t
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001347vapi_get_context_offset (vapi_msg_id_t id)
1348{
1349 assert (id < __vapi_metadata.count);
1350 return __vapi_metadata.msgs[id]->context_offset;
1351}
1352
1353vapi_msg_id_t
1354vapi_register_msg (vapi_message_desc_t * msg)
1355{
1356 int i = 0;
1357 for (i = 0; i < __vapi_metadata.count; ++i)
1358 {
1359 if (!strcmp
1360 (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 msg->id = __vapi_metadata.msgs[i]->id;
1366 return msg->id;
1367 }
1368 }
1369 vapi_msg_id_t id = __vapi_metadata.count;
1370 ++__vapi_metadata.count;
1371 __vapi_metadata.msgs =
1372 realloc (__vapi_metadata.msgs,
1373 sizeof (*__vapi_metadata.msgs) * __vapi_metadata.count);
1374 __vapi_metadata.msgs[id] = msg;
1375 size_t s = strlen (msg->name_with_crc);
1376 if (s > __vapi_metadata.max_len_name_with_crc)
1377 {
1378 __vapi_metadata.max_len_name_with_crc = s;
1379 }
1380 msg->id = id;
1381 return id;
1382}
1383
1384vapi_error_e
1385vapi_producer_lock (vapi_ctx_t ctx)
1386{
1387 int mrv;
1388 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 return VAPI_MUTEX_FAILURE;
1393 }
1394 return VAPI_OK;
1395}
1396
1397vapi_error_e
1398vapi_producer_unlock (vapi_ctx_t ctx)
1399{
1400 int mrv;
1401 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 return VAPI_MUTEX_FAILURE;
1407 }
1408 return VAPI_OK;
1409}
1410
Klement Sekeradc15be22017-06-12 06:49:33 +02001411size_t
1412vapi_get_message_count ()
1413{
1414 return __vapi_metadata.count;
1415}
1416
1417const char *
1418vapi_get_msg_name (vapi_msg_id_t id)
1419{
1420 return __vapi_metadata.msgs[id]->name;
1421}
1422
Matthew Smith57f177d2022-12-15 22:18:08 +00001423void
1424vapi_stop_rx_thread (vapi_ctx_t ctx)
1425{
1426 if (!ctx || !ctx->connected || !ctx->vl_input_queue)
1427 {
1428 return;
1429 }
1430
1431 vl_client_stop_rx_thread (ctx->vl_input_queue);
1432}
Klement Sekera8f2a4ea2017-05-04 06:15:18 +02001433/*
1434 * fd.io coding-style-patch-verification: ON
1435 *
1436 * Local Variables:
1437 * eval: (c-set-style "gnu")
1438 * End:
1439 */