b74570d10da669efef4c837e1762eca6057bba65
[vpp.git] / src / vcl / vppcom.c
1 /*
2  * Copyright (c) 2017-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <vcl/vppcom.h>
19 #include <vcl/vcl_debug.h>
20 #include <vcl/vcl_private.h>
21 #include <svm/fifo_segment.h>
22
23 __thread uword __vcl_worker_index = ~0;
24
25 static inline int
26 vcl_mq_dequeue_batch (vcl_worker_t * wrk, svm_msg_q_t * mq, u32 n_max_msg)
27 {
28   u32 n_msgs = 0, sz, len;
29
30   while ((sz = svm_msg_q_size (mq)))
31     {
32       len = vec_len (wrk->mq_msg_vector);
33       vec_validate (wrk->mq_msg_vector, len + sz - 1);
34       svm_msg_q_sub_raw_batch (mq, wrk->mq_msg_vector + len, sz);
35       n_msgs += sz;
36     }
37   return n_msgs;
38 }
39
40 const char *
41 vppcom_session_state_str (vcl_session_state_t state)
42 {
43   char *st;
44
45   switch (state)
46     {
47     case VCL_STATE_CLOSED:
48       st = "STATE_CLOSED";
49       break;
50     case VCL_STATE_LISTEN:
51       st = "STATE_LISTEN";
52       break;
53     case VCL_STATE_READY:
54       st = "STATE_READY";
55       break;
56     case VCL_STATE_VPP_CLOSING:
57       st = "STATE_VPP_CLOSING";
58       break;
59     case VCL_STATE_DISCONNECT:
60       st = "STATE_DISCONNECT";
61       break;
62     case VCL_STATE_DETACHED:
63       st = "STATE_DETACHED";
64       break;
65     case VCL_STATE_UPDATED:
66       st = "STATE_UPDATED";
67       break;
68     case VCL_STATE_LISTEN_NO_MQ:
69       st = "STATE_LISTEN_NO_MQ";
70       break;
71     default:
72       st = "UNKNOWN_STATE";
73       break;
74     }
75
76   return st;
77 }
78
79 u8 *
80 format_ip4_address (u8 * s, va_list * args)
81 {
82   u8 *a = va_arg (*args, u8 *);
83   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
84 }
85
86 u8 *
87 format_ip6_address (u8 * s, va_list * args)
88 {
89   ip6_address_t *a = va_arg (*args, ip6_address_t *);
90   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
91
92   i_max_n_zero = ARRAY_LEN (a->as_u16);
93   max_n_zeros = 0;
94   i_first_zero = i_max_n_zero;
95   n_zeros = 0;
96   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
97     {
98       u32 is_zero = a->as_u16[i] == 0;
99       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
100         {
101           i_first_zero = i;
102           n_zeros = 0;
103         }
104       n_zeros += is_zero;
105       if ((!is_zero && n_zeros > max_n_zeros)
106           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
107         {
108           i_max_n_zero = i_first_zero;
109           max_n_zeros = n_zeros;
110           i_first_zero = ARRAY_LEN (a->as_u16);
111           n_zeros = 0;
112         }
113     }
114
115   last_double_colon = 0;
116   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
117     {
118       if (i == i_max_n_zero && max_n_zeros > 1)
119         {
120           s = format (s, "::");
121           i += max_n_zeros - 1;
122           last_double_colon = 1;
123         }
124       else
125         {
126           s = format (s, "%s%x",
127                       (last_double_colon || i == 0) ? "" : ":",
128                       clib_net_to_host_u16 (a->as_u16[i]));
129           last_double_colon = 0;
130         }
131     }
132
133   return s;
134 }
135
136 /* Format an IP46 address. */
137 u8 *
138 format_ip46_address (u8 * s, va_list * args)
139 {
140   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
141   ip46_type_t type = va_arg (*args, ip46_type_t);
142   int is_ip4 = 1;
143
144   switch (type)
145     {
146     case IP46_TYPE_ANY:
147       is_ip4 = ip46_address_is_ip4 (ip46);
148       break;
149     case IP46_TYPE_IP4:
150       is_ip4 = 1;
151       break;
152     case IP46_TYPE_IP6:
153       is_ip4 = 0;
154       break;
155     }
156
157   return is_ip4 ?
158     format (s, "%U", format_ip4_address, &ip46->ip4) :
159     format (s, "%U", format_ip6_address, &ip46->ip6);
160 }
161
162 /*
163  * VPPCOM Utility Functions
164  */
165
166 static void
167 vcl_msg_add_ext_config (vcl_session_t *s, uword *offset)
168 {
169   svm_fifo_chunk_t *c;
170
171   c = vcl_segment_alloc_chunk (vcl_vpp_worker_segment_handle (0),
172                                0 /* one slice only */, s->ext_config->len,
173                                offset);
174   if (c)
175     clib_memcpy_fast (c->data, s->ext_config, s->ext_config->len);
176 }
177
178 static void
179 vcl_send_session_listen (vcl_worker_t * wrk, vcl_session_t * s)
180 {
181   app_session_evt_t _app_evt, *app_evt = &_app_evt;
182   session_listen_msg_t *mp;
183   svm_msg_q_t *mq;
184
185   mq = vcl_worker_ctrl_mq (wrk);
186   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_LISTEN);
187   mp = (session_listen_msg_t *) app_evt->evt->data;
188   memset (mp, 0, sizeof (*mp));
189   mp->client_index = wrk->api_client_handle;
190   mp->context = s->session_index;
191   mp->wrk_index = wrk->vpp_wrk_index;
192   mp->is_ip4 = s->transport.is_ip4;
193   clib_memcpy_fast (&mp->ip, &s->transport.lcl_ip, sizeof (mp->ip));
194   mp->port = s->transport.lcl_port;
195   mp->proto = s->session_type;
196   mp->vrf = s->vrf;
197   if (s->flags & VCL_SESSION_F_CONNECTED)
198     mp->flags = TRANSPORT_CFG_F_CONNECTED;
199   if (s->ext_config)
200     vcl_msg_add_ext_config (s, &mp->ext_config);
201   app_send_ctrl_evt_to_vpp (mq, app_evt);
202   if (s->ext_config)
203     {
204       clib_mem_free (s->ext_config);
205       s->ext_config = 0;
206     }
207 }
208
209 static void
210 vcl_send_session_connect (vcl_worker_t * wrk, vcl_session_t * s)
211 {
212   app_session_evt_t _app_evt, *app_evt = &_app_evt;
213   session_connect_msg_t *mp;
214   svm_msg_q_t *mq;
215
216   mq = vcl_worker_ctrl_mq (wrk);
217   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_CONNECT);
218   mp = (session_connect_msg_t *) app_evt->evt->data;
219   memset (mp, 0, sizeof (*mp));
220   mp->client_index = wrk->api_client_handle;
221   mp->context = s->session_index;
222   mp->wrk_index = wrk->vpp_wrk_index;
223   mp->is_ip4 = s->transport.is_ip4;
224   mp->parent_handle = s->parent_handle;
225   clib_memcpy_fast (&mp->ip, &s->transport.rmt_ip, sizeof (mp->ip));
226   clib_memcpy_fast (&mp->lcl_ip, &s->transport.lcl_ip, sizeof (mp->lcl_ip));
227   mp->port = s->transport.rmt_port;
228   mp->lcl_port = s->transport.lcl_port;
229   mp->proto = s->session_type;
230   mp->vrf = s->vrf;
231   if (s->flags & VCL_SESSION_F_CONNECTED)
232     mp->flags |= TRANSPORT_CFG_F_CONNECTED;
233   if (s->ext_config)
234     vcl_msg_add_ext_config (s, &mp->ext_config);
235   app_send_ctrl_evt_to_vpp (mq, app_evt);
236
237   if (s->ext_config)
238     {
239       clib_mem_free (s->ext_config);
240       s->ext_config = 0;
241     }
242 }
243
244 void
245 vcl_send_session_unlisten (vcl_worker_t * wrk, vcl_session_t * s)
246 {
247   app_session_evt_t _app_evt, *app_evt = &_app_evt;
248   session_unlisten_msg_t *mp;
249   svm_msg_q_t *mq;
250
251   mq = vcl_worker_ctrl_mq (wrk);
252   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_UNLISTEN);
253   mp = (session_unlisten_msg_t *) app_evt->evt->data;
254   memset (mp, 0, sizeof (*mp));
255   mp->client_index = wrk->api_client_handle;
256   mp->wrk_index = wrk->vpp_wrk_index;
257   mp->handle = s->vpp_handle;
258   mp->context = wrk->wrk_index;
259   app_send_ctrl_evt_to_vpp (mq, app_evt);
260 }
261
262 static void
263 vcl_send_session_shutdown (vcl_worker_t *wrk, vcl_session_t *s)
264 {
265   app_session_evt_t _app_evt, *app_evt = &_app_evt;
266   session_shutdown_msg_t *mp;
267   svm_msg_q_t *mq;
268
269   /* Send to thread that owns the session */
270   mq = s->vpp_evt_q;
271   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_SHUTDOWN);
272   mp = (session_shutdown_msg_t *) app_evt->evt->data;
273   memset (mp, 0, sizeof (*mp));
274   mp->client_index = wrk->api_client_handle;
275   mp->handle = s->vpp_handle;
276   app_send_ctrl_evt_to_vpp (mq, app_evt);
277 }
278
279 static void
280 vcl_send_session_disconnect (vcl_worker_t * wrk, vcl_session_t * s)
281 {
282   app_session_evt_t _app_evt, *app_evt = &_app_evt;
283   session_disconnect_msg_t *mp;
284   svm_msg_q_t *mq;
285
286   /* Send to thread that owns the session */
287   mq = s->vpp_evt_q;
288   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_DISCONNECT);
289   mp = (session_disconnect_msg_t *) app_evt->evt->data;
290   memset (mp, 0, sizeof (*mp));
291   mp->client_index = wrk->api_client_handle;
292   mp->handle = s->vpp_handle;
293   app_send_ctrl_evt_to_vpp (mq, app_evt);
294 }
295
296 static void
297 vcl_send_app_detach (vcl_worker_t * wrk)
298 {
299   app_session_evt_t _app_evt, *app_evt = &_app_evt;
300   session_app_detach_msg_t *mp;
301   svm_msg_q_t *mq;
302
303   mq = vcl_worker_ctrl_mq (wrk);
304   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_APP_DETACH);
305   mp = (session_app_detach_msg_t *) app_evt->evt->data;
306   memset (mp, 0, sizeof (*mp));
307   mp->client_index = wrk->api_client_handle;
308   app_send_ctrl_evt_to_vpp (mq, app_evt);
309 }
310
311 static void
312 vcl_send_session_accepted_reply (svm_msg_q_t * mq, u32 context,
313                                  session_handle_t handle, int retval)
314 {
315   app_session_evt_t _app_evt, *app_evt = &_app_evt;
316   session_accepted_reply_msg_t *rmp;
317   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_ACCEPTED_REPLY);
318   rmp = (session_accepted_reply_msg_t *) app_evt->evt->data;
319   rmp->handle = handle;
320   rmp->context = context;
321   rmp->retval = retval;
322   app_send_ctrl_evt_to_vpp (mq, app_evt);
323 }
324
325 static void
326 vcl_send_session_disconnected_reply (vcl_worker_t * wrk, vcl_session_t * s,
327                                      int retval)
328 {
329   app_session_evt_t _app_evt, *app_evt = &_app_evt;
330   session_disconnected_reply_msg_t *rmp;
331   app_alloc_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt,
332                              SESSION_CTRL_EVT_DISCONNECTED_REPLY);
333   rmp = (session_disconnected_reply_msg_t *) app_evt->evt->data;
334   rmp->handle = s->vpp_handle;
335   rmp->context = wrk->api_client_handle;
336   rmp->retval = retval;
337   app_send_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt);
338 }
339
340 static void
341 vcl_send_session_reset_reply (vcl_worker_t * wrk, vcl_session_t * s,
342                               int retval)
343 {
344   app_session_evt_t _app_evt, *app_evt = &_app_evt;
345   session_reset_reply_msg_t *rmp;
346   app_alloc_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt,
347                              SESSION_CTRL_EVT_RESET_REPLY);
348   rmp = (session_reset_reply_msg_t *) app_evt->evt->data;
349   rmp->handle = s->vpp_handle;
350   rmp->context = wrk->api_client_handle;
351   rmp->retval = retval;
352   app_send_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt);
353 }
354
355 void
356 vcl_send_session_worker_update (vcl_worker_t * wrk, vcl_session_t * s,
357                                 u32 wrk_index)
358 {
359   app_session_evt_t _app_evt, *app_evt = &_app_evt;
360   session_worker_update_msg_t *mp;
361
362   app_alloc_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt,
363                              SESSION_CTRL_EVT_WORKER_UPDATE);
364   mp = (session_worker_update_msg_t *) app_evt->evt->data;
365   mp->client_index = wrk->api_client_handle;
366   mp->handle = s->vpp_handle;
367   mp->req_wrk_index = wrk->vpp_wrk_index;
368   mp->wrk_index = wrk_index;
369   app_send_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt);
370 }
371
372 int
373 vcl_send_worker_rpc (u32 dst_wrk_index, void *data, u32 data_len)
374 {
375   app_session_evt_t _app_evt, *app_evt = &_app_evt;
376   session_app_wrk_rpc_msg_t *mp;
377   vcl_worker_t *dst_wrk, *wrk;
378   svm_msg_q_t *mq;
379   int ret = -1;
380
381   if (data_len > sizeof (mp->data))
382     goto done;
383
384   clib_spinlock_lock (&vcm->workers_lock);
385
386   dst_wrk = vcl_worker_get_if_valid (dst_wrk_index);
387   if (!dst_wrk)
388     goto done;
389
390   wrk = vcl_worker_get_current ();
391   mq = vcl_worker_ctrl_mq (wrk);
392   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_APP_WRK_RPC);
393   mp = (session_app_wrk_rpc_msg_t *) app_evt->evt->data;
394   mp->client_index = wrk->api_client_handle;
395   mp->wrk_index = dst_wrk->vpp_wrk_index;
396   clib_memcpy (mp->data, data, data_len);
397   app_send_ctrl_evt_to_vpp (mq, app_evt);
398   ret = 0;
399
400 done:
401   clib_spinlock_unlock (&vcm->workers_lock);
402   return ret;
403 }
404
405 int
406 vcl_session_transport_attr (vcl_worker_t *wrk, vcl_session_t *s, u8 is_get,
407                             transport_endpt_attr_t *attr)
408 {
409   app_session_evt_t _app_evt, *app_evt = &_app_evt;
410   session_transport_attr_msg_t *mp;
411   svm_msg_q_t *mq;
412   f64 timeout;
413
414   ASSERT (!wrk->session_attr_op);
415   wrk->session_attr_op = 1;
416   wrk->session_attr_op_rv = -1;
417
418   mq = s->vpp_evt_q;
419   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_TRANSPORT_ATTR);
420   mp = (session_transport_attr_msg_t *) app_evt->evt->data;
421   memset (mp, 0, sizeof (*mp));
422   mp->client_index = wrk->api_client_handle;
423   mp->handle = s->vpp_handle;
424   mp->is_get = is_get;
425   mp->attr = *attr;
426   app_send_ctrl_evt_to_vpp (mq, app_evt);
427
428   timeout = clib_time_now (&wrk->clib_time) + 1;
429
430   while (wrk->session_attr_op && clib_time_now (&wrk->clib_time) < timeout)
431     vcl_flush_mq_events ();
432
433   if (!wrk->session_attr_op_rv && is_get)
434     *attr = wrk->session_attr_rv;
435
436   wrk->session_attr_op = 0;
437
438   return wrk->session_attr_op_rv;
439 }
440
441 static u32
442 vcl_session_accepted_handler (vcl_worker_t * wrk, session_accepted_msg_t * mp,
443                               u32 ls_index)
444 {
445   vcl_session_t *session, *listen_session;
446   svm_msg_q_t *evt_q;
447
448   session = vcl_session_alloc (wrk);
449
450   listen_session = vcl_session_get (wrk, ls_index);
451   if (listen_session->vpp_handle != mp->listener_handle)
452     {
453       VDBG (0, "ERROR: listener handle %lu does not match session %u",
454             mp->listener_handle, ls_index);
455       goto error;
456     }
457
458   if (vcl_segment_attach_session (mp->segment_handle, mp->server_rx_fifo,
459                                   mp->server_tx_fifo,
460                                   mp->vpp_event_queue_address, 0, session))
461     {
462       VDBG (0, "failed to attach fifos for %u", session->session_index);
463       goto error;
464     }
465
466   session->vpp_handle = mp->handle;
467   session->session_state = VCL_STATE_READY;
468   session->transport.rmt_port = mp->rmt.port;
469   session->transport.is_ip4 = mp->rmt.is_ip4;
470   clib_memcpy_fast (&session->transport.rmt_ip, &mp->rmt.ip,
471                     sizeof (ip46_address_t));
472
473   vcl_session_table_add_vpp_handle (wrk, mp->handle, session->session_index);
474   session->transport.lcl_port = mp->lcl.port;
475   session->transport.lcl_ip = mp->lcl.ip;
476   session->session_type = listen_session->session_type;
477   session->is_dgram = vcl_proto_is_dgram (session->session_type);
478   session->listener_index = listen_session->session_index;
479   listen_session->n_accepted_sessions++;
480
481   VDBG (1, "session %u [0x%llx]: client accept request from %s address %U"
482         " port %d queue %p!", session->session_index, mp->handle,
483         mp->rmt.is_ip4 ? "IPv4" : "IPv6", format_ip46_address, &mp->rmt.ip,
484         mp->rmt.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
485         clib_net_to_host_u16 (mp->rmt.port), session->vpp_evt_q);
486   vcl_evt (VCL_EVT_ACCEPT, session, listen_session, session_index);
487
488   vcl_send_session_accepted_reply (session->vpp_evt_q, mp->context,
489                                    session->vpp_handle, 0);
490
491   return session->session_index;
492
493 error:
494   vcl_segment_attach_mq (vcl_vpp_worker_segment_handle (0),
495                          mp->vpp_event_queue_address, mp->mq_index, &evt_q);
496   vcl_send_session_accepted_reply (evt_q, mp->context, mp->handle,
497                                    VNET_API_ERROR_INVALID_ARGUMENT);
498   vcl_session_free (wrk, session);
499   return VCL_INVALID_SESSION_INDEX;
500 }
501
502 static u32
503 vcl_session_connected_handler (vcl_worker_t * wrk,
504                                session_connected_msg_t * mp)
505 {
506   vcl_session_t *session = 0;
507   u32 session_index;
508
509   session_index = mp->context;
510   session = vcl_session_get (wrk, session_index);
511   if (!session)
512     {
513       VDBG (0, "ERROR: vpp handle 0x%llx has no session index (%u)!",
514             mp->handle, session_index);
515       return VCL_INVALID_SESSION_INDEX;
516     }
517   if (mp->retval)
518     {
519       VDBG (0, "ERROR: session index %u: connect failed! %U",
520             session_index, format_session_error, mp->retval);
521       session->session_state = VCL_STATE_DETACHED;
522       session->vpp_handle = mp->handle;
523       return session_index;
524     }
525
526   session->vpp_handle = mp->handle;
527
528   if (vcl_segment_attach_session (mp->segment_handle, mp->server_rx_fifo,
529                                   mp->server_tx_fifo,
530                                   mp->vpp_event_queue_address, 0, session))
531     {
532       VDBG (0, "failed to attach fifos for %u", session->session_index);
533       session->session_state = VCL_STATE_DETACHED;
534       vcl_send_session_disconnect (wrk, session);
535       return session_index;
536     }
537
538   if (mp->ct_rx_fifo)
539     {
540       if (vcl_segment_attach_session (mp->ct_segment_handle, mp->ct_rx_fifo,
541                                       mp->ct_tx_fifo, (uword) ~0, 1, session))
542         {
543           VDBG (0, "failed to attach ct fifos for %u", session->session_index);
544           session->session_state = VCL_STATE_DETACHED;
545           vcl_send_session_disconnect (wrk, session);
546           return session_index;
547         }
548     }
549
550   session->transport.is_ip4 = mp->lcl.is_ip4;
551   clib_memcpy_fast (&session->transport.lcl_ip, &mp->lcl.ip,
552                     sizeof (session->transport.lcl_ip));
553   session->transport.lcl_port = mp->lcl.port;
554
555   /* Application closed session before connect reply */
556   if (vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK)
557       && session->session_state == VCL_STATE_CLOSED)
558     vcl_send_session_disconnect (wrk, session);
559   else
560     session->session_state = VCL_STATE_READY;
561
562   /* Add it to lookup table */
563   vcl_session_table_add_vpp_handle (wrk, mp->handle, session_index);
564
565   VDBG (1, "session %u [0x%llx] connected! rx_fifo %p, refcnt %d, tx_fifo %p,"
566         " refcnt %d", session_index, mp->handle, session->rx_fifo,
567         session->rx_fifo->refcnt, session->tx_fifo, session->tx_fifo->refcnt);
568
569   return session_index;
570 }
571
572 static int
573 vcl_flag_accepted_session (vcl_session_t * session, u64 handle, u32 flags)
574 {
575   vcl_session_msg_t *accepted_msg;
576   int i;
577
578   for (i = 0; i < vec_len (session->accept_evts_fifo); i++)
579     {
580       accepted_msg = &session->accept_evts_fifo[i];
581       if (accepted_msg->accepted_msg.handle == handle)
582         {
583           accepted_msg->flags |= flags;
584           return 1;
585         }
586     }
587   return 0;
588 }
589
590 static u32
591 vcl_session_reset_handler (vcl_worker_t * wrk,
592                            session_reset_msg_t * reset_msg)
593 {
594   vcl_session_t *session;
595   u32 sid;
596
597   sid = vcl_session_index_from_vpp_handle (wrk, reset_msg->handle);
598   session = vcl_session_get (wrk, sid);
599   if (!session)
600     {
601       VDBG (0, "request to reset unknown handle 0x%llx", reset_msg->handle);
602       return VCL_INVALID_SESSION_INDEX;
603     }
604
605   /* Caught a reset before actually accepting the session */
606   if (session->session_state == VCL_STATE_LISTEN)
607     {
608
609       if (!vcl_flag_accepted_session (session, reset_msg->handle,
610                                       VCL_ACCEPTED_F_RESET))
611         VDBG (0, "session was not accepted!");
612       return VCL_INVALID_SESSION_INDEX;
613     }
614
615   if (session->session_state != VCL_STATE_CLOSED)
616     session->session_state = VCL_STATE_DISCONNECT;
617   VDBG (0, "reset session %u [0x%llx]", sid, reset_msg->handle);
618   return sid;
619 }
620
621 static u32
622 vcl_session_bound_handler (vcl_worker_t * wrk, session_bound_msg_t * mp)
623 {
624   vcl_session_t *session;
625   u32 sid = mp->context;
626
627   session = vcl_session_get (wrk, sid);
628   if (mp->retval)
629     {
630       VERR ("session %u [0x%llx]: bind failed: %U", sid, mp->handle,
631             format_session_error, mp->retval);
632       if (session)
633         {
634           session->session_state = VCL_STATE_DETACHED;
635           session->vpp_handle = mp->handle;
636           return sid;
637         }
638       else
639         {
640           VDBG (0, "ERROR: session %u [0x%llx]: Invalid session index!",
641                 sid, mp->handle);
642           return VCL_INVALID_SESSION_INDEX;
643         }
644     }
645
646   session->vpp_handle = mp->handle;
647   session->transport.is_ip4 = mp->lcl_is_ip4;
648   clib_memcpy_fast (&session->transport.lcl_ip, mp->lcl_ip,
649                     sizeof (ip46_address_t));
650   session->transport.lcl_port = mp->lcl_port;
651   vcl_session_table_add_listener (wrk, mp->handle, sid);
652   session->session_state = VCL_STATE_LISTEN;
653
654   if (vcl_session_is_cl (session))
655     {
656       if (vcl_segment_attach_session (mp->segment_handle, mp->rx_fifo,
657                                       mp->tx_fifo, mp->vpp_evt_q, 0, session))
658         {
659           VDBG (0, "failed to attach fifos for %u", session->session_index);
660           session->session_state = VCL_STATE_DETACHED;
661           return VCL_INVALID_SESSION_INDEX;
662         }
663     }
664
665   VDBG (0, "session %u [0x%llx]: listen succeeded!", sid, mp->handle);
666   return sid;
667 }
668
669 static void
670 vcl_session_unlisten_reply_handler (vcl_worker_t * wrk, void *data)
671 {
672   session_unlisten_reply_msg_t *mp = (session_unlisten_reply_msg_t *) data;
673   vcl_session_t *s;
674
675   s = vcl_session_get_w_vpp_handle (wrk, mp->handle);
676   if (!s)
677     {
678       VDBG (0, "Unlisten reply with wrong handle %llx", mp->handle);
679       return;
680     }
681   if (s->session_state != VCL_STATE_DISCONNECT)
682     {
683       /* Connected udp listener */
684       if (s->session_type == VPPCOM_PROTO_UDP
685           && s->session_state == VCL_STATE_CLOSED)
686         return;
687
688       VDBG (0, "Unlisten session in wrong state %llx", mp->handle);
689       return;
690     }
691
692   if (mp->retval)
693     VDBG (0, "ERROR: session %u [0xllx]: unlisten failed: %U",
694           s->session_index, mp->handle, format_session_error, mp->retval);
695
696   if (mp->context != wrk->wrk_index)
697     VDBG (0, "wrong context");
698
699   vcl_session_table_del_vpp_handle (wrk, mp->handle);
700   vcl_session_free (wrk, s);
701 }
702
703 static void
704 vcl_session_migrated_handler (vcl_worker_t * wrk, void *data)
705 {
706   session_migrated_msg_t *mp = (session_migrated_msg_t *) data;
707   vcl_session_t *s;
708   u32 fs_index;
709
710   s = vcl_session_get_w_vpp_handle (wrk, mp->handle);
711   if (!s)
712     {
713       VDBG (0, "Migrated notification with wrong handle %llx", mp->handle);
714       return;
715     }
716
717   /* Only validate if a value is provided */
718   if (mp->segment_handle != SESSION_INVALID_HANDLE)
719     {
720       fs_index = vcl_segment_table_lookup (mp->segment_handle);
721       if (fs_index == VCL_INVALID_SEGMENT_INDEX)
722         {
723           VDBG (0, "segment %lx for session %u is not mounted!",
724                 mp->segment_handle, s->session_index);
725           s->session_state = VCL_STATE_DETACHED;
726           return;
727         }
728     }
729
730   s->vpp_handle = mp->new_handle;
731
732   vcl_segment_attach_mq (vcl_vpp_worker_segment_handle (0), mp->vpp_evt_q,
733                          mp->vpp_thread_index, &s->vpp_evt_q);
734
735   vcl_session_table_del_vpp_handle (wrk, mp->handle);
736   vcl_session_table_add_vpp_handle (wrk, mp->new_handle, s->session_index);
737
738   /* Generate new tx event if we have outstanding data */
739   if (svm_fifo_has_event (s->tx_fifo))
740     app_send_io_evt_to_vpp (s->vpp_evt_q,
741                             s->tx_fifo->shr->master_session_index,
742                             SESSION_IO_EVT_TX, SVM_Q_WAIT);
743
744   VDBG (0, "Migrated 0x%lx to thread %u 0x%lx", mp->handle,
745         mp->vpp_thread_index, mp->new_handle);
746 }
747
748 static vcl_session_t *
749 vcl_session_accepted (vcl_worker_t * wrk, session_accepted_msg_t * msg)
750 {
751   vcl_session_msg_t *vcl_msg;
752   vcl_session_t *session;
753
754   session = vcl_session_get_w_vpp_handle (wrk, msg->handle);
755   if (PREDICT_FALSE (session != 0))
756     VWRN ("session overlap handle %lu state %u!", msg->handle,
757           session->session_state);
758
759   session = vcl_session_table_lookup_listener (wrk, msg->listener_handle);
760   if (!session)
761     {
762       VERR ("couldn't find listen session: listener handle %llx",
763             msg->listener_handle);
764       return 0;
765     }
766
767   clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
768   vcl_msg->flags = 0;
769   vcl_msg->accepted_msg = *msg;
770   /* Session handle points to listener until fully accepted by app */
771   vcl_session_table_add_vpp_handle (wrk, msg->handle, session->session_index);
772
773   return session;
774 }
775
776 static vcl_session_t *
777 vcl_session_disconnected_handler (vcl_worker_t * wrk,
778                                   session_disconnected_msg_t * msg)
779 {
780   vcl_session_t *session;
781
782   session = vcl_session_get_w_vpp_handle (wrk, msg->handle);
783   if (!session)
784     {
785       VDBG (0, "request to disconnect unknown handle 0x%llx", msg->handle);
786       return 0;
787     }
788
789   /* Late disconnect notification on a session that has been closed */
790   if (session->session_state == VCL_STATE_CLOSED)
791     return 0;
792
793   /* Caught a disconnect before actually accepting the session */
794   if (session->session_state == VCL_STATE_LISTEN)
795     {
796       if (!vcl_flag_accepted_session (session, msg->handle,
797                                       VCL_ACCEPTED_F_CLOSED))
798         VDBG (0, "session was not accepted!");
799       return 0;
800     }
801
802   /* If not already reset change state */
803   if (session->session_state != VCL_STATE_DISCONNECT)
804     session->session_state = VCL_STATE_VPP_CLOSING;
805
806   return session;
807 }
808
809 int
810 vppcom_session_shutdown (uint32_t session_handle, int how)
811 {
812   vcl_worker_t *wrk = vcl_worker_get_current ();
813   vcl_session_t *session;
814   vcl_session_state_t state;
815   u64 vpp_handle;
816
817   session = vcl_session_get_w_handle (wrk, session_handle);
818   if (PREDICT_FALSE (!session))
819     return VPPCOM_EBADFD;
820
821   vpp_handle = session->vpp_handle;
822   state = session->session_state;
823
824   VDBG (1, "session %u [0x%llx] state 0x%x (%s)", session->session_index,
825         vpp_handle, state, vppcom_session_state_str (state));
826
827   if (PREDICT_FALSE (state == VCL_STATE_LISTEN))
828     {
829       VDBG (0, "ERROR: Cannot shutdown a listen socket!");
830       return VPPCOM_EBADFD;
831     }
832
833   if (how == SHUT_RD || how == SHUT_RDWR)
834     {
835       session->flags |= VCL_SESSION_F_RD_SHUTDOWN;
836       if (how == SHUT_RD)
837         return VPPCOM_OK;
838     }
839   session->flags |= VCL_SESSION_F_WR_SHUTDOWN;
840
841   if (PREDICT_TRUE (state == VCL_STATE_READY))
842     {
843       VDBG (1, "session %u [0x%llx]: sending shutdown...",
844             session->session_index, vpp_handle);
845
846       vcl_send_session_shutdown (wrk, session);
847     }
848
849   return VPPCOM_OK;
850 }
851
852 static int
853 vppcom_session_disconnect (u32 session_handle)
854 {
855   vcl_worker_t *wrk = vcl_worker_get_current ();
856   vcl_session_t *session, *listen_session;
857   vcl_session_state_t state;
858   u64 vpp_handle;
859
860   session = vcl_session_get_w_handle (wrk, session_handle);
861   if (!session)
862     return VPPCOM_EBADFD;
863
864   vpp_handle = session->vpp_handle;
865   state = session->session_state;
866
867   VDBG (1, "session %u [0x%llx] state 0x%x (%s)", session->session_index,
868         vpp_handle, state, vppcom_session_state_str (state));
869
870   if (PREDICT_FALSE (state == VCL_STATE_LISTEN))
871     {
872       VDBG (0, "ERROR: Cannot disconnect a listen socket!");
873       return VPPCOM_EBADFD;
874     }
875
876   if (state == VCL_STATE_VPP_CLOSING)
877     {
878       vcl_send_session_disconnected_reply (wrk, session, 0);
879       VDBG (1, "session %u [0x%llx]: sending disconnect REPLY...",
880             session->session_index, vpp_handle);
881     }
882   else
883     {
884       /* Session doesn't have an event queue yet. Probably a non-blocking
885        * connect. Wait for the reply */
886       if (PREDICT_FALSE (!session->vpp_evt_q))
887         return VPPCOM_OK;
888
889       VDBG (1, "session %u [0x%llx]: sending disconnect...",
890             session->session_index, vpp_handle);
891       vcl_send_session_disconnect (wrk, session);
892     }
893
894   if (session->listener_index != VCL_INVALID_SESSION_INDEX)
895     {
896       listen_session = vcl_session_get (wrk, session->listener_index);
897       listen_session->n_accepted_sessions--;
898     }
899
900   return VPPCOM_OK;
901 }
902
903 static void
904 vcl_session_cleanup_handler (vcl_worker_t * wrk, void *data)
905 {
906   session_cleanup_msg_t *msg;
907   vcl_session_t *session;
908
909   msg = (session_cleanup_msg_t *) data;
910   session = vcl_session_get_w_vpp_handle (wrk, msg->handle);
911   if (!session)
912     {
913       VDBG (0, "disconnect confirmed for unknown handle 0x%llx", msg->handle);
914       return;
915     }
916
917   if (msg->type == SESSION_CLEANUP_TRANSPORT)
918     {
919       /* Transport was cleaned up before we confirmed close. Probably the
920        * app is still waiting for some data that cannot be delivered.
921        * Confirm close to make sure everything is cleaned up.
922        * Move to undetermined state to ensure that the session is not
923        * removed before both vpp and the app cleanup.
924        * - If the app closes first, the session is moved to CLOSED state
925        *   and the session cleanup notification from vpp removes the
926        *   session.
927        * - If vpp cleans up the session first, the session is moved to
928        *   DETACHED state lower and subsequently the close from the app
929        *   frees the session
930        */
931       if (session->session_state == VCL_STATE_VPP_CLOSING)
932         {
933           vppcom_session_disconnect (vcl_session_handle (session));
934           session->session_state = VCL_STATE_UPDATED;
935         }
936       else if (session->session_state == VCL_STATE_DISCONNECT)
937         {
938           vcl_send_session_reset_reply (wrk, session, 0);
939           session->session_state = VCL_STATE_UPDATED;
940         }
941       return;
942     }
943
944   vcl_session_table_del_vpp_handle (wrk, msg->handle);
945   /* Should not happen. App did not close the connection so don't free it. */
946   if (session->session_state != VCL_STATE_CLOSED)
947     {
948       VDBG (0, "app did not close session %d", session->session_index);
949       session->session_state = VCL_STATE_DETACHED;
950       session->vpp_handle = VCL_INVALID_SESSION_HANDLE;
951       return;
952     }
953   vcl_session_free (wrk, session);
954 }
955
956 static void
957 vcl_session_req_worker_update_handler (vcl_worker_t * wrk, void *data)
958 {
959   session_req_worker_update_msg_t *msg;
960   vcl_session_t *s;
961
962   msg = (session_req_worker_update_msg_t *) data;
963   s = vcl_session_get_w_vpp_handle (wrk, msg->session_handle);
964   if (!s)
965     return;
966
967   vec_add1 (wrk->pending_session_wrk_updates, s->session_index);
968 }
969
970 static void
971 vcl_session_worker_update_reply_handler (vcl_worker_t * wrk, void *data)
972 {
973   session_worker_update_reply_msg_t *msg;
974   vcl_session_t *s;
975
976   msg = (session_worker_update_reply_msg_t *) data;
977   s = vcl_session_get_w_vpp_handle (wrk, msg->handle);
978   if (!s)
979     {
980       VDBG (0, "unknown handle 0x%llx", msg->handle);
981       return;
982     }
983
984   if (s->rx_fifo)
985     {
986       if (vcl_segment_attach_session (msg->segment_handle, msg->rx_fifo,
987                                       msg->tx_fifo, (uword) ~0, 0, s))
988         {
989           VDBG (0, "failed to attach fifos for %u", s->session_index);
990           return;
991         }
992     }
993   s->session_state = VCL_STATE_UPDATED;
994
995   VDBG (0, "session %u[0x%llx] moved to worker %u", s->session_index,
996         s->vpp_handle, wrk->wrk_index);
997 }
998
999 static int
1000 vcl_api_recv_fd (vcl_worker_t * wrk, int *fds, int n_fds)
1001 {
1002
1003   if (vcm->cfg.vpp_app_socket_api)
1004     return vcl_sapi_recv_fds (wrk, fds, n_fds);
1005
1006   return vcl_bapi_recv_fds (wrk, fds, n_fds);
1007 }
1008
1009 static void
1010 vcl_session_app_add_segment_handler (vcl_worker_t * wrk, void *data)
1011 {
1012   ssvm_segment_type_t seg_type = SSVM_SEGMENT_SHM;
1013   session_app_add_segment_msg_t *msg;
1014   u64 segment_handle;
1015   int fd = -1;
1016
1017   msg = (session_app_add_segment_msg_t *) data;
1018
1019   if (msg->fd_flags)
1020     {
1021       vcl_api_recv_fd (wrk, &fd, 1);
1022       seg_type = SSVM_SEGMENT_MEMFD;
1023     }
1024
1025   segment_handle = msg->segment_handle;
1026   if (segment_handle == VCL_INVALID_SEGMENT_HANDLE)
1027     {
1028       clib_warning ("invalid segment handle");
1029       return;
1030     }
1031
1032   if (vcl_segment_attach (segment_handle, (char *) msg->segment_name,
1033                           seg_type, fd))
1034     {
1035       VDBG (0, "vcl_segment_attach ('%s') failed", msg->segment_name);
1036       return;
1037     }
1038
1039   VDBG (1, "mapped new segment '%s' size %d", msg->segment_name,
1040         msg->segment_size);
1041 }
1042
1043 static void
1044 vcl_session_app_del_segment_handler (vcl_worker_t * wrk, void *data)
1045 {
1046   session_app_del_segment_msg_t *msg = (session_app_del_segment_msg_t *) data;
1047   vcl_segment_detach (msg->segment_handle);
1048   VDBG (1, "Unmapped segment: %d", msg->segment_handle);
1049 }
1050
1051 static void
1052 vcl_worker_rpc_handler (vcl_worker_t * wrk, void *data)
1053 {
1054   if (!vcm->wrk_rpc_fn)
1055     return;
1056
1057   (vcm->wrk_rpc_fn) (((session_app_wrk_rpc_msg_t *) data)->data);
1058 }
1059
1060 static void
1061 vcl_session_transport_attr_reply_handler (vcl_worker_t *wrk, void *data)
1062 {
1063   session_transport_attr_reply_msg_t *mp;
1064
1065   if (!wrk->session_attr_op)
1066     return;
1067
1068   mp = (session_transport_attr_reply_msg_t *) data;
1069
1070   wrk->session_attr_op_rv = mp->retval;
1071   wrk->session_attr_op = 0;
1072   wrk->session_attr_rv = mp->attr;
1073 }
1074
1075 static int
1076 vcl_handle_mq_event (vcl_worker_t * wrk, session_event_t * e)
1077 {
1078   session_disconnected_msg_t *disconnected_msg;
1079   session_connected_msg_t *connected_msg;
1080   session_reset_msg_t *reset_msg;
1081   session_event_t *ecpy;
1082   vcl_session_t *s;
1083   u32 sid;
1084
1085   switch (e->event_type)
1086     {
1087     case SESSION_IO_EVT_RX:
1088     case SESSION_IO_EVT_TX:
1089       s = vcl_session_get (wrk, e->session_index);
1090       if (!s || !vcl_session_is_open (s))
1091         break;
1092       vec_add1 (wrk->unhandled_evts_vector, *e);
1093       break;
1094     case SESSION_CTRL_EVT_BOUND:
1095       /* We can only wait for only one listen so not postponed */
1096       vcl_session_bound_handler (wrk, (session_bound_msg_t *) e->data);
1097       break;
1098     case SESSION_CTRL_EVT_ACCEPTED:
1099       s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
1100       if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
1101         {
1102           vec_add2 (wrk->unhandled_evts_vector, ecpy, 1);
1103           *ecpy = *e;
1104           ecpy->postponed = 1;
1105           ecpy->session_index = s->session_index;
1106         }
1107       break;
1108     case SESSION_CTRL_EVT_CONNECTED:
1109       connected_msg = (session_connected_msg_t *) e->data;
1110       sid = vcl_session_connected_handler (wrk, connected_msg);
1111       if (!(s = vcl_session_get (wrk, sid)))
1112         break;
1113       if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
1114         {
1115           vec_add2 (wrk->unhandled_evts_vector, ecpy, 1);
1116           *ecpy = *e;
1117           ecpy->postponed = 1;
1118           ecpy->session_index = s->session_index;
1119         }
1120       break;
1121     case SESSION_CTRL_EVT_DISCONNECTED:
1122       disconnected_msg = (session_disconnected_msg_t *) e->data;
1123       if (!(s = vcl_session_get_w_vpp_handle (wrk, disconnected_msg->handle)))
1124         break;
1125       if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
1126         {
1127           vec_add1 (wrk->unhandled_evts_vector, *e);
1128           break;
1129         }
1130       if (!(s = vcl_session_disconnected_handler (wrk, disconnected_msg)))
1131         break;
1132       VDBG (0, "disconnected session %u [0x%llx]", s->session_index,
1133             s->vpp_handle);
1134       break;
1135     case SESSION_CTRL_EVT_RESET:
1136       reset_msg = (session_reset_msg_t *) e->data;
1137       if (!(s = vcl_session_get_w_vpp_handle (wrk, reset_msg->handle)))
1138         break;
1139       if (vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
1140         {
1141           vec_add1 (wrk->unhandled_evts_vector, *e);
1142           break;
1143         }
1144       vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
1145       break;
1146     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
1147       vcl_session_unlisten_reply_handler (wrk, e->data);
1148       break;
1149     case SESSION_CTRL_EVT_MIGRATED:
1150       vcl_session_migrated_handler (wrk, e->data);
1151       break;
1152     case SESSION_CTRL_EVT_CLEANUP:
1153       vcl_session_cleanup_handler (wrk, e->data);
1154       break;
1155     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
1156       vcl_session_req_worker_update_handler (wrk, e->data);
1157       break;
1158     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
1159       vcl_session_worker_update_reply_handler (wrk, e->data);
1160       break;
1161     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
1162       vcl_session_app_add_segment_handler (wrk, e->data);
1163       break;
1164     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
1165       vcl_session_app_del_segment_handler (wrk, e->data);
1166       break;
1167     case SESSION_CTRL_EVT_APP_WRK_RPC:
1168       vcl_worker_rpc_handler (wrk, e->data);
1169       break;
1170     case SESSION_CTRL_EVT_TRANSPORT_ATTR_REPLY:
1171       vcl_session_transport_attr_reply_handler (wrk, e->data);
1172       break;
1173     default:
1174       clib_warning ("unhandled %u", e->event_type);
1175     }
1176   return VPPCOM_OK;
1177 }
1178
1179 static int
1180 vppcom_wait_for_session_state_change (u32 session_index,
1181                                       vcl_session_state_t state,
1182                                       f64 wait_for_time)
1183 {
1184   vcl_worker_t *wrk = vcl_worker_get_current ();
1185   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
1186   vcl_session_t *volatile session;
1187   svm_msg_q_msg_t msg;
1188   session_event_t *e;
1189
1190   do
1191     {
1192       session = vcl_session_get (wrk, session_index);
1193       if (PREDICT_FALSE (!session))
1194         {
1195           return VPPCOM_EBADFD;
1196         }
1197       if (session->session_state == state)
1198         {
1199           return VPPCOM_OK;
1200         }
1201       if (session->session_state == VCL_STATE_DETACHED)
1202         {
1203           return VPPCOM_ECONNREFUSED;
1204         }
1205
1206       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0))
1207         {
1208           usleep (100);
1209           continue;
1210         }
1211       e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
1212       vcl_handle_mq_event (wrk, e);
1213       svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1214     }
1215   while (clib_time_now (&wrk->clib_time) < timeout);
1216
1217   VDBG (0, "timeout waiting for state 0x%x (%s)", state,
1218         vppcom_session_state_str (state));
1219   vcl_evt (VCL_EVT_SESSION_TIMEOUT, session, session_state);
1220
1221   return VPPCOM_ETIMEDOUT;
1222 }
1223
1224 static void
1225 vcl_handle_pending_wrk_updates (vcl_worker_t * wrk)
1226 {
1227   vcl_session_state_t state;
1228   vcl_session_t *s;
1229   u32 *sip;
1230
1231   if (PREDICT_TRUE (vec_len (wrk->pending_session_wrk_updates) == 0))
1232     return;
1233
1234   vec_foreach (sip, wrk->pending_session_wrk_updates)
1235   {
1236     s = vcl_session_get (wrk, *sip);
1237     vcl_send_session_worker_update (wrk, s, wrk->wrk_index);
1238     state = s->session_state;
1239     vppcom_wait_for_session_state_change (s->session_index, VCL_STATE_UPDATED,
1240                                           5);
1241     s->session_state = state;
1242   }
1243   vec_reset_length (wrk->pending_session_wrk_updates);
1244 }
1245
1246 void
1247 vcl_worker_flush_mq_events (vcl_worker_t *wrk)
1248 {
1249   svm_msg_q_msg_t *msg;
1250   session_event_t *e;
1251   svm_msg_q_t *mq;
1252   int i;
1253
1254   mq = wrk->app_event_queue;
1255   vcl_mq_dequeue_batch (wrk, mq, ~0);
1256
1257   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
1258     {
1259       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
1260       e = svm_msg_q_msg_data (mq, msg);
1261       vcl_handle_mq_event (wrk, e);
1262       svm_msg_q_free_msg (mq, msg);
1263     }
1264   vec_reset_length (wrk->mq_msg_vector);
1265   vcl_handle_pending_wrk_updates (wrk);
1266 }
1267
1268 void
1269 vcl_flush_mq_events (void)
1270 {
1271   vcl_worker_flush_mq_events (vcl_worker_get_current ());
1272 }
1273
1274 static int
1275 vppcom_session_unbind (u32 session_handle)
1276 {
1277   vcl_worker_t *wrk = vcl_worker_get_current ();
1278   session_accepted_msg_t *accepted_msg;
1279   vcl_session_t *session = 0;
1280   vcl_session_msg_t *evt;
1281
1282   session = vcl_session_get_w_handle (wrk, session_handle);
1283   if (!session)
1284     return VPPCOM_EBADFD;
1285
1286   /* Flush pending accept events, if any */
1287   while (clib_fifo_elts (session->accept_evts_fifo))
1288     {
1289       clib_fifo_sub2 (session->accept_evts_fifo, evt);
1290       accepted_msg = &evt->accepted_msg;
1291       vcl_session_table_del_vpp_handle (wrk, accepted_msg->handle);
1292       vcl_send_session_accepted_reply (session->vpp_evt_q,
1293                                        accepted_msg->context,
1294                                        accepted_msg->handle, -1);
1295     }
1296   clib_fifo_free (session->accept_evts_fifo);
1297
1298   vcl_send_session_unlisten (wrk, session);
1299
1300   VDBG (1, "session %u [0x%llx]: sending unbind!", session->session_index,
1301         session->vpp_handle);
1302   vcl_evt (VCL_EVT_UNBIND, session);
1303
1304   session->vpp_handle = ~0;
1305   session->session_state = VCL_STATE_DISCONNECT;
1306
1307   return VPPCOM_OK;
1308 }
1309
1310 /**
1311  * Handle app exit
1312  *
1313  * Notify vpp of the disconnect and mark the worker as free. If we're the
1314  * last worker, do a full cleanup otherwise, since we're probably a forked
1315  * child, avoid syscalls as much as possible. We might've lost privileges.
1316  */
1317 void
1318 vppcom_app_exit (void)
1319 {
1320   if (!pool_elts (vcm->workers))
1321     return;
1322   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
1323   vcl_set_worker_index (~0);
1324   vcl_elog_stop (vcm);
1325 }
1326
1327 static int
1328 vcl_api_attach (void)
1329 {
1330   if (vcm->cfg.vpp_app_socket_api)
1331     return vcl_sapi_attach ();
1332
1333   return vcl_bapi_attach ();
1334 }
1335
1336 static void
1337 vcl_api_detach (vcl_worker_t * wrk)
1338 {
1339   vcl_send_app_detach (wrk);
1340
1341   if (vcm->cfg.vpp_app_socket_api)
1342     return vcl_sapi_detach (wrk);
1343
1344   return vcl_bapi_disconnect_from_vpp ();
1345 }
1346
1347 /*
1348  * VPPCOM Public API functions
1349  */
1350 int
1351 vppcom_app_create (const char *app_name)
1352 {
1353   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
1354   int rv;
1355
1356   if (vcm->is_init)
1357     {
1358       VDBG (1, "already initialized");
1359       return VPPCOM_EEXIST;
1360     }
1361
1362   vcm->is_init = 1;
1363   vppcom_cfg (&vcm->cfg);
1364   vcl_cfg = &vcm->cfg;
1365
1366   vcm->main_cpu = pthread_self ();
1367   vcm->main_pid = getpid ();
1368   vcm->app_name = format (0, "%s", app_name);
1369   fifo_segment_main_init (&vcm->segment_main, vcl_cfg->segment_baseva,
1370                           20 /* timeout in secs */ );
1371   pool_alloc (vcm->workers, vcl_cfg->max_workers);
1372   clib_spinlock_init (&vcm->workers_lock);
1373   clib_rwlock_init (&vcm->segment_table_lock);
1374   atexit (vppcom_app_exit);
1375   vcl_elog_init (vcm);
1376
1377   /* Allocate default worker */
1378   vcl_worker_alloc_and_init ();
1379
1380   if ((rv = vcl_api_attach ()))
1381     return rv;
1382
1383   VDBG (0, "app_name '%s', my_client_index %d (0x%x)", app_name,
1384         vcm->workers[0].api_client_handle, vcm->workers[0].api_client_handle);
1385
1386   return VPPCOM_OK;
1387 }
1388
1389 void
1390 vppcom_app_destroy (void)
1391 {
1392   vcl_worker_t *wrk, *current_wrk;
1393   void *heap;
1394
1395   if (!pool_elts (vcm->workers))
1396     return;
1397
1398   vcl_evt (VCL_EVT_DETACH, vcm);
1399
1400   current_wrk = vcl_worker_get_current ();
1401
1402   /* *INDENT-OFF* */
1403   pool_foreach (wrk, vcm->workers)  {
1404     if (current_wrk != wrk)
1405       vcl_worker_cleanup (wrk, 0 /* notify vpp */ );
1406   }
1407   /* *INDENT-ON* */
1408
1409   vcl_api_detach (current_wrk);
1410   vcl_worker_cleanup (current_wrk, 0 /* notify vpp */ );
1411
1412   vcl_elog_stop (vcm);
1413
1414   /*
1415    * Free the heap and fix vcm
1416    */
1417   heap = clib_mem_get_heap ();
1418   munmap (clib_mem_get_heap_base (heap), clib_mem_get_heap_size (heap));
1419
1420   vcm = &_vppcom_main;
1421   vcm->is_init = 0;
1422 }
1423
1424 int
1425 vppcom_session_create (u8 proto, u8 is_nonblocking)
1426 {
1427   vcl_worker_t *wrk = vcl_worker_get_current ();
1428   vcl_session_t *session;
1429
1430   session = vcl_session_alloc (wrk);
1431
1432   session->session_type = proto;
1433   session->session_state = VCL_STATE_CLOSED;
1434   session->vpp_handle = ~0;
1435   session->is_dgram = vcl_proto_is_dgram (proto);
1436
1437   if (is_nonblocking)
1438     vcl_session_set_attr (session, VCL_SESS_ATTR_NONBLOCK);
1439
1440   vcl_evt (VCL_EVT_CREATE, session, session_type, session->session_state,
1441            is_nonblocking, session_index);
1442
1443   VDBG (0, "created session %u", session->session_index);
1444
1445   return vcl_session_handle (session);
1446 }
1447
1448 static void
1449 vcl_epoll_wait_clean_lt (vcl_worker_t *wrk, u32 sid)
1450 {
1451   int i;
1452
1453   for (i = vec_len (wrk->ep_level_evts) - 1; i >= 0; i--)
1454     {
1455       if (wrk->ep_level_evts[i] == sid)
1456         vec_del1 (wrk->ep_level_evts, i);
1457     }
1458 }
1459
1460 int
1461 vcl_session_cleanup (vcl_worker_t * wrk, vcl_session_t * s,
1462                      vcl_session_handle_t sh, u8 do_disconnect)
1463 {
1464   int rv = VPPCOM_OK;
1465
1466   VDBG (1, "session %u [0x%llx] closing", s->session_index, s->vpp_handle);
1467
1468   if (s->flags & VCL_SESSION_F_IS_VEP)
1469     {
1470       u32 next_sh = s->vep.next_sh;
1471       while (next_sh != ~0)
1472         {
1473           rv = vppcom_epoll_ctl (sh, EPOLL_CTL_DEL, next_sh, 0);
1474           if (PREDICT_FALSE (rv < 0))
1475             VDBG (0, "vpp handle 0x%llx, sh %u: EPOLL_CTL_DEL vep_idx %u"
1476                   " failed! rv %d (%s)", s->vpp_handle, next_sh,
1477                   s->vep.vep_sh, rv, vppcom_retval_str (rv));
1478           next_sh = s->vep.next_sh;
1479         }
1480       goto free_session;
1481     }
1482
1483   if (s->flags & VCL_SESSION_F_IS_VEP_SESSION)
1484     {
1485       rv = vppcom_epoll_ctl (s->vep.vep_sh, EPOLL_CTL_DEL, sh, 0);
1486       if (rv < 0)
1487         VDBG (0, "session %u [0x%llx]: EPOLL_CTL_DEL vep_idx %u "
1488               "failed! rv %d (%s)", s->session_index, s->vpp_handle,
1489               s->vep.vep_sh, rv, vppcom_retval_str (rv));
1490       if (PREDICT_FALSE (vec_len (wrk->ep_level_evts)))
1491         vcl_epoll_wait_clean_lt (wrk, s->session_index);
1492     }
1493
1494   if (!do_disconnect)
1495     {
1496       VDBG (1, "session %u [0x%llx] disconnect skipped",
1497             s->session_index, s->vpp_handle);
1498       goto cleanup;
1499     }
1500
1501   if (s->session_state == VCL_STATE_LISTEN)
1502     {
1503       rv = vppcom_session_unbind (sh);
1504       if (PREDICT_FALSE (rv < 0))
1505         VDBG (0, "session %u [0x%llx]: listener unbind failed! "
1506               "rv %d (%s)", s->session_index, s->vpp_handle, rv,
1507               vppcom_retval_str (rv));
1508       return rv;
1509     }
1510   else if (vcl_session_is_ready (s)
1511            || (vcl_session_is_connectable_listener (wrk, s)))
1512     {
1513       rv = vppcom_session_disconnect (sh);
1514       if (PREDICT_FALSE (rv < 0))
1515         VDBG (0, "ERROR: session %u [0x%llx]: disconnect failed!"
1516               " rv %d (%s)", s->session_index, s->vpp_handle,
1517               rv, vppcom_retval_str (rv));
1518     }
1519   else if (s->session_state == VCL_STATE_DISCONNECT)
1520     {
1521       vcl_send_session_reset_reply (wrk, s, 0);
1522     }
1523   else if (s->session_state == VCL_STATE_DETACHED)
1524     {
1525       /* Should not happen. VPP cleaned up before app confirmed close */
1526       VDBG (0, "vpp freed session %d before close", s->session_index);
1527       goto free_session;
1528     }
1529
1530   s->session_state = VCL_STATE_CLOSED;
1531
1532   /* Session is removed only after vpp confirms the disconnect */
1533   return rv;
1534
1535 cleanup:
1536   vcl_session_table_del_vpp_handle (wrk, s->vpp_handle);
1537 free_session:
1538   vcl_session_free (wrk, s);
1539   vcl_evt (VCL_EVT_CLOSE, s, rv);
1540
1541   return rv;
1542 }
1543
1544 int
1545 vppcom_session_close (uint32_t session_handle)
1546 {
1547   vcl_worker_t *wrk = vcl_worker_get_current ();
1548   vcl_session_t *session;
1549
1550   session = vcl_session_get_w_handle (wrk, session_handle);
1551   if (!session)
1552     return VPPCOM_EBADFD;
1553   return vcl_session_cleanup (wrk, session, session_handle,
1554                               1 /* do_disconnect */ );
1555 }
1556
1557 int
1558 vppcom_session_bind (uint32_t session_handle, vppcom_endpt_t * ep)
1559 {
1560   vcl_worker_t *wrk = vcl_worker_get_current ();
1561   vcl_session_t *session = 0;
1562
1563   if (!ep || !ep->ip)
1564     return VPPCOM_EINVAL;
1565
1566   session = vcl_session_get_w_handle (wrk, session_handle);
1567   if (!session)
1568     return VPPCOM_EBADFD;
1569
1570   if (session->flags & VCL_SESSION_F_IS_VEP)
1571     {
1572       VDBG (0, "ERROR: cannot bind to epoll session %u!",
1573             session->session_index);
1574       return VPPCOM_EBADFD;
1575     }
1576
1577   session->transport.is_ip4 = ep->is_ip4;
1578   if (ep->is_ip4)
1579     clib_memcpy_fast (&session->transport.lcl_ip.ip4, ep->ip,
1580                       sizeof (ip4_address_t));
1581   else
1582     clib_memcpy_fast (&session->transport.lcl_ip.ip6, ep->ip,
1583                       sizeof (ip6_address_t));
1584   session->transport.lcl_port = ep->port;
1585
1586   VDBG (0, "session %u handle %u: binding to local %s address %U port %u, "
1587         "proto %s", session->session_index, session_handle,
1588         session->transport.is_ip4 ? "IPv4" : "IPv6",
1589         format_ip46_address, &session->transport.lcl_ip,
1590         session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1591         clib_net_to_host_u16 (session->transport.lcl_port),
1592         vppcom_proto_str (session->session_type));
1593   vcl_evt (VCL_EVT_BIND, session);
1594
1595   if (session->session_type == VPPCOM_PROTO_UDP)
1596     vppcom_session_listen (session_handle, 10);
1597
1598   return VPPCOM_OK;
1599 }
1600
1601 int
1602 vppcom_session_listen (uint32_t listen_sh, uint32_t q_len)
1603 {
1604   vcl_worker_t *wrk = vcl_worker_get_current ();
1605   vcl_session_t *listen_session = 0;
1606   u64 listen_vpp_handle;
1607   int rv;
1608
1609   listen_session = vcl_session_get_w_handle (wrk, listen_sh);
1610   if (!listen_session || (listen_session->flags & VCL_SESSION_F_IS_VEP))
1611     return VPPCOM_EBADFD;
1612
1613   if (q_len == 0 || q_len == ~0)
1614     q_len = vcm->cfg.listen_queue_size;
1615
1616   listen_vpp_handle = listen_session->vpp_handle;
1617   if (listen_session->session_state == VCL_STATE_LISTEN)
1618     {
1619       VDBG (0, "session %u [0x%llx]: already in listen state!",
1620             listen_sh, listen_vpp_handle);
1621       return VPPCOM_OK;
1622     }
1623
1624   VDBG (0, "session %u: sending vpp listen request...", listen_sh);
1625
1626   /*
1627    * Send listen request to vpp and wait for reply
1628    */
1629   vcl_send_session_listen (wrk, listen_session);
1630   rv = vppcom_wait_for_session_state_change (listen_session->session_index,
1631                                              VCL_STATE_LISTEN,
1632                                              vcm->cfg.session_timeout);
1633
1634   if (PREDICT_FALSE (rv))
1635     {
1636       listen_session = vcl_session_get_w_handle (wrk, listen_sh);
1637       VDBG (0, "session %u [0x%llx]: listen failed! returning %d (%s)",
1638             listen_sh, listen_session->vpp_handle, rv,
1639             vppcom_retval_str (rv));
1640       return rv;
1641     }
1642
1643   return VPPCOM_OK;
1644 }
1645
1646 static int
1647 validate_args_session_accept_ (vcl_worker_t * wrk, vcl_session_t * ls)
1648 {
1649   if (ls->flags & VCL_SESSION_F_IS_VEP)
1650     {
1651       VDBG (0, "ERROR: cannot accept on epoll session %u!",
1652             ls->session_index);
1653       return VPPCOM_EBADFD;
1654     }
1655
1656   if ((ls->session_state != VCL_STATE_LISTEN)
1657       && (!vcl_session_is_connectable_listener (wrk, ls)))
1658     {
1659       VDBG (0, "ERROR: session [0x%llx]: not in listen state! state 0x%x"
1660             " (%s)", ls->vpp_handle, ls->session_state,
1661             vppcom_session_state_str (ls->session_state));
1662       return VPPCOM_EBADFD;
1663     }
1664   return VPPCOM_OK;
1665 }
1666
1667 int
1668 vppcom_unformat_proto (uint8_t * proto, char *proto_str)
1669 {
1670   if (!strcmp (proto_str, "TCP"))
1671     *proto = VPPCOM_PROTO_TCP;
1672   else if (!strcmp (proto_str, "tcp"))
1673     *proto = VPPCOM_PROTO_TCP;
1674   else if (!strcmp (proto_str, "UDP"))
1675     *proto = VPPCOM_PROTO_UDP;
1676   else if (!strcmp (proto_str, "udp"))
1677     *proto = VPPCOM_PROTO_UDP;
1678   else if (!strcmp (proto_str, "TLS"))
1679     *proto = VPPCOM_PROTO_TLS;
1680   else if (!strcmp (proto_str, "tls"))
1681     *proto = VPPCOM_PROTO_TLS;
1682   else if (!strcmp (proto_str, "QUIC"))
1683     *proto = VPPCOM_PROTO_QUIC;
1684   else if (!strcmp (proto_str, "quic"))
1685     *proto = VPPCOM_PROTO_QUIC;
1686   else if (!strcmp (proto_str, "DTLS"))
1687     *proto = VPPCOM_PROTO_DTLS;
1688   else if (!strcmp (proto_str, "dtls"))
1689     *proto = VPPCOM_PROTO_DTLS;
1690   else if (!strcmp (proto_str, "SRTP"))
1691     *proto = VPPCOM_PROTO_SRTP;
1692   else if (!strcmp (proto_str, "srtp"))
1693     *proto = VPPCOM_PROTO_SRTP;
1694   else
1695     return 1;
1696   return 0;
1697 }
1698
1699 int
1700 vppcom_session_accept (uint32_t listen_session_handle, vppcom_endpt_t * ep,
1701                        uint32_t flags)
1702 {
1703   u32 client_session_index = ~0, listen_session_index, accept_flags = 0;
1704   vcl_worker_t *wrk = vcl_worker_get_current ();
1705   session_accepted_msg_t accepted_msg;
1706   vcl_session_t *listen_session = 0;
1707   vcl_session_t *client_session = 0;
1708   vcl_session_msg_t *evt;
1709   u8 is_nonblocking;
1710   int rv;
1711
1712 again:
1713
1714   listen_session = vcl_session_get_w_handle (wrk, listen_session_handle);
1715   if (!listen_session)
1716     return VPPCOM_EBADFD;
1717
1718   listen_session_index = listen_session->session_index;
1719   if ((rv = validate_args_session_accept_ (wrk, listen_session)))
1720     return rv;
1721
1722   if (clib_fifo_elts (listen_session->accept_evts_fifo))
1723     {
1724       clib_fifo_sub2 (listen_session->accept_evts_fifo, evt);
1725       accept_flags = evt->flags;
1726       accepted_msg = evt->accepted_msg;
1727       goto handle;
1728     }
1729
1730   is_nonblocking = vcl_session_has_attr (listen_session,
1731                                          VCL_SESS_ATTR_NONBLOCK);
1732   while (1)
1733     {
1734       if (svm_msg_q_is_empty (wrk->app_event_queue) && is_nonblocking)
1735         return VPPCOM_EAGAIN;
1736
1737       svm_msg_q_wait (wrk->app_event_queue, SVM_MQ_WAIT_EMPTY);
1738       vcl_worker_flush_mq_events (wrk);
1739       goto again;
1740     }
1741
1742 handle:
1743
1744   client_session_index = vcl_session_accepted_handler (wrk, &accepted_msg,
1745                                                        listen_session_index);
1746   if (client_session_index == VCL_INVALID_SESSION_INDEX)
1747     return VPPCOM_ECONNABORTED;
1748
1749   listen_session = vcl_session_get (wrk, listen_session_index);
1750   client_session = vcl_session_get (wrk, client_session_index);
1751
1752   if (flags & O_NONBLOCK)
1753     vcl_session_set_attr (client_session, VCL_SESS_ATTR_NONBLOCK);
1754
1755   VDBG (1, "listener %u [0x%llx]: Got a connect request! session %u [0x%llx],"
1756         " flags %d, is_nonblocking %u", listen_session->session_index,
1757         listen_session->vpp_handle, client_session_index,
1758         client_session->vpp_handle, flags,
1759         vcl_session_has_attr (client_session, VCL_SESS_ATTR_NONBLOCK));
1760
1761   if (ep)
1762     {
1763       ep->is_ip4 = client_session->transport.is_ip4;
1764       ep->port = client_session->transport.rmt_port;
1765       if (client_session->transport.is_ip4)
1766         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip4,
1767                           sizeof (ip4_address_t));
1768       else
1769         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip6,
1770                           sizeof (ip6_address_t));
1771     }
1772
1773   VDBG (0, "listener %u [0x%llx] accepted %u [0x%llx] peer: %U:%u "
1774         "local: %U:%u", listen_session_handle, listen_session->vpp_handle,
1775         client_session_index, client_session->vpp_handle,
1776         format_ip46_address, &client_session->transport.rmt_ip,
1777         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1778         clib_net_to_host_u16 (client_session->transport.rmt_port),
1779         format_ip46_address, &client_session->transport.lcl_ip,
1780         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1781         clib_net_to_host_u16 (client_session->transport.lcl_port));
1782   vcl_evt (VCL_EVT_ACCEPT, client_session, listen_session,
1783            client_session_index);
1784
1785   /*
1786    * Session might have been closed already
1787    */
1788   if (accept_flags)
1789     {
1790       if (accept_flags & VCL_ACCEPTED_F_CLOSED)
1791         client_session->session_state = VCL_STATE_VPP_CLOSING;
1792       else if (accept_flags & VCL_ACCEPTED_F_RESET)
1793         client_session->session_state = VCL_STATE_DISCONNECT;
1794     }
1795   return vcl_session_handle (client_session);
1796 }
1797
1798 int
1799 vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
1800 {
1801   vcl_worker_t *wrk = vcl_worker_get_current ();
1802   vcl_session_t *session = 0;
1803   u32 session_index;
1804   int rv;
1805
1806   session = vcl_session_get_w_handle (wrk, session_handle);
1807   if (!session)
1808     return VPPCOM_EBADFD;
1809   session_index = session->session_index;
1810
1811   if (PREDICT_FALSE (session->flags & VCL_SESSION_F_IS_VEP))
1812     {
1813       VDBG (0, "ERROR: cannot connect epoll session %u!",
1814             session->session_index);
1815       return VPPCOM_EBADFD;
1816     }
1817
1818   if (PREDICT_FALSE (vcl_session_is_ready (session)))
1819     {
1820       VDBG (0, "session handle %u [0x%llx]: session already "
1821             "connected to %s %U port %d proto %s, state 0x%x (%s)",
1822             session_handle, session->vpp_handle,
1823             session->transport.is_ip4 ? "IPv4" : "IPv6", format_ip46_address,
1824             &session->transport.rmt_ip, session->transport.is_ip4 ?
1825             IP46_TYPE_IP4 : IP46_TYPE_IP6,
1826             clib_net_to_host_u16 (session->transport.rmt_port),
1827             vppcom_proto_str (session->session_type), session->session_state,
1828             vppcom_session_state_str (session->session_state));
1829       return VPPCOM_OK;
1830     }
1831
1832   /* Attempt to connect a connectionless listener */
1833   if (PREDICT_FALSE (session->session_state == VCL_STATE_LISTEN))
1834     {
1835       if (session->session_type != VPPCOM_PROTO_UDP)
1836         return VPPCOM_EINVAL;
1837       vcl_send_session_unlisten (wrk, session);
1838       session->session_state = VCL_STATE_CLOSED;
1839     }
1840
1841   session->transport.is_ip4 = server_ep->is_ip4;
1842   vcl_ip_copy_from_ep (&session->transport.rmt_ip, server_ep);
1843   session->transport.rmt_port = server_ep->port;
1844   session->parent_handle = VCL_INVALID_SESSION_HANDLE;
1845   session->flags |= VCL_SESSION_F_CONNECTED;
1846
1847   VDBG (0, "session handle %u (%s): connecting to peer %s %U "
1848         "port %d proto %s", session_handle,
1849         vppcom_session_state_str (session->session_state),
1850         session->transport.is_ip4 ? "IPv4" : "IPv6",
1851         format_ip46_address,
1852         &session->transport.rmt_ip, session->transport.is_ip4 ?
1853         IP46_TYPE_IP4 : IP46_TYPE_IP6,
1854         clib_net_to_host_u16 (session->transport.rmt_port),
1855         vppcom_proto_str (session->session_type));
1856
1857   vcl_send_session_connect (wrk, session);
1858
1859   if (vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK))
1860     {
1861       /* State set to STATE_UPDATED to ensure the session is not assumed
1862        * to be ready and to also allow the app to close it prior to vpp's
1863        * connected reply. */
1864       session->session_state = VCL_STATE_UPDATED;
1865       return VPPCOM_EINPROGRESS;
1866     }
1867
1868   /*
1869    * Wait for reply from vpp if blocking
1870    */
1871   rv = vppcom_wait_for_session_state_change (session_index, VCL_STATE_READY,
1872                                              vcm->cfg.session_timeout);
1873
1874   session = vcl_session_get (wrk, session_index);
1875   VDBG (0, "session %u [0x%llx]: connect %s!", session->session_index,
1876         session->vpp_handle, rv ? "failed" : "succeeded");
1877
1878   return rv;
1879 }
1880
1881 int
1882 vppcom_session_stream_connect (uint32_t session_handle,
1883                                uint32_t parent_session_handle)
1884 {
1885   vcl_worker_t *wrk = vcl_worker_get_current ();
1886   vcl_session_t *session, *parent_session;
1887   u32 session_index, parent_session_index;
1888   int rv;
1889
1890   session = vcl_session_get_w_handle (wrk, session_handle);
1891   if (!session)
1892     return VPPCOM_EBADFD;
1893   parent_session = vcl_session_get_w_handle (wrk, parent_session_handle);
1894   if (!parent_session)
1895     return VPPCOM_EBADFD;
1896
1897   session_index = session->session_index;
1898   parent_session_index = parent_session->session_index;
1899   if (PREDICT_FALSE (session->flags & VCL_SESSION_F_IS_VEP))
1900     {
1901       VDBG (0, "ERROR: cannot connect epoll session %u!",
1902             session->session_index);
1903       return VPPCOM_EBADFD;
1904     }
1905
1906   if (PREDICT_FALSE (vcl_session_is_ready (session)))
1907     {
1908       VDBG (0, "session handle %u [0x%llx]: session already "
1909             "connected to session %u [0x%llx] proto %s, state 0x%x (%s)",
1910             session_handle, session->vpp_handle,
1911             parent_session_handle, parent_session->vpp_handle,
1912             vppcom_proto_str (session->session_type), session->session_state,
1913             vppcom_session_state_str (session->session_state));
1914       return VPPCOM_OK;
1915     }
1916
1917   /* Connect to quic session specifics */
1918   session->transport.is_ip4 = parent_session->transport.is_ip4;
1919   session->transport.rmt_ip.ip4.as_u32 = (uint32_t) 1;
1920   session->transport.rmt_port = 0;
1921   session->parent_handle = parent_session->vpp_handle;
1922
1923   VDBG (0, "session handle %u: connecting to session %u [0x%llx]",
1924         session_handle, parent_session_handle, parent_session->vpp_handle);
1925
1926   /*
1927    * Send connect request and wait for reply from vpp
1928    */
1929   vcl_send_session_connect (wrk, session);
1930   rv = vppcom_wait_for_session_state_change (session_index, VCL_STATE_READY,
1931                                              vcm->cfg.session_timeout);
1932
1933   session->listener_index = parent_session_index;
1934   parent_session = vcl_session_get_w_handle (wrk, parent_session_handle);
1935   if (parent_session)
1936     parent_session->n_accepted_sessions++;
1937
1938   session = vcl_session_get (wrk, session_index);
1939   VDBG (0, "session %u [0x%llx]: connect %s!", session->session_index,
1940         session->vpp_handle, rv ? "failed" : "succeeded");
1941
1942   return rv;
1943 }
1944
1945 static inline int
1946 vppcom_session_read_internal (uint32_t session_handle, void *buf, int n,
1947                               u8 peek)
1948 {
1949   vcl_worker_t *wrk = vcl_worker_get_current ();
1950   int rv, n_read = 0, is_nonblocking;
1951   vcl_session_t *s = 0;
1952   svm_fifo_t *rx_fifo;
1953   session_event_t *e;
1954   svm_msg_q_t *mq;
1955   u8 is_ct;
1956
1957   if (PREDICT_FALSE (!buf))
1958     return VPPCOM_EINVAL;
1959
1960   s = vcl_session_get_w_handle (wrk, session_handle);
1961   if (PREDICT_FALSE (!s || (s->flags & VCL_SESSION_F_IS_VEP)))
1962     return VPPCOM_EBADFD;
1963
1964   if (PREDICT_FALSE (!vcl_session_is_open (s)))
1965     {
1966       VDBG (0, "session %u[0x%llx] is not open! state 0x%x (%s)",
1967             s->session_index, s->vpp_handle, s->session_state,
1968             vppcom_session_state_str (s->session_state));
1969       return vcl_session_closed_error (s);
1970     }
1971
1972   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_RD_SHUTDOWN))
1973     {
1974       /* Vpp would ack the incoming data and enqueue it for reading.
1975        * So even if SHUT_RD is set, we can still read() the data if
1976        * the session is ready.
1977        */
1978       if (!vcl_session_read_ready (s))
1979         {
1980           return 0;
1981         }
1982     }
1983
1984   is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
1985   is_ct = vcl_session_is_ct (s);
1986   mq = wrk->app_event_queue;
1987   rx_fifo = is_ct ? s->ct_rx_fifo : s->rx_fifo;
1988   s->flags &= ~VCL_SESSION_F_HAS_RX_EVT;
1989
1990   if (svm_fifo_is_empty_cons (rx_fifo))
1991     {
1992       if (is_nonblocking)
1993         {
1994           if (vcl_session_is_closing (s))
1995             return vcl_session_closing_error (s);
1996           if (is_ct)
1997             svm_fifo_unset_event (s->rx_fifo);
1998           svm_fifo_unset_event (rx_fifo);
1999           return VPPCOM_EWOULDBLOCK;
2000         }
2001       while (svm_fifo_is_empty_cons (rx_fifo))
2002         {
2003           if (vcl_session_is_closing (s))
2004             return vcl_session_closing_error (s);
2005
2006           if (is_ct)
2007             svm_fifo_unset_event (s->rx_fifo);
2008           svm_fifo_unset_event (rx_fifo);
2009
2010           svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2011           vcl_worker_flush_mq_events (wrk);
2012         }
2013     }
2014
2015 read_again:
2016
2017   if (s->is_dgram)
2018     rv = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek);
2019   else
2020     rv = app_recv_stream_raw (rx_fifo, buf, n, 0, peek);
2021
2022   ASSERT (rv >= 0);
2023
2024   if (peek)
2025     return rv;
2026
2027   n_read += rv;
2028
2029   if (svm_fifo_is_empty_cons (rx_fifo))
2030     {
2031       if (is_ct)
2032         svm_fifo_unset_event (s->rx_fifo);
2033       svm_fifo_unset_event (rx_fifo);
2034       if (!svm_fifo_is_empty_cons (rx_fifo)
2035           && svm_fifo_set_event (rx_fifo) && is_nonblocking)
2036         {
2037           vec_add2 (wrk->unhandled_evts_vector, e, 1);
2038           e->event_type = SESSION_IO_EVT_RX;
2039           e->session_index = s->session_index;
2040         }
2041     }
2042   else if (PREDICT_FALSE (rv < n && !s->is_dgram))
2043     {
2044       /* More data enqueued while reading. Try to drain it
2045        * or fill the buffer. Avoid doing that for dgrams */
2046       buf += rv;
2047       n -= rv;
2048       goto read_again;
2049     }
2050
2051   if (PREDICT_FALSE (svm_fifo_needs_deq_ntf (rx_fifo, n_read)))
2052     {
2053       svm_fifo_clear_deq_ntf (rx_fifo);
2054       app_send_io_evt_to_vpp (s->vpp_evt_q,
2055                               s->rx_fifo->shr->master_session_index,
2056                               SESSION_IO_EVT_RX, SVM_Q_WAIT);
2057     }
2058
2059   VDBG (2, "session %u[0x%llx]: read %d bytes from (%p)", s->session_index,
2060         s->vpp_handle, n_read, rx_fifo);
2061
2062   return n_read;
2063 }
2064
2065 int
2066 vppcom_session_read (uint32_t session_handle, void *buf, size_t n)
2067 {
2068   return (vppcom_session_read_internal (session_handle, buf, n, 0));
2069 }
2070
2071 static int
2072 vppcom_session_peek (uint32_t session_handle, void *buf, int n)
2073 {
2074   return (vppcom_session_read_internal (session_handle, buf, n, 1));
2075 }
2076
2077 int
2078 vppcom_session_read_segments (uint32_t session_handle,
2079                               vppcom_data_segment_t * ds, uint32_t n_segments,
2080                               uint32_t max_bytes)
2081 {
2082   vcl_worker_t *wrk = vcl_worker_get_current ();
2083   int n_read = 0, is_nonblocking;
2084   vcl_session_t *s = 0;
2085   svm_fifo_t *rx_fifo;
2086   svm_msg_q_t *mq;
2087   u8 is_ct;
2088
2089   s = vcl_session_get_w_handle (wrk, session_handle);
2090   if (PREDICT_FALSE (!s || (s->flags & VCL_SESSION_F_IS_VEP)))
2091     return VPPCOM_EBADFD;
2092
2093   if (PREDICT_FALSE (!vcl_session_is_open (s)))
2094     return vcl_session_closed_error (s);
2095
2096   is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
2097   is_ct = vcl_session_is_ct (s);
2098   mq = wrk->app_event_queue;
2099   rx_fifo = is_ct ? s->ct_rx_fifo : s->rx_fifo;
2100   s->flags &= ~VCL_SESSION_F_HAS_RX_EVT;
2101
2102   if (svm_fifo_is_empty_cons (rx_fifo))
2103     {
2104       if (is_nonblocking)
2105         {
2106           if (is_ct)
2107             svm_fifo_unset_event (s->rx_fifo);
2108           svm_fifo_unset_event (rx_fifo);
2109           return VPPCOM_EWOULDBLOCK;
2110         }
2111       while (svm_fifo_is_empty_cons (rx_fifo))
2112         {
2113           if (vcl_session_is_closing (s))
2114             return vcl_session_closing_error (s);
2115
2116           if (is_ct)
2117             svm_fifo_unset_event (s->rx_fifo);
2118           svm_fifo_unset_event (rx_fifo);
2119
2120           svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2121           vcl_worker_flush_mq_events (wrk);
2122         }
2123     }
2124
2125   n_read = svm_fifo_segments (rx_fifo, s->rx_bytes_pending,
2126                               (svm_fifo_seg_t *) ds, n_segments, max_bytes);
2127   if (n_read < 0)
2128     return VPPCOM_EAGAIN;
2129
2130   if (svm_fifo_max_dequeue_cons (rx_fifo) == n_read)
2131     {
2132       if (is_ct)
2133         svm_fifo_unset_event (s->rx_fifo);
2134       svm_fifo_unset_event (rx_fifo);
2135       if (svm_fifo_max_dequeue_cons (rx_fifo) != n_read
2136           && svm_fifo_set_event (rx_fifo)
2137           && vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
2138         {
2139           session_event_t *e;
2140           vec_add2 (wrk->unhandled_evts_vector, e, 1);
2141           e->event_type = SESSION_IO_EVT_RX;
2142           e->session_index = s->session_index;
2143         }
2144     }
2145
2146   s->rx_bytes_pending += n_read;
2147   return n_read;
2148 }
2149
2150 void
2151 vppcom_session_free_segments (uint32_t session_handle, uint32_t n_bytes)
2152 {
2153   vcl_worker_t *wrk = vcl_worker_get_current ();
2154   vcl_session_t *s;
2155   u8 is_ct;
2156
2157   s = vcl_session_get_w_handle (wrk, session_handle);
2158   if (PREDICT_FALSE (!s || (s->flags & VCL_SESSION_F_IS_VEP)))
2159     return;
2160
2161   is_ct = vcl_session_is_ct (s);
2162   svm_fifo_dequeue_drop (is_ct ? s->ct_rx_fifo : s->rx_fifo, n_bytes);
2163
2164   ASSERT (s->rx_bytes_pending < n_bytes);
2165   s->rx_bytes_pending -= n_bytes;
2166 }
2167
2168 always_inline u8
2169 vcl_fifo_is_writeable (svm_fifo_t * f, u32 len, u8 is_dgram)
2170 {
2171   u32 max_enq = svm_fifo_max_enqueue_prod (f);
2172   if (is_dgram)
2173     return max_enq >= (sizeof (session_dgram_hdr_t) + len);
2174   else
2175     return max_enq > 0;
2176 }
2177
2178 always_inline int
2179 vppcom_session_write_inline (vcl_worker_t * wrk, vcl_session_t * s, void *buf,
2180                              size_t n, u8 is_flush, u8 is_dgram)
2181 {
2182   int n_write, is_nonblocking;
2183   session_evt_type_t et;
2184   svm_fifo_t *tx_fifo;
2185   svm_msg_q_t *mq;
2186   u8 is_ct;
2187
2188   /* Accept zero length writes but just return */
2189   if (PREDICT_FALSE (!n))
2190     return VPPCOM_OK;
2191
2192   if (PREDICT_FALSE (!buf))
2193     return VPPCOM_EFAULT;
2194
2195   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_IS_VEP))
2196     {
2197       VDBG (0, "ERROR: session %u [0x%llx]: cannot write to an epoll"
2198             " session!", s->session_index, s->vpp_handle);
2199       return VPPCOM_EBADFD;
2200     }
2201
2202   if (PREDICT_FALSE (!vcl_session_is_open (s)))
2203     {
2204       VDBG (1, "session %u [0x%llx]: is not open! state 0x%x (%s)",
2205             s->session_index, s->vpp_handle, s->session_state,
2206             vppcom_session_state_str (s->session_state));
2207       return vcl_session_closed_error (s);;
2208     }
2209
2210   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_WR_SHUTDOWN))
2211     {
2212       VDBG (1, "session %u [0x%llx]: is shutdown! state 0x%x (%s)",
2213             s->session_index, s->vpp_handle, s->session_state,
2214             vppcom_session_state_str (s->session_state));
2215       return VPPCOM_EPIPE;
2216     }
2217
2218   is_ct = vcl_session_is_ct (s);
2219   tx_fifo = is_ct ? s->ct_tx_fifo : s->tx_fifo;
2220   is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
2221
2222   mq = wrk->app_event_queue;
2223   if (!vcl_fifo_is_writeable (tx_fifo, n, is_dgram))
2224     {
2225       if (is_nonblocking)
2226         {
2227           return VPPCOM_EWOULDBLOCK;
2228         }
2229       while (!vcl_fifo_is_writeable (tx_fifo, n, is_dgram))
2230         {
2231           svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
2232           if (vcl_session_is_closing (s))
2233             return vcl_session_closing_error (s);
2234
2235           svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2236           vcl_worker_flush_mq_events (wrk);
2237         }
2238     }
2239
2240   et = SESSION_IO_EVT_TX;
2241   if (is_flush && !is_ct)
2242     et = SESSION_IO_EVT_TX_FLUSH;
2243
2244   if (is_dgram)
2245     n_write = app_send_dgram_raw (tx_fifo, &s->transport,
2246                                   s->vpp_evt_q, buf, n, et,
2247                                   0 /* do_evt */ , SVM_Q_WAIT);
2248   else
2249     n_write = app_send_stream_raw (tx_fifo, s->vpp_evt_q, buf, n, et,
2250                                    0 /* do_evt */ , SVM_Q_WAIT);
2251
2252   if (svm_fifo_set_event (s->tx_fifo))
2253     app_send_io_evt_to_vpp (
2254       s->vpp_evt_q, s->tx_fifo->shr->master_session_index, et, SVM_Q_WAIT);
2255
2256   /* The underlying fifo segment can run out of memory */
2257   if (PREDICT_FALSE (n_write < 0))
2258     return VPPCOM_EAGAIN;
2259
2260   VDBG (2, "session %u [0x%llx]: wrote %d bytes", s->session_index,
2261         s->vpp_handle, n_write);
2262
2263   return n_write;
2264 }
2265
2266 int
2267 vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
2268 {
2269   vcl_worker_t *wrk = vcl_worker_get_current ();
2270   vcl_session_t *s;
2271
2272   s = vcl_session_get_w_handle (wrk, session_handle);
2273   if (PREDICT_FALSE (!s))
2274     return VPPCOM_EBADFD;
2275
2276   return vppcom_session_write_inline (wrk, s, buf, n,
2277                                       0 /* is_flush */ , s->is_dgram ? 1 : 0);
2278 }
2279
2280 int
2281 vppcom_session_write_msg (uint32_t session_handle, void *buf, size_t n)
2282 {
2283   vcl_worker_t *wrk = vcl_worker_get_current ();
2284   vcl_session_t *s;
2285
2286   s = vcl_session_get_w_handle (wrk, session_handle);
2287   if (PREDICT_FALSE (!s))
2288     return VPPCOM_EBADFD;
2289
2290   return vppcom_session_write_inline (wrk, s, buf, n,
2291                                       1 /* is_flush */ , s->is_dgram ? 1 : 0);
2292 }
2293
2294 #define vcl_fifo_rx_evt_valid_or_break(_s)                              \
2295 if (PREDICT_FALSE (!_s->rx_fifo))                                       \
2296   break;                                                                \
2297 if (PREDICT_FALSE (svm_fifo_is_empty (_s->rx_fifo)))                    \
2298   {                                                                     \
2299     if (!vcl_session_is_ct (_s))                                        \
2300       {                                                                 \
2301         svm_fifo_unset_event (_s->rx_fifo);                             \
2302         if (svm_fifo_is_empty (_s->rx_fifo))                            \
2303           break;                                                        \
2304       }                                                                 \
2305     else if (svm_fifo_is_empty (_s->ct_rx_fifo))                        \
2306       {                                                                 \
2307         svm_fifo_unset_event (_s->rx_fifo); /* rx evts on actual fifo*/ \
2308         if (svm_fifo_is_empty (_s->ct_rx_fifo))                         \
2309           break;                                                        \
2310       }                                                                 \
2311   }                                                                     \
2312
2313 static void
2314 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2315                             unsigned long n_bits, unsigned long *read_map,
2316                             unsigned long *write_map,
2317                             unsigned long *except_map, u32 * bits_set)
2318 {
2319   session_disconnected_msg_t *disconnected_msg;
2320   session_connected_msg_t *connected_msg;
2321   vcl_session_t *s;
2322   u32 sid;
2323
2324   switch (e->event_type)
2325     {
2326     case SESSION_IO_EVT_RX:
2327       sid = e->session_index;
2328       s = vcl_session_get (wrk, sid);
2329       if (!s || !vcl_session_is_open (s))
2330         break;
2331       vcl_fifo_rx_evt_valid_or_break (s);
2332       if (sid < n_bits && read_map)
2333         {
2334           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2335           *bits_set += 1;
2336         }
2337       break;
2338     case SESSION_IO_EVT_TX:
2339       sid = e->session_index;
2340       s = vcl_session_get (wrk, sid);
2341       if (!s || !vcl_session_is_open (s))
2342         break;
2343       if (sid < n_bits && write_map)
2344         {
2345           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2346           *bits_set += 1;
2347         }
2348       break;
2349     case SESSION_CTRL_EVT_ACCEPTED:
2350       if (!e->postponed)
2351         s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
2352       else
2353         s = vcl_session_get (wrk, e->session_index);
2354       if (!s)
2355         break;
2356       sid = s->session_index;
2357       if (sid < n_bits && read_map)
2358         {
2359           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2360           *bits_set += 1;
2361         }
2362       break;
2363     case SESSION_CTRL_EVT_CONNECTED:
2364       if (!e->postponed)
2365         {
2366           connected_msg = (session_connected_msg_t *) e->data;
2367           sid = vcl_session_connected_handler (wrk, connected_msg);
2368         }
2369       else
2370         sid = e->session_index;
2371       if (sid == VCL_INVALID_SESSION_INDEX)
2372         break;
2373       if (sid < n_bits && write_map)
2374         {
2375           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2376           *bits_set += 1;
2377         }
2378       break;
2379     case SESSION_CTRL_EVT_DISCONNECTED:
2380       disconnected_msg = (session_disconnected_msg_t *) e->data;
2381       s = vcl_session_disconnected_handler (wrk, disconnected_msg);
2382       if (!s)
2383         break;
2384       sid = s->session_index;
2385       if (sid < n_bits && except_map)
2386         {
2387           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2388           *bits_set += 1;
2389         }
2390       break;
2391     case SESSION_CTRL_EVT_RESET:
2392       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2393       if (sid < n_bits && except_map)
2394         {
2395           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2396           *bits_set += 1;
2397         }
2398       break;
2399     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
2400       vcl_session_unlisten_reply_handler (wrk, e->data);
2401       break;
2402     case SESSION_CTRL_EVT_MIGRATED:
2403       vcl_session_migrated_handler (wrk, e->data);
2404       break;
2405     case SESSION_CTRL_EVT_CLEANUP:
2406       vcl_session_cleanup_handler (wrk, e->data);
2407       break;
2408     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
2409       vcl_session_worker_update_reply_handler (wrk, e->data);
2410       break;
2411     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
2412       vcl_session_req_worker_update_handler (wrk, e->data);
2413       break;
2414     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
2415       vcl_session_app_add_segment_handler (wrk, e->data);
2416       break;
2417     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
2418       vcl_session_app_del_segment_handler (wrk, e->data);
2419       break;
2420     case SESSION_CTRL_EVT_APP_WRK_RPC:
2421       vcl_worker_rpc_handler (wrk, e->data);
2422       break;
2423     default:
2424       clib_warning ("unhandled: %u", e->event_type);
2425       break;
2426     }
2427 }
2428
2429 static int
2430 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2431                       unsigned long n_bits, unsigned long *read_map,
2432                       unsigned long *write_map, unsigned long *except_map,
2433                       double time_to_wait, u32 * bits_set)
2434 {
2435   svm_msg_q_msg_t *msg;
2436   session_event_t *e;
2437   u32 i;
2438
2439   if (svm_msg_q_is_empty (mq))
2440     {
2441       if (*bits_set)
2442         return 0;
2443
2444       if (!time_to_wait)
2445         return 0;
2446       else if (time_to_wait < 0)
2447         svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2448       else
2449         {
2450           if (svm_msg_q_timedwait (mq, time_to_wait))
2451             return 0;
2452         }
2453     }
2454   vcl_mq_dequeue_batch (wrk, mq, ~0);
2455
2456   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2457     {
2458       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2459       e = svm_msg_q_msg_data (mq, msg);
2460       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
2461                                   except_map, bits_set);
2462       svm_msg_q_free_msg (mq, msg);
2463     }
2464   vec_reset_length (wrk->mq_msg_vector);
2465   vcl_handle_pending_wrk_updates (wrk);
2466   return *bits_set;
2467 }
2468
2469 static int
2470 vppcom_select_condvar (vcl_worker_t * wrk, int n_bits,
2471                        vcl_si_set * read_map, vcl_si_set * write_map,
2472                        vcl_si_set * except_map, double time_to_wait,
2473                        u32 * bits_set)
2474 {
2475   double wait = 0, start = 0;
2476
2477   if (!*bits_set)
2478     {
2479       wait = time_to_wait;
2480       start = clib_time_now (&wrk->clib_time);
2481     }
2482
2483   do
2484     {
2485       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
2486                             write_map, except_map, wait, bits_set);
2487       if (*bits_set)
2488         return *bits_set;
2489       if (wait == -1)
2490         continue;
2491
2492       wait = wait - (clib_time_now (&wrk->clib_time) - start);
2493     }
2494   while (wait > 0);
2495
2496   return 0;
2497 }
2498
2499 static int
2500 vppcom_select_eventfd (vcl_worker_t * wrk, int n_bits,
2501                        vcl_si_set * read_map, vcl_si_set * write_map,
2502                        vcl_si_set * except_map, double time_to_wait,
2503                        u32 * bits_set)
2504 {
2505   vcl_mq_evt_conn_t *mqc;
2506   int __clib_unused n_read;
2507   int n_mq_evts, i;
2508   u64 buf;
2509
2510   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2511   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2512                           vec_len (wrk->mq_events), time_to_wait);
2513   for (i = 0; i < n_mq_evts; i++)
2514     {
2515       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2516       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2517       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
2518                             except_map, 0, bits_set);
2519     }
2520
2521   return (n_mq_evts > 0 ? (int) *bits_set : 0);
2522 }
2523
2524 int
2525 vppcom_select (int n_bits, vcl_si_set * read_map, vcl_si_set * write_map,
2526                vcl_si_set * except_map, double time_to_wait)
2527 {
2528   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
2529   vcl_worker_t *wrk = vcl_worker_get_current ();
2530   vcl_session_t *s = 0;
2531   int i;
2532
2533   if (n_bits && read_map)
2534     {
2535       clib_bitmap_validate (wrk->rd_bitmap, minbits);
2536       clib_memcpy_fast (wrk->rd_bitmap, read_map,
2537                         vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2538       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2539     }
2540   if (n_bits && write_map)
2541     {
2542       clib_bitmap_validate (wrk->wr_bitmap, minbits);
2543       clib_memcpy_fast (wrk->wr_bitmap, write_map,
2544                         vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2545       memset (write_map, 0, vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2546     }
2547   if (n_bits && except_map)
2548     {
2549       clib_bitmap_validate (wrk->ex_bitmap, minbits);
2550       clib_memcpy_fast (wrk->ex_bitmap, except_map,
2551                         vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2552       memset (except_map, 0, vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2553     }
2554
2555   if (!n_bits)
2556     return 0;
2557
2558   if (!write_map)
2559     goto check_rd;
2560
2561   clib_bitmap_foreach (sid, wrk->wr_bitmap)
2562     {
2563       if (!(s = vcl_session_get (wrk, sid)))
2564         {
2565           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2566           bits_set++;
2567           continue;
2568         }
2569
2570       if (vcl_session_write_ready (s))
2571         {
2572           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2573           bits_set++;
2574         }
2575       else
2576         {
2577           svm_fifo_t *txf = vcl_session_is_ct (s) ? s->ct_tx_fifo : s->tx_fifo;
2578           svm_fifo_add_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF);
2579         }
2580     }
2581
2582 check_rd:
2583   if (!read_map)
2584     goto check_mq;
2585
2586   clib_bitmap_foreach (sid, wrk->rd_bitmap)
2587     {
2588       if (!(s = vcl_session_get (wrk, sid)))
2589         {
2590           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2591           bits_set++;
2592           continue;
2593         }
2594
2595       if (vcl_session_read_ready (s))
2596         {
2597           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2598           bits_set++;
2599         }
2600     }
2601
2602 check_mq:
2603
2604   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2605     {
2606       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
2607                                   read_map, write_map, except_map, &bits_set);
2608     }
2609   vec_reset_length (wrk->unhandled_evts_vector);
2610
2611   if (vcm->cfg.use_mq_eventfd)
2612     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
2613                            time_to_wait, &bits_set);
2614   else
2615     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
2616                            time_to_wait, &bits_set);
2617
2618   return (bits_set);
2619 }
2620
2621 static inline void
2622 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_handle)
2623 {
2624   vppcom_epoll_t *vep;
2625   u32 sh = vep_handle;
2626   vcl_session_t *s;
2627
2628   if (VPPCOM_DEBUG <= 2)
2629     return;
2630
2631   s = vcl_session_get_w_handle (wrk, vep_handle);
2632   if (PREDICT_FALSE (!s))
2633     {
2634       VDBG (0, "ERROR: Invalid vep_sh (%u)!", vep_handle);
2635       goto done;
2636     }
2637   if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP)))
2638     {
2639       VDBG (0, "ERROR: vep_sh (%u) is not a vep!", vep_handle);
2640       goto done;
2641     }
2642   vep = &s->vep;
2643   VDBG (0, "vep_sh (%u): Dumping epoll chain\n"
2644         "{\n"
2645         "   is_vep         = %u\n"
2646         "   is_vep_session = %u\n"
2647         "   next_sh        = 0x%x (%u)\n"
2648         "}\n", vep_handle, s->flags & VCL_SESSION_F_IS_VEP,
2649         s->flags & VCL_SESSION_F_IS_VEP_SESSION, vep->next_sh, vep->next_sh);
2650
2651   for (sh = vep->next_sh; sh != ~0; sh = vep->next_sh)
2652     {
2653       s = vcl_session_get_w_handle (wrk, sh);
2654       if (PREDICT_FALSE (!s))
2655         {
2656           VDBG (0, "ERROR: Invalid sh (%u)!", sh);
2657           goto done;
2658         }
2659       if (PREDICT_FALSE (s->flags & VCL_SESSION_F_IS_VEP))
2660         {
2661           VDBG (0, "ERROR: sh (%u) is a vep!", vep_handle);
2662         }
2663       else if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2664         {
2665           VDBG (0, "ERROR: sh (%u) is not a vep session handle!", sh);
2666           goto done;
2667         }
2668       vep = &s->vep;
2669       if (PREDICT_FALSE (vep->vep_sh != vep_handle))
2670         VDBG (0, "ERROR: session (%u) vep_sh (%u) != vep_sh (%u)!",
2671               sh, s->vep.vep_sh, vep_handle);
2672       if (s->flags & VCL_SESSION_F_IS_VEP_SESSION)
2673         {
2674           VDBG (0, "vep_sh[%u]: sh 0x%x (%u)\n"
2675                 "{\n"
2676                 "   next_sh        = 0x%x (%u)\n"
2677                 "   prev_sh        = 0x%x (%u)\n"
2678                 "   vep_sh         = 0x%x (%u)\n"
2679                 "   ev.events      = 0x%x\n"
2680                 "   ev.data.u64    = 0x%llx\n"
2681                 "   et_mask        = 0x%x\n"
2682                 "}\n",
2683                 vep_handle, sh, sh, vep->next_sh, vep->next_sh, vep->prev_sh,
2684                 vep->prev_sh, vep->vep_sh, vep->vep_sh, vep->ev.events,
2685                 vep->ev.data.u64, vep->et_mask);
2686         }
2687     }
2688
2689 done:
2690   VDBG (0, "vep_sh (%u): Dump complete!\n", vep_handle);
2691 }
2692
2693 int
2694 vppcom_epoll_create (void)
2695 {
2696   vcl_worker_t *wrk = vcl_worker_get_current ();
2697   vcl_session_t *vep_session;
2698
2699   vep_session = vcl_session_alloc (wrk);
2700
2701   vep_session->flags |= VCL_SESSION_F_IS_VEP;
2702   vep_session->vep.vep_sh = ~0;
2703   vep_session->vep.next_sh = ~0;
2704   vep_session->vep.prev_sh = ~0;
2705   vep_session->vpp_handle = ~0;
2706
2707   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_session->session_index);
2708   VDBG (0, "Created vep_idx %u", vep_session->session_index);
2709
2710   return vcl_session_handle (vep_session);
2711 }
2712
2713 int
2714 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2715                   struct epoll_event *event)
2716 {
2717   vcl_worker_t *wrk = vcl_worker_get_current ();
2718   int rv = VPPCOM_OK, add_evt = 0;
2719   vcl_session_t *vep_session;
2720   vcl_session_t *s;
2721   svm_fifo_t *txf;
2722
2723   if (vep_handle == session_handle)
2724     {
2725       VDBG (0, "vep_sh == session handle (%u)!", vep_handle);
2726       return VPPCOM_EINVAL;
2727     }
2728
2729   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2730   if (PREDICT_FALSE (!vep_session))
2731     {
2732       VDBG (0, "Invalid vep_sh (%u)!", vep_handle);
2733       return VPPCOM_EBADFD;
2734     }
2735   if (PREDICT_FALSE (!(vep_session->flags & VCL_SESSION_F_IS_VEP)))
2736     {
2737       VDBG (0, "vep_sh (%u) is not a vep!", vep_handle);
2738       return VPPCOM_EINVAL;
2739     }
2740
2741   ASSERT (vep_session->vep.vep_sh == ~0);
2742   ASSERT (vep_session->vep.prev_sh == ~0);
2743
2744   s = vcl_session_get_w_handle (wrk, session_handle);
2745   if (PREDICT_FALSE (!s))
2746     {
2747       VDBG (0, "Invalid session_handle (%u)!", session_handle);
2748       return VPPCOM_EBADFD;
2749     }
2750   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_IS_VEP))
2751     {
2752       VDBG (0, "session_handle (%u) is a vep!", vep_handle);
2753       return VPPCOM_EINVAL;
2754     }
2755
2756   switch (op)
2757     {
2758     case EPOLL_CTL_ADD:
2759       if (PREDICT_FALSE (!event))
2760         {
2761           VDBG (0, "EPOLL_CTL_ADD: NULL pointer to epoll_event structure!");
2762           return VPPCOM_EINVAL;
2763         }
2764       if (s->flags & VCL_SESSION_F_IS_VEP_SESSION)
2765         {
2766           VDBG (0, "EPOLL_CTL_ADD: %u already epolled!", s->session_index);
2767           rv = VPPCOM_EEXIST;
2768           goto done;
2769         }
2770       if (vep_session->vep.next_sh != ~0)
2771         {
2772           vcl_session_t *next_session;
2773           next_session = vcl_session_get_w_handle (wrk,
2774                                                    vep_session->vep.next_sh);
2775           if (PREDICT_FALSE (!next_session))
2776             {
2777               VDBG (0, "EPOLL_CTL_ADD: Invalid vep.next_sh (%u) on "
2778                     "vep_idx (%u)!", vep_session->vep.next_sh, vep_handle);
2779               return VPPCOM_EBADFD;
2780             }
2781           ASSERT (next_session->vep.prev_sh == vep_handle);
2782           next_session->vep.prev_sh = session_handle;
2783         }
2784       s->vep.next_sh = vep_session->vep.next_sh;
2785       s->vep.prev_sh = vep_handle;
2786       s->vep.vep_sh = vep_handle;
2787       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
2788       s->vep.ev = *event;
2789       s->flags &= ~VCL_SESSION_F_IS_VEP;
2790       s->flags |= VCL_SESSION_F_IS_VEP_SESSION;
2791       vep_session->vep.next_sh = session_handle;
2792
2793       txf = vcl_session_is_ct (s) ? s->ct_tx_fifo : s->tx_fifo;
2794       if (txf && (event->events & EPOLLOUT))
2795         svm_fifo_add_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2796
2797       /* Generate EPOLLOUT if tx fifo not full */
2798       if ((event->events & EPOLLOUT) && (vcl_session_write_ready (s) > 0))
2799         {
2800           session_event_t e = { 0 };
2801           e.event_type = SESSION_IO_EVT_TX;
2802           e.session_index = s->session_index;
2803           vec_add1 (wrk->unhandled_evts_vector, e);
2804           add_evt = 1;
2805         }
2806       /* Generate EPOLLIN if rx fifo has data */
2807       if ((event->events & EPOLLIN) && (vcl_session_read_ready (s) > 0))
2808         {
2809           session_event_t e = { 0 };
2810           e.event_type = SESSION_IO_EVT_RX;
2811           e.session_index = s->session_index;
2812           vec_add1 (wrk->unhandled_evts_vector, e);
2813           s->flags &= ~VCL_SESSION_F_HAS_RX_EVT;
2814           add_evt = 1;
2815         }
2816       if (!add_evt && vcl_session_is_closing (s))
2817         {
2818           session_event_t e = { 0 };
2819           if (s->session_state == VCL_STATE_VPP_CLOSING)
2820             e.event_type = SESSION_CTRL_EVT_DISCONNECTED;
2821           else
2822             e.event_type = SESSION_CTRL_EVT_RESET;
2823           e.session_index = s->session_index;
2824           e.postponed = 1;
2825           vec_add1 (wrk->unhandled_evts_vector, e);
2826         }
2827       VDBG (1, "EPOLL_CTL_ADD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2828             vep_handle, session_handle, event->events, event->data.u64);
2829       vcl_evt (VCL_EVT_EPOLL_CTLADD, s, event->events, event->data.u64);
2830       break;
2831
2832     case EPOLL_CTL_MOD:
2833       if (PREDICT_FALSE (!event))
2834         {
2835           VDBG (0, "EPOLL_CTL_MOD: NULL pointer to epoll_event structure!");
2836           rv = VPPCOM_EINVAL;
2837           goto done;
2838         }
2839       else if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2840         {
2841           VDBG (0, "sh %u EPOLL_CTL_MOD: not a vep session!", session_handle);
2842           rv = VPPCOM_ENOENT;
2843           goto done;
2844         }
2845       else if (PREDICT_FALSE (s->vep.vep_sh != vep_handle))
2846         {
2847           VDBG (0, "EPOLL_CTL_MOD: sh %u vep_sh (%u) != vep_sh (%u)!",
2848                 session_handle, s->vep.vep_sh, vep_handle);
2849           rv = VPPCOM_EINVAL;
2850           goto done;
2851         }
2852
2853       /* Generate EPOLLOUT if session write ready nd event was not on */
2854       if ((event->events & EPOLLOUT) && !(s->vep.ev.events & EPOLLOUT) &&
2855           (vcl_session_write_ready (s) > 0))
2856         {
2857           session_event_t e = { 0 };
2858           e.event_type = SESSION_IO_EVT_TX;
2859           e.session_index = s->session_index;
2860           vec_add1 (wrk->unhandled_evts_vector, e);
2861         }
2862       /* Generate EPOLLIN if session read ready and event was not on */
2863       if ((event->events & EPOLLIN) && !(s->vep.ev.events & EPOLLIN) &&
2864           (vcl_session_read_ready (s) > 0))
2865         {
2866           session_event_t e = { 0 };
2867           e.event_type = SESSION_IO_EVT_RX;
2868           e.session_index = s->session_index;
2869           vec_add1 (wrk->unhandled_evts_vector, e);
2870           s->flags &= ~VCL_SESSION_F_HAS_RX_EVT;
2871         }
2872       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
2873       s->vep.ev = *event;
2874       txf = vcl_session_is_ct (s) ? s->ct_tx_fifo : s->tx_fifo;
2875       if (txf)
2876         {
2877           if (event->events & EPOLLOUT)
2878             svm_fifo_add_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2879           else
2880             svm_fifo_del_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2881         }
2882       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2883             vep_handle, session_handle, event->events, event->data.u64);
2884       break;
2885
2886     case EPOLL_CTL_DEL:
2887       if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2888         {
2889           VDBG (0, "EPOLL_CTL_DEL: %u not a vep session!", session_handle);
2890           rv = VPPCOM_ENOENT;
2891           goto done;
2892         }
2893       else if (PREDICT_FALSE (s->vep.vep_sh != vep_handle))
2894         {
2895           VDBG (0, "EPOLL_CTL_DEL: sh %u vep_sh (%u) != vep_sh (%u)!",
2896                 session_handle, s->vep.vep_sh, vep_handle);
2897           rv = VPPCOM_EINVAL;
2898           goto done;
2899         }
2900
2901       if (s->vep.prev_sh == vep_handle)
2902         vep_session->vep.next_sh = s->vep.next_sh;
2903       else
2904         {
2905           vcl_session_t *prev_session;
2906           prev_session = vcl_session_get_w_handle (wrk, s->vep.prev_sh);
2907           if (PREDICT_FALSE (!prev_session))
2908             {
2909               VDBG (0, "EPOLL_CTL_DEL: Invalid prev_sh (%u) on sh (%u)!",
2910                     s->vep.prev_sh, session_handle);
2911               return VPPCOM_EBADFD;
2912             }
2913           ASSERT (prev_session->vep.next_sh == session_handle);
2914           prev_session->vep.next_sh = s->vep.next_sh;
2915         }
2916       if (s->vep.next_sh != ~0)
2917         {
2918           vcl_session_t *next_session;
2919           next_session = vcl_session_get_w_handle (wrk, s->vep.next_sh);
2920           if (PREDICT_FALSE (!next_session))
2921             {
2922               VDBG (0, "EPOLL_CTL_DEL: Invalid next_sh (%u) on sh (%u)!",
2923                     s->vep.next_sh, session_handle);
2924               return VPPCOM_EBADFD;
2925             }
2926           ASSERT (next_session->vep.prev_sh == session_handle);
2927           next_session->vep.prev_sh = s->vep.prev_sh;
2928         }
2929
2930       memset (&s->vep, 0, sizeof (s->vep));
2931       s->vep.next_sh = ~0;
2932       s->vep.prev_sh = ~0;
2933       s->vep.vep_sh = ~0;
2934       s->flags &= ~VCL_SESSION_F_IS_VEP_SESSION;
2935
2936       if (vcl_session_is_open (s))
2937         {
2938           txf = vcl_session_is_ct (s) ? s->ct_tx_fifo : s->tx_fifo;
2939           if (txf)
2940             svm_fifo_del_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2941         }
2942
2943       VDBG (1, "EPOLL_CTL_DEL: vep_idx %u, sh %u!", vep_handle,
2944             session_handle);
2945       vcl_evt (VCL_EVT_EPOLL_CTLDEL, s, vep_sh);
2946       break;
2947
2948     default:
2949       VDBG (0, "Invalid operation (%d)!", op);
2950       rv = VPPCOM_EINVAL;
2951     }
2952
2953   vep_verify_epoll_chain (wrk, vep_handle);
2954
2955 done:
2956   return rv;
2957 }
2958
2959 static inline void
2960 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2961                                 struct epoll_event *events, u32 * num_ev)
2962 {
2963   session_disconnected_msg_t *disconnected_msg;
2964   session_connected_msg_t *connected_msg;
2965   u32 sid = ~0, session_events;
2966   u64 session_evt_data = ~0;
2967   vcl_session_t *s;
2968   u8 add_event = 0;
2969
2970   switch (e->event_type)
2971     {
2972     case SESSION_IO_EVT_RX:
2973       sid = e->session_index;
2974       s = vcl_session_get (wrk, sid);
2975       if (vcl_session_is_closed (s))
2976         break;
2977       vcl_fifo_rx_evt_valid_or_break (s);
2978       session_events = s->vep.ev.events;
2979       if (!(EPOLLIN & s->vep.ev.events)
2980           || (s->flags & VCL_SESSION_F_HAS_RX_EVT))
2981         break;
2982       add_event = 1;
2983       events[*num_ev].events |= EPOLLIN;
2984       session_evt_data = s->vep.ev.data.u64;
2985       s->flags |= VCL_SESSION_F_HAS_RX_EVT;
2986       break;
2987     case SESSION_IO_EVT_TX:
2988       sid = e->session_index;
2989       s = vcl_session_get (wrk, sid);
2990       if (vcl_session_is_closed (s))
2991         break;
2992       session_events = s->vep.ev.events;
2993       if (!(EPOLLOUT & session_events))
2994         break;
2995       add_event = 1;
2996       events[*num_ev].events |= EPOLLOUT;
2997       session_evt_data = s->vep.ev.data.u64;
2998       svm_fifo_reset_has_deq_ntf (vcl_session_is_ct (s) ?
2999                                   s->ct_tx_fifo : s->tx_fifo);
3000       break;
3001     case SESSION_CTRL_EVT_ACCEPTED:
3002       if (!e->postponed)
3003         s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
3004       else
3005         s = vcl_session_get (wrk, e->session_index);
3006       if (!s)
3007         break;
3008       session_events = s->vep.ev.events;
3009       sid = s->session_index;
3010       if (!(EPOLLIN & session_events))
3011         break;
3012       add_event = 1;
3013       events[*num_ev].events |= EPOLLIN;
3014       session_evt_data = s->vep.ev.data.u64;
3015       break;
3016     case SESSION_CTRL_EVT_CONNECTED:
3017       if (!e->postponed)
3018         {
3019           connected_msg = (session_connected_msg_t *) e->data;
3020           sid = vcl_session_connected_handler (wrk, connected_msg);
3021         }
3022       else
3023         sid = e->session_index;
3024       s = vcl_session_get (wrk, sid);
3025       if (vcl_session_is_closed (s))
3026         break;
3027       session_events = s->vep.ev.events;
3028       /* Generate EPOLLOUT because there's no connected event */
3029       if (!(EPOLLOUT & session_events))
3030         break;
3031       add_event = 1;
3032       events[*num_ev].events |= EPOLLOUT;
3033       session_evt_data = s->vep.ev.data.u64;
3034       if (s->session_state == VCL_STATE_DETACHED)
3035         events[*num_ev].events |= EPOLLHUP;
3036       break;
3037     case SESSION_CTRL_EVT_DISCONNECTED:
3038       if (!e->postponed)
3039         {
3040           disconnected_msg = (session_disconnected_msg_t *) e->data;
3041           s = vcl_session_disconnected_handler (wrk, disconnected_msg);
3042         }
3043       else
3044         {
3045           s = vcl_session_get (wrk, e->session_index);
3046         }
3047       if (vcl_session_is_closed (s) ||
3048           !(s->flags & VCL_SESSION_F_IS_VEP_SESSION))
3049         break;
3050       sid = s->session_index;
3051       session_events = s->vep.ev.events;
3052       add_event = 1;
3053       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
3054       session_evt_data = s->vep.ev.data.u64;
3055       break;
3056     case SESSION_CTRL_EVT_RESET:
3057       if (!e->postponed)
3058         sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
3059       else
3060         sid = e->session_index;
3061       s = vcl_session_get (wrk, sid);
3062       if (vcl_session_is_closed (s) ||
3063           !(s->flags & VCL_SESSION_F_IS_VEP_SESSION))
3064         break;
3065       session_events = s->vep.ev.events;
3066       add_event = 1;
3067       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
3068       session_evt_data = s->vep.ev.data.u64;
3069       break;
3070     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
3071       vcl_session_unlisten_reply_handler (wrk, e->data);
3072       break;
3073     case SESSION_CTRL_EVT_MIGRATED:
3074       vcl_session_migrated_handler (wrk, e->data);
3075       break;
3076     case SESSION_CTRL_EVT_CLEANUP:
3077       vcl_session_cleanup_handler (wrk, e->data);
3078       break;
3079     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
3080       vcl_session_req_worker_update_handler (wrk, e->data);
3081       break;
3082     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
3083       vcl_session_worker_update_reply_handler (wrk, e->data);
3084       break;
3085     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
3086       vcl_session_app_add_segment_handler (wrk, e->data);
3087       break;
3088     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
3089       vcl_session_app_del_segment_handler (wrk, e->data);
3090       break;
3091     case SESSION_CTRL_EVT_APP_WRK_RPC:
3092       vcl_worker_rpc_handler (wrk, e->data);
3093       break;
3094     default:
3095       VDBG (0, "unhandled: %u", e->event_type);
3096       break;
3097     }
3098
3099   if (add_event)
3100     {
3101       events[*num_ev].data.u64 = session_evt_data;
3102       if (EPOLLONESHOT & session_events)
3103         {
3104           s = vcl_session_get (wrk, sid);
3105           s->vep.ev.events = 0;
3106         }
3107       if (!(EPOLLET & session_events))
3108         {
3109           vec_add1 (wrk->ep_level_evts, sid);
3110         }
3111       *num_ev += 1;
3112     }
3113 }
3114
3115 static int
3116 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
3117                           struct epoll_event *events, u32 maxevents,
3118                           double wait_for_time, u32 * num_ev)
3119 {
3120   svm_msg_q_msg_t *msg;
3121   session_event_t *e;
3122   int i;
3123
3124   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
3125     goto handle_dequeued;
3126
3127   if (svm_msg_q_is_empty (mq))
3128     {
3129       if (!wait_for_time)
3130         return 0;
3131       else if (wait_for_time < 0)
3132         svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
3133       else
3134         {
3135           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
3136             return 0;
3137         }
3138     }
3139   ASSERT (maxevents > *num_ev);
3140   vcl_mq_dequeue_batch (wrk, mq, ~0);
3141
3142 handle_dequeued:
3143   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
3144     {
3145       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
3146       e = svm_msg_q_msg_data (mq, msg);
3147       if (*num_ev < maxevents)
3148         vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
3149       else
3150         vcl_handle_mq_event (wrk, e);
3151       svm_msg_q_free_msg (mq, msg);
3152     }
3153   vec_reset_length (wrk->mq_msg_vector);
3154   vcl_handle_pending_wrk_updates (wrk);
3155   return *num_ev;
3156 }
3157
3158 static int
3159 vppcom_epoll_wait_condvar (vcl_worker_t *wrk, struct epoll_event *events,
3160                            int maxevents, u32 n_evts, double timeout_ms)
3161 {
3162   double end = -1;
3163
3164   if (!n_evts)
3165     {
3166       if (timeout_ms > 0)
3167         end = clib_time_now (&wrk->clib_time) + (timeout_ms / 1e3);
3168     }
3169
3170   do
3171     {
3172       vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events, maxevents,
3173                                 timeout_ms, &n_evts);
3174       if (n_evts || !timeout_ms)
3175         return n_evts;
3176     }
3177   while (end == -1 || clib_time_now (&wrk->clib_time) < end);
3178
3179   return 0;
3180 }
3181
3182 static int
3183 vppcom_epoll_wait_eventfd (vcl_worker_t *wrk, struct epoll_event *events,
3184                            int maxevents, u32 n_evts, double timeout_ms)
3185 {
3186   int __clib_unused n_read;
3187   vcl_mq_evt_conn_t *mqc;
3188   int n_mq_evts, i;
3189   double end = -1;
3190   u64 buf;
3191
3192   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
3193   if (!n_evts)
3194     {
3195       if (timeout_ms > 0)
3196         end = clib_time_now (&wrk->clib_time) + (timeout_ms / 1e3);
3197     }
3198
3199   do
3200     {
3201       n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
3202                               vec_len (wrk->mq_events), timeout_ms);
3203       if (n_mq_evts < 0)
3204         {
3205           VDBG (0, "epoll_wait error %u", errno);
3206           return n_evts;
3207         }
3208
3209       for (i = 0; i < n_mq_evts; i++)
3210         {
3211           mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
3212           n_read = read (mqc->mq_fd, &buf, sizeof (buf));
3213           vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0,
3214                                     &n_evts);
3215         }
3216
3217       if (n_evts || !timeout_ms)
3218         return n_evts;
3219     }
3220   while (end == -1 || clib_time_now (&wrk->clib_time) < end);
3221
3222   return 0;
3223 }
3224
3225 static void
3226 vcl_epoll_swap_lt_lists (vcl_worker_t *wrk)
3227 {
3228   u32 *le;
3229
3230   le = wrk->ep_level_evts;
3231   wrk->ep_level_evts = wrk->ep_level_evts_fl;
3232   wrk->ep_level_evts_fl = le;
3233 }
3234
3235 static void
3236 vcl_epoll_wait_handle_lt (vcl_worker_t *wrk, struct epoll_event *events,
3237                           int maxevents, u32 *n_evts)
3238 {
3239   u32 *sid, add_event = 0, *le = wrk->ep_level_evts_fl;
3240   vcl_session_t *s;
3241   u64 evt_data;
3242
3243   if (*n_evts >= maxevents)
3244     {
3245       vec_add (wrk->ep_level_evts, le, vec_len (le));
3246       vec_reset_length (wrk->ep_level_evts_fl);
3247       return;
3248     }
3249
3250   vec_foreach (sid, le)
3251     {
3252       s = vcl_session_get (wrk, sid[0]);
3253       if (!s)
3254         continue;
3255       if ((s->vep.ev.events & EPOLLIN) && vcl_session_read_ready (s))
3256         {
3257           add_event = 1;
3258           events[*n_evts].events |= EPOLLIN;
3259           evt_data = s->vep.ev.data.u64;
3260         }
3261       if ((s->vep.ev.events & EPOLLOUT) && vcl_session_write_ready (s))
3262         {
3263           add_event = 1;
3264           events[*n_evts].events |= EPOLLOUT;
3265           evt_data = s->vep.ev.data.u64;
3266         }
3267       if (add_event)
3268         {
3269           events[*n_evts].data.u64 = evt_data;
3270           *n_evts += 1;
3271           add_event = 0;
3272           vec_add1 (wrk->ep_level_evts, sid[0]);
3273           if (*n_evts == maxevents)
3274             {
3275               u32 pos = (sid - le) + 1;
3276               vec_add (wrk->ep_level_evts, &le[pos], vec_len (le) - pos);
3277               break;
3278             }
3279         }
3280     }
3281
3282   vec_reset_length (wrk->ep_level_evts_fl);
3283 }
3284
3285 int
3286 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
3287                    int maxevents, double wait_for_time)
3288 {
3289   vcl_worker_t *wrk = vcl_worker_get_current ();
3290   vcl_session_t *vep_session;
3291   u32 n_evts = 0, do_lt = 0;
3292   int i;
3293
3294   if (PREDICT_FALSE (maxevents <= 0))
3295     {
3296       VDBG (0, "ERROR: Invalid maxevents (%d)!", maxevents);
3297       return VPPCOM_EINVAL;
3298     }
3299
3300   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
3301   if (!vep_session)
3302     return VPPCOM_EBADFD;
3303
3304   if (PREDICT_FALSE (!(vep_session->flags & VCL_SESSION_F_IS_VEP)))
3305     {
3306       VDBG (0, "ERROR: vep_idx (%u) is not a vep!", vep_handle);
3307       return VPPCOM_EINVAL;
3308     }
3309
3310   memset (events, 0, sizeof (*events) * maxevents);
3311
3312   if (vec_len (wrk->unhandled_evts_vector))
3313     {
3314       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
3315         {
3316           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
3317                                           events, &n_evts);
3318           if (n_evts == maxevents)
3319             {
3320               vec_delete (wrk->unhandled_evts_vector, i + 1, 0);
3321               return n_evts;
3322             }
3323         }
3324       vec_reset_length (wrk->unhandled_evts_vector);
3325     }
3326   /* Request to only drain unhandled */
3327   if ((int) wait_for_time == -2)
3328     return n_evts;
3329
3330   if (PREDICT_FALSE (vec_len (wrk->ep_level_evts)))
3331     {
3332       vcl_epoll_swap_lt_lists (wrk);
3333       do_lt = 1;
3334     }
3335
3336   if (vcm->cfg.use_mq_eventfd)
3337     n_evts = vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
3338                                         wait_for_time);
3339   else
3340     n_evts = vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
3341                                         wait_for_time);
3342
3343   if (PREDICT_FALSE (do_lt))
3344     vcl_epoll_wait_handle_lt (wrk, events, maxevents, &n_evts);
3345
3346   return n_evts;
3347 }
3348
3349 int
3350 vppcom_session_attr (uint32_t session_handle, uint32_t op,
3351                      void *buffer, uint32_t * buflen)
3352 {
3353   vcl_worker_t *wrk = vcl_worker_get_current ();
3354   u32 *flags = buffer;
3355   vppcom_endpt_t *ep = buffer;
3356   transport_endpt_attr_t tea;
3357   vcl_session_t *session;
3358   int rv = VPPCOM_OK;
3359
3360   session = vcl_session_get_w_handle (wrk, session_handle);
3361   if (!session)
3362     return VPPCOM_EBADFD;
3363
3364   switch (op)
3365     {
3366     case VPPCOM_ATTR_GET_NREAD:
3367       rv = vcl_session_read_ready (session);
3368       VDBG (2, "VPPCOM_ATTR_GET_NREAD: sh %u, nread = %d", session_handle,
3369             rv);
3370       break;
3371
3372     case VPPCOM_ATTR_GET_NWRITE:
3373       rv = vcl_session_write_ready (session);
3374       VDBG (2, "VPPCOM_ATTR_GET_NWRITE: sh %u, nwrite = %d", session_handle,
3375             rv);
3376       break;
3377
3378     case VPPCOM_ATTR_GET_FLAGS:
3379       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
3380         {
3381           *flags =
3382             O_RDWR |
3383             (vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK) ?
3384              O_NONBLOCK : 0);
3385           *buflen = sizeof (*flags);
3386           VDBG (2, "VPPCOM_ATTR_GET_FLAGS: sh %u, flags = 0x%08x, "
3387                 "is_nonblocking = %u", session_handle, *flags,
3388                 vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK));
3389         }
3390       else
3391         rv = VPPCOM_EINVAL;
3392       break;
3393
3394     case VPPCOM_ATTR_SET_FLAGS:
3395       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
3396         {
3397           if (*flags & O_NONBLOCK)
3398             vcl_session_set_attr (session, VCL_SESS_ATTR_NONBLOCK);
3399           else
3400             vcl_session_clear_attr (session, VCL_SESS_ATTR_NONBLOCK);
3401
3402           VDBG (2, "VPPCOM_ATTR_SET_FLAGS: sh %u, flags = 0x%08x,"
3403                 " is_nonblocking = %u", session_handle, *flags,
3404                 vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK));
3405         }
3406       else
3407         rv = VPPCOM_EINVAL;
3408       break;
3409
3410     case VPPCOM_ATTR_GET_PEER_ADDR:
3411       if (PREDICT_TRUE (buffer && buflen &&
3412                         (*buflen >= sizeof (*ep)) && ep->ip))
3413         {
3414           ep->is_ip4 = session->transport.is_ip4;
3415           ep->port = session->transport.rmt_port;
3416           if (session->transport.is_ip4)
3417             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3418                               sizeof (ip4_address_t));
3419           else
3420             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3421                               sizeof (ip6_address_t));
3422           *buflen = sizeof (*ep);
3423           VDBG (1, "VPPCOM_ATTR_GET_PEER_ADDR: sh %u, is_ip4 = %u, "
3424                 "addr = %U, port %u", session_handle, ep->is_ip4,
3425                 format_ip46_address, &session->transport.rmt_ip,
3426                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3427                 clib_net_to_host_u16 (ep->port));
3428         }
3429       else
3430         rv = VPPCOM_EINVAL;
3431       break;
3432
3433     case VPPCOM_ATTR_GET_LCL_ADDR:
3434       if (PREDICT_TRUE (buffer && buflen &&
3435                         (*buflen >= sizeof (*ep)) && ep->ip))
3436         {
3437           ep->is_ip4 = session->transport.is_ip4;
3438           ep->port = session->transport.lcl_port;
3439           if (session->transport.is_ip4)
3440             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
3441                               sizeof (ip4_address_t));
3442           else
3443             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
3444                               sizeof (ip6_address_t));
3445           *buflen = sizeof (*ep);
3446           VDBG (1, "VPPCOM_ATTR_GET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3447                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
3448                 &session->transport.lcl_ip,
3449                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3450                 clib_net_to_host_u16 (ep->port));
3451         }
3452       else
3453         rv = VPPCOM_EINVAL;
3454       break;
3455
3456     case VPPCOM_ATTR_SET_LCL_ADDR:
3457       if (PREDICT_TRUE (buffer && buflen &&
3458                         (*buflen >= sizeof (*ep)) && ep->ip))
3459         {
3460           session->transport.is_ip4 = ep->is_ip4;
3461           session->transport.lcl_port = ep->port;
3462           vcl_ip_copy_from_ep (&session->transport.lcl_ip, ep);
3463           *buflen = sizeof (*ep);
3464           VDBG (1, "VPPCOM_ATTR_SET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3465                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
3466                 &session->transport.lcl_ip,
3467                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3468                 clib_net_to_host_u16 (ep->port));
3469         }
3470       else
3471         rv = VPPCOM_EINVAL;
3472       break;
3473
3474     case VPPCOM_ATTR_GET_LIBC_EPFD:
3475       rv = session->libc_epfd;
3476       VDBG (2, "VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d", rv);
3477       break;
3478
3479     case VPPCOM_ATTR_SET_LIBC_EPFD:
3480       if (PREDICT_TRUE (buffer && buflen &&
3481                         (*buflen == sizeof (session->libc_epfd))))
3482         {
3483           session->libc_epfd = *(int *) buffer;
3484           *buflen = sizeof (session->libc_epfd);
3485
3486           VDBG (2, "VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, buflen %d",
3487                 session->libc_epfd, *buflen);
3488         }
3489       else
3490         rv = VPPCOM_EINVAL;
3491       break;
3492
3493     case VPPCOM_ATTR_GET_PROTOCOL:
3494       if (buffer && buflen && (*buflen >= sizeof (int)))
3495         {
3496           *(int *) buffer = session->session_type;
3497           *buflen = sizeof (int);
3498
3499           VDBG (2, "VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
3500                 *(int *) buffer, *(int *) buffer ? "UDP" : "TCP", *buflen);
3501         }
3502       else
3503         rv = VPPCOM_EINVAL;
3504       break;
3505
3506     case VPPCOM_ATTR_GET_LISTEN:
3507       if (buffer && buflen && (*buflen >= sizeof (int)))
3508         {
3509           *(int *) buffer = vcl_session_has_attr (session,
3510                                                   VCL_SESS_ATTR_LISTEN);
3511           *buflen = sizeof (int);
3512
3513           VDBG (2, "VPPCOM_ATTR_GET_LISTEN: %d, buflen %d", *(int *) buffer,
3514                 *buflen);
3515         }
3516       else
3517         rv = VPPCOM_EINVAL;
3518       break;
3519
3520     case VPPCOM_ATTR_GET_ERROR:
3521       if (buffer && buflen && (*buflen >= sizeof (int)))
3522         {
3523           *(int *) buffer = 0;
3524           *buflen = sizeof (int);
3525
3526           VDBG (2, "VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
3527                 *(int *) buffer, *buflen);
3528         }
3529       else
3530         rv = VPPCOM_EINVAL;
3531       break;
3532
3533     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
3534       if (buffer && buflen && (*buflen >= sizeof (u32)))
3535         {
3536
3537           /* VPP-TBD */
3538           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
3539                                 session->tx_fifo ?
3540                                 svm_fifo_size (session->tx_fifo) :
3541                                 vcm->cfg.tx_fifo_size);
3542           *buflen = sizeof (u32);
3543
3544           VDBG (2, "VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3545                 " #VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer,
3546                 *buflen);
3547         }
3548       else
3549         rv = VPPCOM_EINVAL;
3550       break;
3551
3552     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
3553       if (buffer && buflen && (*buflen == sizeof (u32)))
3554         {
3555           /* VPP-TBD */
3556           session->sndbuf_size = *(u32 *) buffer;
3557           VDBG (2, "VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3558                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3559                 *buflen);
3560         }
3561       else
3562         rv = VPPCOM_EINVAL;
3563       break;
3564
3565     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
3566       if (buffer && buflen && (*buflen >= sizeof (u32)))
3567         {
3568
3569           /* VPP-TBD */
3570           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
3571                                 session->rx_fifo ?
3572                                 svm_fifo_size (session->rx_fifo) :
3573                                 vcm->cfg.rx_fifo_size);
3574           *buflen = sizeof (u32);
3575
3576           VDBG (2, "VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), buflen %d, "
3577                 "#VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer, *buflen);
3578         }
3579       else
3580         rv = VPPCOM_EINVAL;
3581       break;
3582
3583     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
3584       if (buffer && buflen && (*buflen == sizeof (u32)))
3585         {
3586           /* VPP-TBD */
3587           session->rcvbuf_size = *(u32 *) buffer;
3588           VDBG (2, "VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), buflen %d,"
3589                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3590                 *buflen);
3591         }
3592       else
3593         rv = VPPCOM_EINVAL;
3594       break;
3595
3596     case VPPCOM_ATTR_GET_REUSEADDR:
3597       if (buffer && buflen && (*buflen >= sizeof (int)))
3598         {
3599           /* VPP-TBD */
3600           *(int *) buffer = vcl_session_has_attr (session,
3601                                                   VCL_SESS_ATTR_REUSEADDR);
3602           *buflen = sizeof (int);
3603
3604           VDBG (2, "VPPCOM_ATTR_GET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3605                 *(int *) buffer, *buflen);
3606         }
3607       else
3608         rv = VPPCOM_EINVAL;
3609       break;
3610
3611     case VPPCOM_ATTR_SET_REUSEADDR:
3612       if (buffer && buflen && (*buflen == sizeof (int)) &&
3613           !vcl_session_has_attr (session, VCL_SESS_ATTR_LISTEN))
3614         {
3615           /* VPP-TBD */
3616           if (*(int *) buffer)
3617             vcl_session_set_attr (session, VCL_SESS_ATTR_REUSEADDR);
3618           else
3619             vcl_session_clear_attr (session, VCL_SESS_ATTR_REUSEADDR);
3620
3621           VDBG (2, "VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3622                 vcl_session_has_attr (session, VCL_SESS_ATTR_REUSEADDR),
3623                 *buflen);
3624         }
3625       else
3626         rv = VPPCOM_EINVAL;
3627       break;
3628
3629     case VPPCOM_ATTR_GET_REUSEPORT:
3630       if (buffer && buflen && (*buflen >= sizeof (int)))
3631         {
3632           /* VPP-TBD */
3633           *(int *) buffer = vcl_session_has_attr (session,
3634                                                   VCL_SESS_ATTR_REUSEPORT);
3635           *buflen = sizeof (int);
3636
3637           VDBG (2, "VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3638                 *(int *) buffer, *buflen);
3639         }
3640       else
3641         rv = VPPCOM_EINVAL;
3642       break;
3643
3644     case VPPCOM_ATTR_SET_REUSEPORT:
3645       if (buffer && buflen && (*buflen == sizeof (int)) &&
3646           !vcl_session_has_attr (session, VCL_SESS_ATTR_LISTEN))
3647         {
3648           /* VPP-TBD */
3649           if (*(int *) buffer)
3650             vcl_session_set_attr (session, VCL_SESS_ATTR_REUSEPORT);
3651           else
3652             vcl_session_clear_attr (session, VCL_SESS_ATTR_REUSEPORT);
3653
3654           VDBG (2, "VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3655                 vcl_session_has_attr (session, VCL_SESS_ATTR_REUSEPORT),
3656                 *buflen);
3657         }
3658       else
3659         rv = VPPCOM_EINVAL;
3660       break;
3661
3662     case VPPCOM_ATTR_GET_BROADCAST:
3663       if (buffer && buflen && (*buflen >= sizeof (int)))
3664         {
3665           /* VPP-TBD */
3666           *(int *) buffer = vcl_session_has_attr (session,
3667                                                   VCL_SESS_ATTR_BROADCAST);
3668           *buflen = sizeof (int);
3669
3670           VDBG (2, "VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3671                 *(int *) buffer, *buflen);
3672         }
3673       else
3674         rv = VPPCOM_EINVAL;
3675       break;
3676
3677     case VPPCOM_ATTR_SET_BROADCAST:
3678       if (buffer && buflen && (*buflen == sizeof (int)))
3679         {
3680           /* VPP-TBD */
3681           if (*(int *) buffer)
3682             vcl_session_set_attr (session, VCL_SESS_ATTR_BROADCAST);
3683           else
3684             vcl_session_clear_attr (session, VCL_SESS_ATTR_BROADCAST);
3685
3686           VDBG (2, "VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3687                 vcl_session_has_attr (session, VCL_SESS_ATTR_BROADCAST),
3688                 *buflen);
3689         }
3690       else
3691         rv = VPPCOM_EINVAL;
3692       break;
3693
3694     case VPPCOM_ATTR_GET_V6ONLY:
3695       if (buffer && buflen && (*buflen >= sizeof (int)))
3696         {
3697           /* VPP-TBD */
3698           *(int *) buffer = vcl_session_has_attr (session,
3699                                                   VCL_SESS_ATTR_V6ONLY);
3700           *buflen = sizeof (int);
3701
3702           VDBG (2, "VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3703                 *(int *) buffer, *buflen);
3704         }
3705       else
3706         rv = VPPCOM_EINVAL;
3707       break;
3708
3709     case VPPCOM_ATTR_SET_V6ONLY:
3710       if (buffer && buflen && (*buflen == sizeof (int)))
3711         {
3712           /* VPP-TBD */
3713           if (*(int *) buffer)
3714             vcl_session_set_attr (session, VCL_SESS_ATTR_V6ONLY);
3715           else
3716             vcl_session_clear_attr (session, VCL_SESS_ATTR_V6ONLY);
3717
3718           VDBG (2, "VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3719                 vcl_session_has_attr (session, VCL_SESS_ATTR_V6ONLY),
3720                 *buflen);
3721         }
3722       else
3723         rv = VPPCOM_EINVAL;
3724       break;
3725
3726     case VPPCOM_ATTR_GET_KEEPALIVE:
3727       if (buffer && buflen && (*buflen >= sizeof (int)))
3728         {
3729           /* VPP-TBD */
3730           *(int *) buffer = vcl_session_has_attr (session,
3731                                                   VCL_SESS_ATTR_KEEPALIVE);
3732           *buflen = sizeof (int);
3733
3734           VDBG (2, "VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3735                 *(int *) buffer, *buflen);
3736         }
3737       else
3738         rv = VPPCOM_EINVAL;
3739       break;
3740
3741     case VPPCOM_ATTR_SET_KEEPALIVE:
3742       if (buffer && buflen && (*buflen == sizeof (int)))
3743         {
3744           /* VPP-TBD */
3745           if (*(int *) buffer)
3746             vcl_session_set_attr (session, VCL_SESS_ATTR_KEEPALIVE);
3747           else
3748             vcl_session_clear_attr (session, VCL_SESS_ATTR_KEEPALIVE);
3749
3750           VDBG (2, "VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3751                 vcl_session_has_attr (session, VCL_SESS_ATTR_KEEPALIVE),
3752                 *buflen);
3753         }
3754       else
3755         rv = VPPCOM_EINVAL;
3756       break;
3757
3758     case VPPCOM_ATTR_GET_TCP_NODELAY:
3759       if (buffer && buflen && (*buflen >= sizeof (int)))
3760         {
3761           /* VPP-TBD */
3762           *(int *) buffer = vcl_session_has_attr (session,
3763                                                   VCL_SESS_ATTR_TCP_NODELAY);
3764           *buflen = sizeof (int);
3765
3766           VDBG (2, "VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3767                 *(int *) buffer, *buflen);
3768         }
3769       else
3770         rv = VPPCOM_EINVAL;
3771       break;
3772
3773     case VPPCOM_ATTR_SET_TCP_NODELAY:
3774       if (buffer && buflen && (*buflen == sizeof (int)))
3775         {
3776           /* VPP-TBD */
3777           if (*(int *) buffer)
3778             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_NODELAY);
3779           else
3780             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_NODELAY);
3781
3782           VDBG (2, "VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3783                 vcl_session_has_attr (session, VCL_SESS_ATTR_TCP_NODELAY),
3784                 *buflen);
3785         }
3786       else
3787         rv = VPPCOM_EINVAL;
3788       break;
3789
3790     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
3791       if (buffer && buflen && (*buflen >= sizeof (int)))
3792         {
3793           /* VPP-TBD */
3794           *(int *) buffer = vcl_session_has_attr (session,
3795                                                   VCL_SESS_ATTR_TCP_KEEPIDLE);
3796           *buflen = sizeof (int);
3797
3798           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3799                 *(int *) buffer, *buflen);
3800         }
3801       else
3802         rv = VPPCOM_EINVAL;
3803       break;
3804
3805     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3806       if (buffer && buflen && (*buflen == sizeof (int)))
3807         {
3808           /* VPP-TBD */
3809           if (*(int *) buffer)
3810             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_KEEPIDLE);
3811           else
3812             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_KEEPIDLE);
3813
3814           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3815                 vcl_session_has_attr (session,
3816                                       VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
3817         }
3818       else
3819         rv = VPPCOM_EINVAL;
3820       break;
3821
3822     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
3823       if (buffer && buflen && (*buflen >= sizeof (int)))
3824         {
3825           /* VPP-TBD */
3826           *(int *) buffer = vcl_session_has_attr (session,
3827                                                   VCL_SESS_ATTR_TCP_KEEPINTVL);
3828           *buflen = sizeof (int);
3829
3830           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3831                 *(int *) buffer, *buflen);
3832         }
3833       else
3834         rv = VPPCOM_EINVAL;
3835       break;
3836
3837     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3838       if (buffer && buflen && (*buflen == sizeof (int)))
3839         {
3840           /* VPP-TBD */
3841           if (*(int *) buffer)
3842             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_KEEPINTVL);
3843           else
3844             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_KEEPINTVL);
3845
3846           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3847                 vcl_session_has_attr (session,
3848                                       VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
3849         }
3850       else
3851         rv = VPPCOM_EINVAL;
3852       break;
3853
3854     case VPPCOM_ATTR_GET_TCP_USER_MSS:
3855       if (!(buffer && buflen && (*buflen >= sizeof (u32))))
3856         {
3857           rv = VPPCOM_EINVAL;
3858           break;
3859         }
3860
3861       tea.type = TRANSPORT_ENDPT_ATTR_MSS;
3862       tea.mss = *(u32 *) buffer;
3863       if (vcl_session_transport_attr (wrk, session, 1 /* is_get */, &tea))
3864         rv = VPPCOM_ENOPROTOOPT;
3865
3866       if (!rv)
3867         {
3868           *(u32 *) buffer = tea.mss;
3869           *buflen = sizeof (int);
3870         }
3871
3872       VDBG (2, "VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d", *(int *) buffer,
3873             *buflen);
3874       break;
3875
3876     case VPPCOM_ATTR_SET_TCP_USER_MSS:
3877       if (!(buffer && buflen && (*buflen == sizeof (u32))))
3878         {
3879           rv = VPPCOM_EINVAL;
3880           break;
3881         }
3882
3883       tea.type = TRANSPORT_ENDPT_ATTR_MSS;
3884       tea.mss = *(u32 *) buffer;
3885       if (vcl_session_transport_attr (wrk, session, 0 /* is_get */, &tea))
3886         rv = VPPCOM_ENOPROTOOPT;
3887
3888       VDBG (2, "VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d", tea.mss,
3889             *buflen);
3890       break;
3891
3892     case VPPCOM_ATTR_SET_CONNECTED:
3893       session->flags |= VCL_SESSION_F_CONNECTED;
3894       break;
3895
3896     case VPPCOM_ATTR_SET_CKPAIR:
3897       if (!(buffer && buflen && (*buflen == sizeof (int))) ||
3898           !vcl_session_has_crypto (session))
3899         {
3900           rv = VPPCOM_EINVAL;
3901           break;
3902         }
3903       if (!session->ext_config)
3904         {
3905           vcl_session_alloc_ext_cfg (session, TRANSPORT_ENDPT_EXT_CFG_CRYPTO,
3906                                      sizeof (transport_endpt_ext_cfg_t));
3907         }
3908       else if (session->ext_config->type != TRANSPORT_ENDPT_EXT_CFG_CRYPTO)
3909         {
3910           rv = VPPCOM_EINVAL;
3911           break;
3912         }
3913
3914       session->ext_config->crypto.ckpair_index = *(uint32_t *) buffer;
3915       break;
3916
3917     case VPPCOM_ATTR_SET_VRF:
3918       if (!(buffer && buflen && (*buflen == sizeof (u32))))
3919         {
3920           rv = VPPCOM_EINVAL;
3921           break;
3922         }
3923       session->vrf = *(u32 *) buffer;
3924       break;
3925
3926     case VPPCOM_ATTR_GET_VRF:
3927       if (!(buffer && buflen && (*buflen >= sizeof (u32))))
3928         {
3929           rv = VPPCOM_EINVAL;
3930           break;
3931         }
3932       *(u32 *) buffer = session->vrf;
3933       *buflen = sizeof (u32);
3934       break;
3935
3936     case VPPCOM_ATTR_GET_DOMAIN:
3937       if (!(buffer && buflen && (*buflen >= sizeof (int))))
3938         {
3939           rv = VPPCOM_EINVAL;
3940           break;
3941         }
3942
3943       if (session->transport.is_ip4)
3944         *(int *) buffer = AF_INET;
3945       else
3946         *(int *) buffer = AF_INET6;
3947       *buflen = sizeof (int);
3948
3949       VDBG (2, "VPPCOM_ATTR_GET_DOMAIN: %d, buflen %u", *(int *) buffer,
3950             *buflen);
3951       break;
3952
3953     case VPPCOM_ATTR_SET_ENDPT_EXT_CFG:
3954       if (!(buffer && buflen && (*buflen > 0)))
3955         {
3956           rv = VPPCOM_EINVAL;
3957           break;
3958         }
3959       if (session->ext_config)
3960         {
3961           rv = VPPCOM_EINVAL;
3962           break;
3963         }
3964       vcl_session_alloc_ext_cfg (session, TRANSPORT_ENDPT_EXT_CFG_NONE,
3965                                  *buflen + sizeof (u32));
3966       clib_memcpy (session->ext_config->data, buffer, *buflen);
3967       session->ext_config->len = *buflen;
3968       break;
3969
3970     default:
3971       rv = VPPCOM_EINVAL;
3972       break;
3973     }
3974
3975   return rv;
3976 }
3977
3978 int
3979 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
3980                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3981 {
3982   vcl_worker_t *wrk = vcl_worker_get_current ();
3983   vcl_session_t *session;
3984   int rv = VPPCOM_OK;
3985
3986   if (flags == 0)
3987     rv = vppcom_session_read (session_handle, buffer, buflen);
3988   else if (flags & MSG_PEEK)
3989     rv = vppcom_session_peek (session_handle, buffer, buflen);
3990   else
3991     {
3992       VDBG (0, "Unsupport flags for recvfrom %d", flags);
3993       return VPPCOM_EAFNOSUPPORT;
3994     }
3995
3996   if (ep && rv > 0)
3997     {
3998       session = vcl_session_get_w_handle (wrk, session_handle);
3999       if (session->transport.is_ip4)
4000         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
4001                           sizeof (ip4_address_t));
4002       else
4003         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
4004                           sizeof (ip6_address_t));
4005       ep->is_ip4 = session->transport.is_ip4;
4006       ep->port = session->transport.rmt_port;
4007     }
4008
4009   return rv;
4010 }
4011
4012 int
4013 vppcom_session_sendto (uint32_t session_handle, void *buffer,
4014                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
4015 {
4016   vcl_worker_t *wrk = vcl_worker_get_current ();
4017   vcl_session_t *s;
4018
4019   s = vcl_session_get_w_handle (wrk, session_handle);
4020   if (PREDICT_FALSE (!s))
4021     return VPPCOM_EBADFD;
4022
4023   if (ep)
4024     {
4025       if (!vcl_session_is_cl (s))
4026         return VPPCOM_EINVAL;
4027
4028       /* Session not connected/bound in vpp. Create it by 'connecting' it */
4029       if (PREDICT_FALSE (s->session_state == VCL_STATE_CLOSED))
4030         {
4031           u32 session_index = s->session_index;
4032           f64 timeout = vcm->cfg.session_timeout;
4033           int rv;
4034
4035           vcl_send_session_connect (wrk, s);
4036           rv = vppcom_wait_for_session_state_change (session_index,
4037                                                      VCL_STATE_READY,
4038                                                      timeout);
4039           if (rv < 0)
4040             return rv;
4041           s = vcl_session_get (wrk, session_index);
4042         }
4043
4044       s->transport.is_ip4 = ep->is_ip4;
4045       s->transport.rmt_port = ep->port;
4046       vcl_ip_copy_from_ep (&s->transport.rmt_ip, ep);
4047     }
4048
4049   if (flags)
4050     {
4051       // TBD check the flags and do the right thing
4052       VDBG (2, "handling flags 0x%u (%d) not implemented yet.", flags, flags);
4053     }
4054
4055   return (vppcom_session_write_inline (wrk, s, buffer, buflen, 1,
4056                                        s->is_dgram ? 1 : 0));
4057 }
4058
4059 int
4060 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
4061 {
4062   vcl_worker_t *wrk = vcl_worker_get_current ();
4063   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
4064   u32 i, keep_trying = 1;
4065   svm_msg_q_msg_t msg;
4066   session_event_t *e;
4067   int rv, num_ev = 0;
4068
4069   VDBG (3, "vp %p, nsids %u, wait_for_time %f", vp, n_sids, wait_for_time);
4070
4071   if (!vp)
4072     return VPPCOM_EFAULT;
4073
4074   do
4075     {
4076       vcl_session_t *session;
4077
4078       /* Dequeue all events and drop all unhandled io events */
4079       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
4080         {
4081           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
4082           vcl_handle_mq_event (wrk, e);
4083           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
4084         }
4085       vec_reset_length (wrk->unhandled_evts_vector);
4086
4087       for (i = 0; i < n_sids; i++)
4088         {
4089           session = vcl_session_get (wrk, vp[i].sh);
4090           if (!session)
4091             {
4092               vp[i].revents = POLLHUP;
4093               num_ev++;
4094               continue;
4095             }
4096
4097           vp[i].revents = 0;
4098
4099           if (POLLIN & vp[i].events)
4100             {
4101               rv = vcl_session_read_ready (session);
4102               if (rv > 0)
4103                 {
4104                   vp[i].revents |= POLLIN;
4105                   num_ev++;
4106                 }
4107               else if (rv < 0)
4108                 {
4109                   switch (rv)
4110                     {
4111                     case VPPCOM_ECONNRESET:
4112                       vp[i].revents = POLLHUP;
4113                       break;
4114
4115                     default:
4116                       vp[i].revents = POLLERR;
4117                       break;
4118                     }
4119                   num_ev++;
4120                 }
4121             }
4122
4123           if (POLLOUT & vp[i].events)
4124             {
4125               rv = vcl_session_write_ready (session);
4126               if (rv > 0)
4127                 {
4128                   vp[i].revents |= POLLOUT;
4129                   num_ev++;
4130                 }
4131               else if (rv < 0)
4132                 {
4133                   switch (rv)
4134                     {
4135                     case VPPCOM_ECONNRESET:
4136                       vp[i].revents = POLLHUP;
4137                       break;
4138
4139                     default:
4140                       vp[i].revents = POLLERR;
4141                       break;
4142                     }
4143                   num_ev++;
4144                 }
4145             }
4146
4147           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
4148             {
4149               vp[i].revents = POLLNVAL;
4150               num_ev++;
4151             }
4152         }
4153       if (wait_for_time != -1)
4154         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
4155     }
4156   while ((num_ev == 0) && keep_trying);
4157
4158   return num_ev;
4159 }
4160
4161 int
4162 vppcom_mq_epoll_fd (void)
4163 {
4164   vcl_worker_t *wrk = vcl_worker_get_current ();
4165   return wrk->mqs_epfd;
4166 }
4167
4168 int
4169 vppcom_session_index (vcl_session_handle_t session_handle)
4170 {
4171   return session_handle & 0xFFFFFF;
4172 }
4173
4174 int
4175 vppcom_session_worker (vcl_session_handle_t session_handle)
4176 {
4177   return session_handle >> 24;
4178 }
4179
4180 int
4181 vppcom_worker_register (void)
4182 {
4183   if (!vcl_worker_alloc_and_init ())
4184     return VPPCOM_EEXIST;
4185
4186   if (vcl_worker_register_with_vpp ())
4187     return VPPCOM_EEXIST;
4188
4189   return VPPCOM_OK;
4190 }
4191
4192 void
4193 vppcom_worker_unregister (void)
4194 {
4195   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
4196   vcl_set_worker_index (~0);
4197 }
4198
4199 void
4200 vppcom_worker_index_set (int index)
4201 {
4202   vcl_set_worker_index (index);
4203 }
4204
4205 int
4206 vppcom_worker_index (void)
4207 {
4208   return vcl_get_worker_index ();
4209 }
4210
4211 int
4212 vppcom_worker_mqs_epfd (void)
4213 {
4214   vcl_worker_t *wrk = vcl_worker_get_current ();
4215   if (!vcm->cfg.use_mq_eventfd)
4216     return -1;
4217   return wrk->mqs_epfd;
4218 }
4219
4220 int
4221 vppcom_session_is_connectable_listener (uint32_t session_handle)
4222 {
4223   vcl_session_t *session;
4224   vcl_worker_t *wrk = vcl_worker_get_current ();
4225   session = vcl_session_get_w_handle (wrk, session_handle);
4226   if (!session)
4227     return VPPCOM_EBADFD;
4228   return vcl_session_is_connectable_listener (wrk, session);
4229 }
4230
4231 int
4232 vppcom_session_listener (uint32_t session_handle)
4233 {
4234   vcl_worker_t *wrk = vcl_worker_get_current ();
4235   vcl_session_t *listen_session, *session;
4236   session = vcl_session_get_w_handle (wrk, session_handle);
4237   if (!session)
4238     return VPPCOM_EBADFD;
4239   if (session->listener_index == VCL_INVALID_SESSION_INDEX)
4240     return VPPCOM_EBADFD;
4241   listen_session = vcl_session_get_w_handle (wrk, session->listener_index);
4242   if (!listen_session)
4243     return VPPCOM_EBADFD;
4244   return vcl_session_handle (listen_session);
4245 }
4246
4247 int
4248 vppcom_session_n_accepted (uint32_t session_handle)
4249 {
4250   vcl_worker_t *wrk = vcl_worker_get_current ();
4251   vcl_session_t *session = vcl_session_get_w_handle (wrk, session_handle);
4252   if (!session)
4253     return VPPCOM_EBADFD;
4254   return session->n_accepted_sessions;
4255 }
4256
4257 const char *
4258 vppcom_proto_str (vppcom_proto_t proto)
4259 {
4260   char const *proto_str;
4261
4262   switch (proto)
4263     {
4264     case VPPCOM_PROTO_TCP:
4265       proto_str = "TCP";
4266       break;
4267     case VPPCOM_PROTO_UDP:
4268       proto_str = "UDP";
4269       break;
4270     case VPPCOM_PROTO_TLS:
4271       proto_str = "TLS";
4272       break;
4273     case VPPCOM_PROTO_QUIC:
4274       proto_str = "QUIC";
4275       break;
4276     case VPPCOM_PROTO_DTLS:
4277       proto_str = "DTLS";
4278       break;
4279     case VPPCOM_PROTO_SRTP:
4280       proto_str = "SRTP";
4281       break;
4282     default:
4283       proto_str = "UNKNOWN";
4284       break;
4285     }
4286   return proto_str;
4287 }
4288
4289 const char *
4290 vppcom_retval_str (int retval)
4291 {
4292   char const *st;
4293
4294   switch (retval)
4295     {
4296     case VPPCOM_OK:
4297       st = "VPPCOM_OK";
4298       break;
4299
4300     case VPPCOM_EAGAIN:
4301       st = "VPPCOM_EAGAIN";
4302       break;
4303
4304     case VPPCOM_EFAULT:
4305       st = "VPPCOM_EFAULT";
4306       break;
4307
4308     case VPPCOM_ENOMEM:
4309       st = "VPPCOM_ENOMEM";
4310       break;
4311
4312     case VPPCOM_EINVAL:
4313       st = "VPPCOM_EINVAL";
4314       break;
4315
4316     case VPPCOM_EBADFD:
4317       st = "VPPCOM_EBADFD";
4318       break;
4319
4320     case VPPCOM_EAFNOSUPPORT:
4321       st = "VPPCOM_EAFNOSUPPORT";
4322       break;
4323
4324     case VPPCOM_ECONNABORTED:
4325       st = "VPPCOM_ECONNABORTED";
4326       break;
4327
4328     case VPPCOM_ECONNRESET:
4329       st = "VPPCOM_ECONNRESET";
4330       break;
4331
4332     case VPPCOM_ENOTCONN:
4333       st = "VPPCOM_ENOTCONN";
4334       break;
4335
4336     case VPPCOM_ECONNREFUSED:
4337       st = "VPPCOM_ECONNREFUSED";
4338       break;
4339
4340     case VPPCOM_ETIMEDOUT:
4341       st = "VPPCOM_ETIMEDOUT";
4342       break;
4343
4344     default:
4345       st = "UNKNOWN_STATE";
4346       break;
4347     }
4348
4349   return st;
4350 }
4351
4352 int
4353 vppcom_add_cert_key_pair (vppcom_cert_key_pair_t *ckpair)
4354 {
4355   if (vcm->cfg.vpp_app_socket_api)
4356     {
4357       clib_warning ("not supported");
4358       return VPPCOM_EINVAL;
4359     }
4360   return vcl_bapi_add_cert_key_pair (ckpair);
4361 }
4362
4363 int
4364 vppcom_del_cert_key_pair (uint32_t ckpair_index)
4365 {
4366   if (vcm->cfg.vpp_app_socket_api)
4367     {
4368       clib_warning ("not supported");
4369       return VPPCOM_EINVAL;
4370     }
4371   return vcl_bapi_del_cert_key_pair (ckpair_index);
4372 }
4373
4374 /*
4375  * fd.io coding-style-patch-verification: ON
4376  *
4377  * Local Variables:
4378  * eval: (c-set-style "gnu")
4379  * End:
4380  */