vcl: basic support for epoll level-triggered evts
[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   vcl_session_t *vep_session;
2719   int rv = VPPCOM_OK;
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         }
2805       /* Generate EPOLLIN if rx fifo has data */
2806       if ((event->events & EPOLLIN) && (vcl_session_read_ready (s) > 0))
2807         {
2808           session_event_t e = { 0 };
2809           e.event_type = SESSION_IO_EVT_RX;
2810           e.session_index = s->session_index;
2811           vec_add1 (wrk->unhandled_evts_vector, e);
2812         }
2813       VDBG (1, "EPOLL_CTL_ADD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2814             vep_handle, session_handle, event->events, event->data.u64);
2815       vcl_evt (VCL_EVT_EPOLL_CTLADD, s, event->events, event->data.u64);
2816       break;
2817
2818     case EPOLL_CTL_MOD:
2819       if (PREDICT_FALSE (!event))
2820         {
2821           VDBG (0, "EPOLL_CTL_MOD: NULL pointer to epoll_event structure!");
2822           rv = VPPCOM_EINVAL;
2823           goto done;
2824         }
2825       else if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2826         {
2827           VDBG (0, "sh %u EPOLL_CTL_MOD: not a vep session!", session_handle);
2828           rv = VPPCOM_ENOENT;
2829           goto done;
2830         }
2831       else if (PREDICT_FALSE (s->vep.vep_sh != vep_handle))
2832         {
2833           VDBG (0, "EPOLL_CTL_MOD: sh %u vep_sh (%u) != vep_sh (%u)!",
2834                 session_handle, s->vep.vep_sh, vep_handle);
2835           rv = VPPCOM_EINVAL;
2836           goto done;
2837         }
2838
2839       /* Generate EPOLLOUT if session write ready nd event was not on */
2840       if ((event->events & EPOLLOUT) && !(s->vep.ev.events & EPOLLOUT) &&
2841           (vcl_session_write_ready (s) > 0))
2842         {
2843           session_event_t e = { 0 };
2844           e.event_type = SESSION_IO_EVT_TX;
2845           e.session_index = s->session_index;
2846           vec_add1 (wrk->unhandled_evts_vector, e);
2847         }
2848       /* Generate EPOLLIN if session read ready and event was not on */
2849       if ((event->events & EPOLLIN) && !(s->vep.ev.events & EPOLLIN) &&
2850           (vcl_session_read_ready (s) > 0))
2851         {
2852           session_event_t e = { 0 };
2853           e.event_type = SESSION_IO_EVT_RX;
2854           e.session_index = s->session_index;
2855           vec_add1 (wrk->unhandled_evts_vector, e);
2856         }
2857       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
2858       s->vep.ev = *event;
2859       txf = vcl_session_is_ct (s) ? s->ct_tx_fifo : s->tx_fifo;
2860       if (txf)
2861         {
2862           if (event->events & EPOLLOUT)
2863             svm_fifo_add_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2864           else
2865             svm_fifo_del_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2866         }
2867       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2868             vep_handle, session_handle, event->events, event->data.u64);
2869       break;
2870
2871     case EPOLL_CTL_DEL:
2872       if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2873         {
2874           VDBG (0, "EPOLL_CTL_DEL: %u not a vep session!", session_handle);
2875           rv = VPPCOM_ENOENT;
2876           goto done;
2877         }
2878       else if (PREDICT_FALSE (s->vep.vep_sh != vep_handle))
2879         {
2880           VDBG (0, "EPOLL_CTL_DEL: sh %u vep_sh (%u) != vep_sh (%u)!",
2881                 session_handle, s->vep.vep_sh, vep_handle);
2882           rv = VPPCOM_EINVAL;
2883           goto done;
2884         }
2885
2886       if (s->vep.prev_sh == vep_handle)
2887         vep_session->vep.next_sh = s->vep.next_sh;
2888       else
2889         {
2890           vcl_session_t *prev_session;
2891           prev_session = vcl_session_get_w_handle (wrk, s->vep.prev_sh);
2892           if (PREDICT_FALSE (!prev_session))
2893             {
2894               VDBG (0, "EPOLL_CTL_DEL: Invalid prev_sh (%u) on sh (%u)!",
2895                     s->vep.prev_sh, session_handle);
2896               return VPPCOM_EBADFD;
2897             }
2898           ASSERT (prev_session->vep.next_sh == session_handle);
2899           prev_session->vep.next_sh = s->vep.next_sh;
2900         }
2901       if (s->vep.next_sh != ~0)
2902         {
2903           vcl_session_t *next_session;
2904           next_session = vcl_session_get_w_handle (wrk, s->vep.next_sh);
2905           if (PREDICT_FALSE (!next_session))
2906             {
2907               VDBG (0, "EPOLL_CTL_DEL: Invalid next_sh (%u) on sh (%u)!",
2908                     s->vep.next_sh, session_handle);
2909               return VPPCOM_EBADFD;
2910             }
2911           ASSERT (next_session->vep.prev_sh == session_handle);
2912           next_session->vep.prev_sh = s->vep.prev_sh;
2913         }
2914
2915       memset (&s->vep, 0, sizeof (s->vep));
2916       s->vep.next_sh = ~0;
2917       s->vep.prev_sh = ~0;
2918       s->vep.vep_sh = ~0;
2919       s->flags &= ~VCL_SESSION_F_IS_VEP_SESSION;
2920
2921       if (vcl_session_is_open (s))
2922         {
2923           txf = vcl_session_is_ct (s) ? s->ct_tx_fifo : s->tx_fifo;
2924           if (txf)
2925             svm_fifo_del_want_deq_ntf (txf, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2926         }
2927
2928       VDBG (1, "EPOLL_CTL_DEL: vep_idx %u, sh %u!", vep_handle,
2929             session_handle);
2930       vcl_evt (VCL_EVT_EPOLL_CTLDEL, s, vep_sh);
2931       break;
2932
2933     default:
2934       VDBG (0, "Invalid operation (%d)!", op);
2935       rv = VPPCOM_EINVAL;
2936     }
2937
2938   vep_verify_epoll_chain (wrk, vep_handle);
2939
2940 done:
2941   return rv;
2942 }
2943
2944 static inline void
2945 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2946                                 struct epoll_event *events, u32 * num_ev)
2947 {
2948   session_disconnected_msg_t *disconnected_msg;
2949   session_connected_msg_t *connected_msg;
2950   u32 sid = ~0, session_events;
2951   u64 session_evt_data = ~0;
2952   vcl_session_t *s;
2953   u8 add_event = 0;
2954
2955   switch (e->event_type)
2956     {
2957     case SESSION_IO_EVT_RX:
2958       sid = e->session_index;
2959       s = vcl_session_get (wrk, sid);
2960       if (vcl_session_is_closed (s))
2961         break;
2962       vcl_fifo_rx_evt_valid_or_break (s);
2963       session_events = s->vep.ev.events;
2964       if (!(EPOLLIN & s->vep.ev.events)
2965           || (s->flags & VCL_SESSION_F_HAS_RX_EVT))
2966         break;
2967       add_event = 1;
2968       events[*num_ev].events |= EPOLLIN;
2969       session_evt_data = s->vep.ev.data.u64;
2970       s->flags |= VCL_SESSION_F_HAS_RX_EVT;
2971       break;
2972     case SESSION_IO_EVT_TX:
2973       sid = e->session_index;
2974       s = vcl_session_get (wrk, sid);
2975       if (vcl_session_is_closed (s))
2976         break;
2977       session_events = s->vep.ev.events;
2978       if (!(EPOLLOUT & session_events))
2979         break;
2980       add_event = 1;
2981       events[*num_ev].events |= EPOLLOUT;
2982       session_evt_data = s->vep.ev.data.u64;
2983       svm_fifo_reset_has_deq_ntf (vcl_session_is_ct (s) ?
2984                                   s->ct_tx_fifo : s->tx_fifo);
2985       break;
2986     case SESSION_CTRL_EVT_ACCEPTED:
2987       if (!e->postponed)
2988         s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
2989       else
2990         s = vcl_session_get (wrk, e->session_index);
2991       if (!s)
2992         break;
2993       session_events = s->vep.ev.events;
2994       sid = s->session_index;
2995       if (!(EPOLLIN & session_events))
2996         break;
2997       add_event = 1;
2998       events[*num_ev].events |= EPOLLIN;
2999       session_evt_data = s->vep.ev.data.u64;
3000       break;
3001     case SESSION_CTRL_EVT_CONNECTED:
3002       if (!e->postponed)
3003         {
3004           connected_msg = (session_connected_msg_t *) e->data;
3005           sid = vcl_session_connected_handler (wrk, connected_msg);
3006         }
3007       else
3008         sid = e->session_index;
3009       s = vcl_session_get (wrk, sid);
3010       if (vcl_session_is_closed (s))
3011         break;
3012       session_events = s->vep.ev.events;
3013       /* Generate EPOLLOUT because there's no connected event */
3014       if (!(EPOLLOUT & session_events))
3015         break;
3016       add_event = 1;
3017       events[*num_ev].events |= EPOLLOUT;
3018       session_evt_data = s->vep.ev.data.u64;
3019       if (s->session_state == VCL_STATE_DETACHED)
3020         events[*num_ev].events |= EPOLLHUP;
3021       break;
3022     case SESSION_CTRL_EVT_DISCONNECTED:
3023       disconnected_msg = (session_disconnected_msg_t *) e->data;
3024       s = vcl_session_disconnected_handler (wrk, disconnected_msg);
3025       if (vcl_session_is_closed (s))
3026         break;
3027       sid = s->session_index;
3028       session_events = s->vep.ev.events;
3029       add_event = 1;
3030       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
3031       session_evt_data = s->vep.ev.data.u64;
3032       break;
3033     case SESSION_CTRL_EVT_RESET:
3034       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
3035       s = vcl_session_get (wrk, sid);
3036       if (vcl_session_is_closed (s))
3037         break;
3038       session_events = s->vep.ev.events;
3039       add_event = 1;
3040       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
3041       session_evt_data = s->vep.ev.data.u64;
3042       break;
3043     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
3044       vcl_session_unlisten_reply_handler (wrk, e->data);
3045       break;
3046     case SESSION_CTRL_EVT_MIGRATED:
3047       vcl_session_migrated_handler (wrk, e->data);
3048       break;
3049     case SESSION_CTRL_EVT_CLEANUP:
3050       vcl_session_cleanup_handler (wrk, e->data);
3051       break;
3052     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
3053       vcl_session_req_worker_update_handler (wrk, e->data);
3054       break;
3055     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
3056       vcl_session_worker_update_reply_handler (wrk, e->data);
3057       break;
3058     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
3059       vcl_session_app_add_segment_handler (wrk, e->data);
3060       break;
3061     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
3062       vcl_session_app_del_segment_handler (wrk, e->data);
3063       break;
3064     case SESSION_CTRL_EVT_APP_WRK_RPC:
3065       vcl_worker_rpc_handler (wrk, e->data);
3066       break;
3067     default:
3068       VDBG (0, "unhandled: %u", e->event_type);
3069       break;
3070     }
3071
3072   if (add_event)
3073     {
3074       events[*num_ev].data.u64 = session_evt_data;
3075       if (EPOLLONESHOT & session_events)
3076         {
3077           s = vcl_session_get (wrk, sid);
3078           s->vep.ev.events = 0;
3079         }
3080       if (!(EPOLLET & session_events))
3081         {
3082           vec_add1 (wrk->ep_level_evts, sid);
3083         }
3084       *num_ev += 1;
3085     }
3086 }
3087
3088 static int
3089 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
3090                           struct epoll_event *events, u32 maxevents,
3091                           double wait_for_time, u32 * num_ev)
3092 {
3093   svm_msg_q_msg_t *msg;
3094   session_event_t *e;
3095   int i;
3096
3097   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
3098     goto handle_dequeued;
3099
3100   if (svm_msg_q_is_empty (mq))
3101     {
3102       if (!wait_for_time)
3103         return 0;
3104       else if (wait_for_time < 0)
3105         svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
3106       else
3107         {
3108           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
3109             return 0;
3110         }
3111     }
3112   ASSERT (maxevents > *num_ev);
3113   vcl_mq_dequeue_batch (wrk, mq, ~0);
3114
3115 handle_dequeued:
3116   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
3117     {
3118       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
3119       e = svm_msg_q_msg_data (mq, msg);
3120       if (*num_ev < maxevents)
3121         vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
3122       else
3123         vcl_handle_mq_event (wrk, e);
3124       svm_msg_q_free_msg (mq, msg);
3125     }
3126   vec_reset_length (wrk->mq_msg_vector);
3127   vcl_handle_pending_wrk_updates (wrk);
3128   return *num_ev;
3129 }
3130
3131 static int
3132 vppcom_epoll_wait_condvar (vcl_worker_t *wrk, struct epoll_event *events,
3133                            int maxevents, u32 n_evts, double timeout_ms)
3134 {
3135   double end = -1;
3136
3137   if (!n_evts)
3138     {
3139       if (timeout_ms > 0)
3140         end = clib_time_now (&wrk->clib_time) + (timeout_ms / 1e3);
3141     }
3142
3143   do
3144     {
3145       vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events, maxevents,
3146                                 timeout_ms, &n_evts);
3147       if (n_evts || !timeout_ms)
3148         return n_evts;
3149     }
3150   while (end == -1 || clib_time_now (&wrk->clib_time) < end);
3151
3152   return 0;
3153 }
3154
3155 static int
3156 vppcom_epoll_wait_eventfd (vcl_worker_t *wrk, struct epoll_event *events,
3157                            int maxevents, u32 n_evts, double timeout_ms)
3158 {
3159   int __clib_unused n_read;
3160   vcl_mq_evt_conn_t *mqc;
3161   int n_mq_evts, i;
3162   double end = -1;
3163   u64 buf;
3164
3165   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
3166   if (!n_evts)
3167     {
3168       if (timeout_ms > 0)
3169         end = clib_time_now (&wrk->clib_time) + (timeout_ms / 1e3);
3170     }
3171
3172   do
3173     {
3174       n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
3175                               vec_len (wrk->mq_events), timeout_ms);
3176       if (n_mq_evts < 0)
3177         {
3178           VDBG (0, "epoll_wait error %u", errno);
3179           return n_evts;
3180         }
3181
3182       for (i = 0; i < n_mq_evts; i++)
3183         {
3184           mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
3185           n_read = read (mqc->mq_fd, &buf, sizeof (buf));
3186           vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0,
3187                                     &n_evts);
3188         }
3189
3190       if (n_evts || !timeout_ms)
3191         return n_evts;
3192     }
3193   while (end == -1 || clib_time_now (&wrk->clib_time) < end);
3194
3195   return 0;
3196 }
3197
3198 static void
3199 vcl_epoll_swap_lt_lists (vcl_worker_t *wrk)
3200 {
3201   u32 *le;
3202
3203   le = wrk->ep_level_evts;
3204   wrk->ep_level_evts = wrk->ep_level_evts_fl;
3205   wrk->ep_level_evts_fl = le;
3206 }
3207
3208 static void
3209 vcl_epoll_wait_handle_lt (vcl_worker_t *wrk, struct epoll_event *events,
3210                           int maxevents, u32 *n_evts)
3211 {
3212   u32 *sid, add_event = 0, *le = wrk->ep_level_evts_fl;
3213   vcl_session_t *s;
3214   u64 evt_data;
3215
3216   if (*n_evts >= maxevents)
3217     {
3218       vec_add (wrk->ep_level_evts, le, vec_len (le));
3219       vec_reset_length (wrk->ep_level_evts_fl);
3220       return;
3221     }
3222
3223   vec_foreach (sid, le)
3224     {
3225       s = vcl_session_get (wrk, sid[0]);
3226       if (!s)
3227         continue;
3228       if ((s->vep.ev.events & EPOLLIN) && vcl_session_read_ready (s))
3229         {
3230           add_event = 1;
3231           events[*n_evts].events |= EPOLLIN;
3232           evt_data = s->vep.ev.data.u64;
3233         }
3234       if ((s->vep.ev.events & EPOLLOUT) && vcl_session_write_ready (s))
3235         {
3236           add_event = 1;
3237           events[*n_evts].events |= EPOLLOUT;
3238           evt_data = s->vep.ev.data.u64;
3239         }
3240       if (add_event)
3241         {
3242           events[*n_evts].data.u64 = evt_data;
3243           *n_evts += 1;
3244           add_event = 0;
3245           vec_add1 (wrk->ep_level_evts, sid[0]);
3246           if (*n_evts == maxevents)
3247             {
3248               u32 pos = (sid - le) + 1;
3249               vec_add (wrk->ep_level_evts, &le[pos], vec_len (le) - pos);
3250               break;
3251             }
3252         }
3253     }
3254
3255   vec_reset_length (wrk->ep_level_evts_fl);
3256 }
3257
3258 int
3259 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
3260                    int maxevents, double wait_for_time)
3261 {
3262   vcl_worker_t *wrk = vcl_worker_get_current ();
3263   vcl_session_t *vep_session;
3264   u32 n_evts = 0, do_lt = 0;
3265   int i;
3266
3267   if (PREDICT_FALSE (maxevents <= 0))
3268     {
3269       VDBG (0, "ERROR: Invalid maxevents (%d)!", maxevents);
3270       return VPPCOM_EINVAL;
3271     }
3272
3273   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
3274   if (!vep_session)
3275     return VPPCOM_EBADFD;
3276
3277   if (PREDICT_FALSE (!(vep_session->flags & VCL_SESSION_F_IS_VEP)))
3278     {
3279       VDBG (0, "ERROR: vep_idx (%u) is not a vep!", vep_handle);
3280       return VPPCOM_EINVAL;
3281     }
3282
3283   memset (events, 0, sizeof (*events) * maxevents);
3284
3285   if (vec_len (wrk->unhandled_evts_vector))
3286     {
3287       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
3288         {
3289           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
3290                                           events, &n_evts);
3291           if (n_evts == maxevents)
3292             {
3293               vec_delete (wrk->unhandled_evts_vector, i + 1, 0);
3294               return n_evts;
3295             }
3296         }
3297       vec_reset_length (wrk->unhandled_evts_vector);
3298     }
3299   /* Request to only drain unhandled */
3300   if ((int) wait_for_time == -2)
3301     return n_evts;
3302
3303   if (PREDICT_FALSE (vec_len (wrk->ep_level_evts)))
3304     {
3305       vcl_epoll_swap_lt_lists (wrk);
3306       do_lt = 1;
3307     }
3308
3309   if (vcm->cfg.use_mq_eventfd)
3310     n_evts = vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
3311                                         wait_for_time);
3312   else
3313     n_evts = vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
3314                                         wait_for_time);
3315
3316   if (PREDICT_FALSE (do_lt))
3317     vcl_epoll_wait_handle_lt (wrk, events, maxevents, &n_evts);
3318
3319   return n_evts;
3320 }
3321
3322 int
3323 vppcom_session_attr (uint32_t session_handle, uint32_t op,
3324                      void *buffer, uint32_t * buflen)
3325 {
3326   vcl_worker_t *wrk = vcl_worker_get_current ();
3327   u32 *flags = buffer;
3328   vppcom_endpt_t *ep = buffer;
3329   transport_endpt_attr_t tea;
3330   vcl_session_t *session;
3331   int rv = VPPCOM_OK;
3332
3333   session = vcl_session_get_w_handle (wrk, session_handle);
3334   if (!session)
3335     return VPPCOM_EBADFD;
3336
3337   switch (op)
3338     {
3339     case VPPCOM_ATTR_GET_NREAD:
3340       rv = vcl_session_read_ready (session);
3341       VDBG (2, "VPPCOM_ATTR_GET_NREAD: sh %u, nread = %d", session_handle,
3342             rv);
3343       break;
3344
3345     case VPPCOM_ATTR_GET_NWRITE:
3346       rv = vcl_session_write_ready (session);
3347       VDBG (2, "VPPCOM_ATTR_GET_NWRITE: sh %u, nwrite = %d", session_handle,
3348             rv);
3349       break;
3350
3351     case VPPCOM_ATTR_GET_FLAGS:
3352       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
3353         {
3354           *flags =
3355             O_RDWR |
3356             (vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK) ?
3357              O_NONBLOCK : 0);
3358           *buflen = sizeof (*flags);
3359           VDBG (2, "VPPCOM_ATTR_GET_FLAGS: sh %u, flags = 0x%08x, "
3360                 "is_nonblocking = %u", session_handle, *flags,
3361                 vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK));
3362         }
3363       else
3364         rv = VPPCOM_EINVAL;
3365       break;
3366
3367     case VPPCOM_ATTR_SET_FLAGS:
3368       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
3369         {
3370           if (*flags & O_NONBLOCK)
3371             vcl_session_set_attr (session, VCL_SESS_ATTR_NONBLOCK);
3372           else
3373             vcl_session_clear_attr (session, VCL_SESS_ATTR_NONBLOCK);
3374
3375           VDBG (2, "VPPCOM_ATTR_SET_FLAGS: sh %u, flags = 0x%08x,"
3376                 " is_nonblocking = %u", session_handle, *flags,
3377                 vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK));
3378         }
3379       else
3380         rv = VPPCOM_EINVAL;
3381       break;
3382
3383     case VPPCOM_ATTR_GET_PEER_ADDR:
3384       if (PREDICT_TRUE (buffer && buflen &&
3385                         (*buflen >= sizeof (*ep)) && ep->ip))
3386         {
3387           ep->is_ip4 = session->transport.is_ip4;
3388           ep->port = session->transport.rmt_port;
3389           if (session->transport.is_ip4)
3390             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3391                               sizeof (ip4_address_t));
3392           else
3393             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3394                               sizeof (ip6_address_t));
3395           *buflen = sizeof (*ep);
3396           VDBG (1, "VPPCOM_ATTR_GET_PEER_ADDR: sh %u, is_ip4 = %u, "
3397                 "addr = %U, port %u", session_handle, ep->is_ip4,
3398                 format_ip46_address, &session->transport.rmt_ip,
3399                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3400                 clib_net_to_host_u16 (ep->port));
3401         }
3402       else
3403         rv = VPPCOM_EINVAL;
3404       break;
3405
3406     case VPPCOM_ATTR_GET_LCL_ADDR:
3407       if (PREDICT_TRUE (buffer && buflen &&
3408                         (*buflen >= sizeof (*ep)) && ep->ip))
3409         {
3410           ep->is_ip4 = session->transport.is_ip4;
3411           ep->port = session->transport.lcl_port;
3412           if (session->transport.is_ip4)
3413             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
3414                               sizeof (ip4_address_t));
3415           else
3416             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
3417                               sizeof (ip6_address_t));
3418           *buflen = sizeof (*ep);
3419           VDBG (1, "VPPCOM_ATTR_GET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3420                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
3421                 &session->transport.lcl_ip,
3422                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3423                 clib_net_to_host_u16 (ep->port));
3424         }
3425       else
3426         rv = VPPCOM_EINVAL;
3427       break;
3428
3429     case VPPCOM_ATTR_SET_LCL_ADDR:
3430       if (PREDICT_TRUE (buffer && buflen &&
3431                         (*buflen >= sizeof (*ep)) && ep->ip))
3432         {
3433           session->transport.is_ip4 = ep->is_ip4;
3434           session->transport.lcl_port = ep->port;
3435           vcl_ip_copy_from_ep (&session->transport.lcl_ip, ep);
3436           *buflen = sizeof (*ep);
3437           VDBG (1, "VPPCOM_ATTR_SET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3438                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
3439                 &session->transport.lcl_ip,
3440                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3441                 clib_net_to_host_u16 (ep->port));
3442         }
3443       else
3444         rv = VPPCOM_EINVAL;
3445       break;
3446
3447     case VPPCOM_ATTR_GET_LIBC_EPFD:
3448       rv = session->libc_epfd;
3449       VDBG (2, "VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d", rv);
3450       break;
3451
3452     case VPPCOM_ATTR_SET_LIBC_EPFD:
3453       if (PREDICT_TRUE (buffer && buflen &&
3454                         (*buflen == sizeof (session->libc_epfd))))
3455         {
3456           session->libc_epfd = *(int *) buffer;
3457           *buflen = sizeof (session->libc_epfd);
3458
3459           VDBG (2, "VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, buflen %d",
3460                 session->libc_epfd, *buflen);
3461         }
3462       else
3463         rv = VPPCOM_EINVAL;
3464       break;
3465
3466     case VPPCOM_ATTR_GET_PROTOCOL:
3467       if (buffer && buflen && (*buflen >= sizeof (int)))
3468         {
3469           *(int *) buffer = session->session_type;
3470           *buflen = sizeof (int);
3471
3472           VDBG (2, "VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
3473                 *(int *) buffer, *(int *) buffer ? "UDP" : "TCP", *buflen);
3474         }
3475       else
3476         rv = VPPCOM_EINVAL;
3477       break;
3478
3479     case VPPCOM_ATTR_GET_LISTEN:
3480       if (buffer && buflen && (*buflen >= sizeof (int)))
3481         {
3482           *(int *) buffer = vcl_session_has_attr (session,
3483                                                   VCL_SESS_ATTR_LISTEN);
3484           *buflen = sizeof (int);
3485
3486           VDBG (2, "VPPCOM_ATTR_GET_LISTEN: %d, buflen %d", *(int *) buffer,
3487                 *buflen);
3488         }
3489       else
3490         rv = VPPCOM_EINVAL;
3491       break;
3492
3493     case VPPCOM_ATTR_GET_ERROR:
3494       if (buffer && buflen && (*buflen >= sizeof (int)))
3495         {
3496           *(int *) buffer = 0;
3497           *buflen = sizeof (int);
3498
3499           VDBG (2, "VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
3500                 *(int *) buffer, *buflen);
3501         }
3502       else
3503         rv = VPPCOM_EINVAL;
3504       break;
3505
3506     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
3507       if (buffer && buflen && (*buflen >= sizeof (u32)))
3508         {
3509
3510           /* VPP-TBD */
3511           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
3512                                 session->tx_fifo ?
3513                                 svm_fifo_size (session->tx_fifo) :
3514                                 vcm->cfg.tx_fifo_size);
3515           *buflen = sizeof (u32);
3516
3517           VDBG (2, "VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3518                 " #VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer,
3519                 *buflen);
3520         }
3521       else
3522         rv = VPPCOM_EINVAL;
3523       break;
3524
3525     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
3526       if (buffer && buflen && (*buflen == sizeof (u32)))
3527         {
3528           /* VPP-TBD */
3529           session->sndbuf_size = *(u32 *) buffer;
3530           VDBG (2, "VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3531                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3532                 *buflen);
3533         }
3534       else
3535         rv = VPPCOM_EINVAL;
3536       break;
3537
3538     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
3539       if (buffer && buflen && (*buflen >= sizeof (u32)))
3540         {
3541
3542           /* VPP-TBD */
3543           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
3544                                 session->rx_fifo ?
3545                                 svm_fifo_size (session->rx_fifo) :
3546                                 vcm->cfg.rx_fifo_size);
3547           *buflen = sizeof (u32);
3548
3549           VDBG (2, "VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), buflen %d, "
3550                 "#VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer, *buflen);
3551         }
3552       else
3553         rv = VPPCOM_EINVAL;
3554       break;
3555
3556     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
3557       if (buffer && buflen && (*buflen == sizeof (u32)))
3558         {
3559           /* VPP-TBD */
3560           session->rcvbuf_size = *(u32 *) buffer;
3561           VDBG (2, "VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), buflen %d,"
3562                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3563                 *buflen);
3564         }
3565       else
3566         rv = VPPCOM_EINVAL;
3567       break;
3568
3569     case VPPCOM_ATTR_GET_REUSEADDR:
3570       if (buffer && buflen && (*buflen >= sizeof (int)))
3571         {
3572           /* VPP-TBD */
3573           *(int *) buffer = vcl_session_has_attr (session,
3574                                                   VCL_SESS_ATTR_REUSEADDR);
3575           *buflen = sizeof (int);
3576
3577           VDBG (2, "VPPCOM_ATTR_GET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3578                 *(int *) buffer, *buflen);
3579         }
3580       else
3581         rv = VPPCOM_EINVAL;
3582       break;
3583
3584     case VPPCOM_ATTR_SET_REUSEADDR:
3585       if (buffer && buflen && (*buflen == sizeof (int)) &&
3586           !vcl_session_has_attr (session, VCL_SESS_ATTR_LISTEN))
3587         {
3588           /* VPP-TBD */
3589           if (*(int *) buffer)
3590             vcl_session_set_attr (session, VCL_SESS_ATTR_REUSEADDR);
3591           else
3592             vcl_session_clear_attr (session, VCL_SESS_ATTR_REUSEADDR);
3593
3594           VDBG (2, "VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3595                 vcl_session_has_attr (session, VCL_SESS_ATTR_REUSEADDR),
3596                 *buflen);
3597         }
3598       else
3599         rv = VPPCOM_EINVAL;
3600       break;
3601
3602     case VPPCOM_ATTR_GET_REUSEPORT:
3603       if (buffer && buflen && (*buflen >= sizeof (int)))
3604         {
3605           /* VPP-TBD */
3606           *(int *) buffer = vcl_session_has_attr (session,
3607                                                   VCL_SESS_ATTR_REUSEPORT);
3608           *buflen = sizeof (int);
3609
3610           VDBG (2, "VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3611                 *(int *) buffer, *buflen);
3612         }
3613       else
3614         rv = VPPCOM_EINVAL;
3615       break;
3616
3617     case VPPCOM_ATTR_SET_REUSEPORT:
3618       if (buffer && buflen && (*buflen == sizeof (int)) &&
3619           !vcl_session_has_attr (session, VCL_SESS_ATTR_LISTEN))
3620         {
3621           /* VPP-TBD */
3622           if (*(int *) buffer)
3623             vcl_session_set_attr (session, VCL_SESS_ATTR_REUSEPORT);
3624           else
3625             vcl_session_clear_attr (session, VCL_SESS_ATTR_REUSEPORT);
3626
3627           VDBG (2, "VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3628                 vcl_session_has_attr (session, VCL_SESS_ATTR_REUSEPORT),
3629                 *buflen);
3630         }
3631       else
3632         rv = VPPCOM_EINVAL;
3633       break;
3634
3635     case VPPCOM_ATTR_GET_BROADCAST:
3636       if (buffer && buflen && (*buflen >= sizeof (int)))
3637         {
3638           /* VPP-TBD */
3639           *(int *) buffer = vcl_session_has_attr (session,
3640                                                   VCL_SESS_ATTR_BROADCAST);
3641           *buflen = sizeof (int);
3642
3643           VDBG (2, "VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3644                 *(int *) buffer, *buflen);
3645         }
3646       else
3647         rv = VPPCOM_EINVAL;
3648       break;
3649
3650     case VPPCOM_ATTR_SET_BROADCAST:
3651       if (buffer && buflen && (*buflen == sizeof (int)))
3652         {
3653           /* VPP-TBD */
3654           if (*(int *) buffer)
3655             vcl_session_set_attr (session, VCL_SESS_ATTR_BROADCAST);
3656           else
3657             vcl_session_clear_attr (session, VCL_SESS_ATTR_BROADCAST);
3658
3659           VDBG (2, "VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3660                 vcl_session_has_attr (session, VCL_SESS_ATTR_BROADCAST),
3661                 *buflen);
3662         }
3663       else
3664         rv = VPPCOM_EINVAL;
3665       break;
3666
3667     case VPPCOM_ATTR_GET_V6ONLY:
3668       if (buffer && buflen && (*buflen >= sizeof (int)))
3669         {
3670           /* VPP-TBD */
3671           *(int *) buffer = vcl_session_has_attr (session,
3672                                                   VCL_SESS_ATTR_V6ONLY);
3673           *buflen = sizeof (int);
3674
3675           VDBG (2, "VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3676                 *(int *) buffer, *buflen);
3677         }
3678       else
3679         rv = VPPCOM_EINVAL;
3680       break;
3681
3682     case VPPCOM_ATTR_SET_V6ONLY:
3683       if (buffer && buflen && (*buflen == sizeof (int)))
3684         {
3685           /* VPP-TBD */
3686           if (*(int *) buffer)
3687             vcl_session_set_attr (session, VCL_SESS_ATTR_V6ONLY);
3688           else
3689             vcl_session_clear_attr (session, VCL_SESS_ATTR_V6ONLY);
3690
3691           VDBG (2, "VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3692                 vcl_session_has_attr (session, VCL_SESS_ATTR_V6ONLY),
3693                 *buflen);
3694         }
3695       else
3696         rv = VPPCOM_EINVAL;
3697       break;
3698
3699     case VPPCOM_ATTR_GET_KEEPALIVE:
3700       if (buffer && buflen && (*buflen >= sizeof (int)))
3701         {
3702           /* VPP-TBD */
3703           *(int *) buffer = vcl_session_has_attr (session,
3704                                                   VCL_SESS_ATTR_KEEPALIVE);
3705           *buflen = sizeof (int);
3706
3707           VDBG (2, "VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3708                 *(int *) buffer, *buflen);
3709         }
3710       else
3711         rv = VPPCOM_EINVAL;
3712       break;
3713
3714     case VPPCOM_ATTR_SET_KEEPALIVE:
3715       if (buffer && buflen && (*buflen == sizeof (int)))
3716         {
3717           /* VPP-TBD */
3718           if (*(int *) buffer)
3719             vcl_session_set_attr (session, VCL_SESS_ATTR_KEEPALIVE);
3720           else
3721             vcl_session_clear_attr (session, VCL_SESS_ATTR_KEEPALIVE);
3722
3723           VDBG (2, "VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3724                 vcl_session_has_attr (session, VCL_SESS_ATTR_KEEPALIVE),
3725                 *buflen);
3726         }
3727       else
3728         rv = VPPCOM_EINVAL;
3729       break;
3730
3731     case VPPCOM_ATTR_GET_TCP_NODELAY:
3732       if (buffer && buflen && (*buflen >= sizeof (int)))
3733         {
3734           /* VPP-TBD */
3735           *(int *) buffer = vcl_session_has_attr (session,
3736                                                   VCL_SESS_ATTR_TCP_NODELAY);
3737           *buflen = sizeof (int);
3738
3739           VDBG (2, "VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3740                 *(int *) buffer, *buflen);
3741         }
3742       else
3743         rv = VPPCOM_EINVAL;
3744       break;
3745
3746     case VPPCOM_ATTR_SET_TCP_NODELAY:
3747       if (buffer && buflen && (*buflen == sizeof (int)))
3748         {
3749           /* VPP-TBD */
3750           if (*(int *) buffer)
3751             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_NODELAY);
3752           else
3753             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_NODELAY);
3754
3755           VDBG (2, "VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3756                 vcl_session_has_attr (session, VCL_SESS_ATTR_TCP_NODELAY),
3757                 *buflen);
3758         }
3759       else
3760         rv = VPPCOM_EINVAL;
3761       break;
3762
3763     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
3764       if (buffer && buflen && (*buflen >= sizeof (int)))
3765         {
3766           /* VPP-TBD */
3767           *(int *) buffer = vcl_session_has_attr (session,
3768                                                   VCL_SESS_ATTR_TCP_KEEPIDLE);
3769           *buflen = sizeof (int);
3770
3771           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3772                 *(int *) buffer, *buflen);
3773         }
3774       else
3775         rv = VPPCOM_EINVAL;
3776       break;
3777
3778     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3779       if (buffer && buflen && (*buflen == sizeof (int)))
3780         {
3781           /* VPP-TBD */
3782           if (*(int *) buffer)
3783             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_KEEPIDLE);
3784           else
3785             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_KEEPIDLE);
3786
3787           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3788                 vcl_session_has_attr (session,
3789                                       VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
3790         }
3791       else
3792         rv = VPPCOM_EINVAL;
3793       break;
3794
3795     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
3796       if (buffer && buflen && (*buflen >= sizeof (int)))
3797         {
3798           /* VPP-TBD */
3799           *(int *) buffer = vcl_session_has_attr (session,
3800                                                   VCL_SESS_ATTR_TCP_KEEPINTVL);
3801           *buflen = sizeof (int);
3802
3803           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3804                 *(int *) buffer, *buflen);
3805         }
3806       else
3807         rv = VPPCOM_EINVAL;
3808       break;
3809
3810     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3811       if (buffer && buflen && (*buflen == sizeof (int)))
3812         {
3813           /* VPP-TBD */
3814           if (*(int *) buffer)
3815             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_KEEPINTVL);
3816           else
3817             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_KEEPINTVL);
3818
3819           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3820                 vcl_session_has_attr (session,
3821                                       VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
3822         }
3823       else
3824         rv = VPPCOM_EINVAL;
3825       break;
3826
3827     case VPPCOM_ATTR_GET_TCP_USER_MSS:
3828       if (!(buffer && buflen && (*buflen >= sizeof (u32))))
3829         {
3830           rv = VPPCOM_EINVAL;
3831           break;
3832         }
3833
3834       tea.type = TRANSPORT_ENDPT_ATTR_MSS;
3835       tea.mss = *(u32 *) buffer;
3836       if (vcl_session_transport_attr (wrk, session, 1 /* is_get */, &tea))
3837         rv = VPPCOM_ENOPROTOOPT;
3838
3839       if (!rv)
3840         {
3841           *(u32 *) buffer = tea.mss;
3842           *buflen = sizeof (int);
3843         }
3844
3845       VDBG (2, "VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d", *(int *) buffer,
3846             *buflen);
3847       break;
3848
3849     case VPPCOM_ATTR_SET_TCP_USER_MSS:
3850       if (!(buffer && buflen && (*buflen == sizeof (u32))))
3851         {
3852           rv = VPPCOM_EINVAL;
3853           break;
3854         }
3855
3856       tea.type = TRANSPORT_ENDPT_ATTR_MSS;
3857       tea.mss = *(u32 *) buffer;
3858       if (vcl_session_transport_attr (wrk, session, 0 /* is_get */, &tea))
3859         rv = VPPCOM_ENOPROTOOPT;
3860
3861       VDBG (2, "VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d", tea.mss,
3862             *buflen);
3863       break;
3864
3865     case VPPCOM_ATTR_SET_CONNECTED:
3866       session->flags |= VCL_SESSION_F_CONNECTED;
3867       break;
3868
3869     case VPPCOM_ATTR_SET_CKPAIR:
3870       if (!(buffer && buflen && (*buflen == sizeof (int))) ||
3871           !vcl_session_has_crypto (session))
3872         {
3873           rv = VPPCOM_EINVAL;
3874           break;
3875         }
3876       if (!session->ext_config)
3877         {
3878           vcl_session_alloc_ext_cfg (session, TRANSPORT_ENDPT_EXT_CFG_CRYPTO,
3879                                      sizeof (transport_endpt_ext_cfg_t));
3880         }
3881       else if (session->ext_config->type != TRANSPORT_ENDPT_EXT_CFG_CRYPTO)
3882         {
3883           rv = VPPCOM_EINVAL;
3884           break;
3885         }
3886
3887       session->ext_config->crypto.ckpair_index = *(uint32_t *) buffer;
3888       break;
3889
3890     case VPPCOM_ATTR_SET_VRF:
3891       if (!(buffer && buflen && (*buflen == sizeof (u32))))
3892         {
3893           rv = VPPCOM_EINVAL;
3894           break;
3895         }
3896       session->vrf = *(u32 *) buffer;
3897       break;
3898
3899     case VPPCOM_ATTR_GET_VRF:
3900       if (!(buffer && buflen && (*buflen >= sizeof (u32))))
3901         {
3902           rv = VPPCOM_EINVAL;
3903           break;
3904         }
3905       *(u32 *) buffer = session->vrf;
3906       *buflen = sizeof (u32);
3907       break;
3908
3909     case VPPCOM_ATTR_GET_DOMAIN:
3910       if (!(buffer && buflen && (*buflen >= sizeof (int))))
3911         {
3912           rv = VPPCOM_EINVAL;
3913           break;
3914         }
3915
3916       if (session->transport.is_ip4)
3917         *(int *) buffer = AF_INET;
3918       else
3919         *(int *) buffer = AF_INET6;
3920       *buflen = sizeof (int);
3921
3922       VDBG (2, "VPPCOM_ATTR_GET_DOMAIN: %d, buflen %u", *(int *) buffer,
3923             *buflen);
3924       break;
3925
3926     case VPPCOM_ATTR_SET_ENDPT_EXT_CFG:
3927       if (!(buffer && buflen && (*buflen > 0)))
3928         {
3929           rv = VPPCOM_EINVAL;
3930           break;
3931         }
3932       if (session->ext_config)
3933         {
3934           rv = VPPCOM_EINVAL;
3935           break;
3936         }
3937       vcl_session_alloc_ext_cfg (session, TRANSPORT_ENDPT_EXT_CFG_NONE,
3938                                  *buflen + sizeof (u32));
3939       clib_memcpy (session->ext_config->data, buffer, *buflen);
3940       session->ext_config->len = *buflen;
3941       break;
3942
3943     default:
3944       rv = VPPCOM_EINVAL;
3945       break;
3946     }
3947
3948   return rv;
3949 }
3950
3951 int
3952 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
3953                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3954 {
3955   vcl_worker_t *wrk = vcl_worker_get_current ();
3956   vcl_session_t *session;
3957   int rv = VPPCOM_OK;
3958
3959   if (flags == 0)
3960     rv = vppcom_session_read (session_handle, buffer, buflen);
3961   else if (flags & MSG_PEEK)
3962     rv = vppcom_session_peek (session_handle, buffer, buflen);
3963   else
3964     {
3965       VDBG (0, "Unsupport flags for recvfrom %d", flags);
3966       return VPPCOM_EAFNOSUPPORT;
3967     }
3968
3969   if (ep && rv > 0)
3970     {
3971       session = vcl_session_get_w_handle (wrk, session_handle);
3972       if (session->transport.is_ip4)
3973         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3974                           sizeof (ip4_address_t));
3975       else
3976         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3977                           sizeof (ip6_address_t));
3978       ep->is_ip4 = session->transport.is_ip4;
3979       ep->port = session->transport.rmt_port;
3980     }
3981
3982   return rv;
3983 }
3984
3985 int
3986 vppcom_session_sendto (uint32_t session_handle, void *buffer,
3987                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
3988 {
3989   vcl_worker_t *wrk = vcl_worker_get_current ();
3990   vcl_session_t *s;
3991
3992   s = vcl_session_get_w_handle (wrk, session_handle);
3993   if (PREDICT_FALSE (!s))
3994     return VPPCOM_EBADFD;
3995
3996   if (ep)
3997     {
3998       if (!vcl_session_is_cl (s))
3999         return VPPCOM_EINVAL;
4000
4001       /* Session not connected/bound in vpp. Create it by 'connecting' it */
4002       if (PREDICT_FALSE (s->session_state == VCL_STATE_CLOSED))
4003         {
4004           u32 session_index = s->session_index;
4005           f64 timeout = vcm->cfg.session_timeout;
4006           int rv;
4007
4008           vcl_send_session_connect (wrk, s);
4009           rv = vppcom_wait_for_session_state_change (session_index,
4010                                                      VCL_STATE_READY,
4011                                                      timeout);
4012           if (rv < 0)
4013             return rv;
4014           s = vcl_session_get (wrk, session_index);
4015         }
4016
4017       s->transport.is_ip4 = ep->is_ip4;
4018       s->transport.rmt_port = ep->port;
4019       vcl_ip_copy_from_ep (&s->transport.rmt_ip, ep);
4020     }
4021
4022   if (flags)
4023     {
4024       // TBD check the flags and do the right thing
4025       VDBG (2, "handling flags 0x%u (%d) not implemented yet.", flags, flags);
4026     }
4027
4028   return (vppcom_session_write_inline (wrk, s, buffer, buflen, 1,
4029                                        s->is_dgram ? 1 : 0));
4030 }
4031
4032 int
4033 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
4034 {
4035   vcl_worker_t *wrk = vcl_worker_get_current ();
4036   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
4037   u32 i, keep_trying = 1;
4038   svm_msg_q_msg_t msg;
4039   session_event_t *e;
4040   int rv, num_ev = 0;
4041
4042   VDBG (3, "vp %p, nsids %u, wait_for_time %f", vp, n_sids, wait_for_time);
4043
4044   if (!vp)
4045     return VPPCOM_EFAULT;
4046
4047   do
4048     {
4049       vcl_session_t *session;
4050
4051       /* Dequeue all events and drop all unhandled io events */
4052       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
4053         {
4054           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
4055           vcl_handle_mq_event (wrk, e);
4056           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
4057         }
4058       vec_reset_length (wrk->unhandled_evts_vector);
4059
4060       for (i = 0; i < n_sids; i++)
4061         {
4062           session = vcl_session_get (wrk, vp[i].sh);
4063           if (!session)
4064             {
4065               vp[i].revents = POLLHUP;
4066               num_ev++;
4067               continue;
4068             }
4069
4070           vp[i].revents = 0;
4071
4072           if (POLLIN & vp[i].events)
4073             {
4074               rv = vcl_session_read_ready (session);
4075               if (rv > 0)
4076                 {
4077                   vp[i].revents |= POLLIN;
4078                   num_ev++;
4079                 }
4080               else if (rv < 0)
4081                 {
4082                   switch (rv)
4083                     {
4084                     case VPPCOM_ECONNRESET:
4085                       vp[i].revents = POLLHUP;
4086                       break;
4087
4088                     default:
4089                       vp[i].revents = POLLERR;
4090                       break;
4091                     }
4092                   num_ev++;
4093                 }
4094             }
4095
4096           if (POLLOUT & vp[i].events)
4097             {
4098               rv = vcl_session_write_ready (session);
4099               if (rv > 0)
4100                 {
4101                   vp[i].revents |= POLLOUT;
4102                   num_ev++;
4103                 }
4104               else if (rv < 0)
4105                 {
4106                   switch (rv)
4107                     {
4108                     case VPPCOM_ECONNRESET:
4109                       vp[i].revents = POLLHUP;
4110                       break;
4111
4112                     default:
4113                       vp[i].revents = POLLERR;
4114                       break;
4115                     }
4116                   num_ev++;
4117                 }
4118             }
4119
4120           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
4121             {
4122               vp[i].revents = POLLNVAL;
4123               num_ev++;
4124             }
4125         }
4126       if (wait_for_time != -1)
4127         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
4128     }
4129   while ((num_ev == 0) && keep_trying);
4130
4131   return num_ev;
4132 }
4133
4134 int
4135 vppcom_mq_epoll_fd (void)
4136 {
4137   vcl_worker_t *wrk = vcl_worker_get_current ();
4138   return wrk->mqs_epfd;
4139 }
4140
4141 int
4142 vppcom_session_index (vcl_session_handle_t session_handle)
4143 {
4144   return session_handle & 0xFFFFFF;
4145 }
4146
4147 int
4148 vppcom_session_worker (vcl_session_handle_t session_handle)
4149 {
4150   return session_handle >> 24;
4151 }
4152
4153 int
4154 vppcom_worker_register (void)
4155 {
4156   if (!vcl_worker_alloc_and_init ())
4157     return VPPCOM_EEXIST;
4158
4159   if (vcl_worker_register_with_vpp ())
4160     return VPPCOM_EEXIST;
4161
4162   return VPPCOM_OK;
4163 }
4164
4165 void
4166 vppcom_worker_unregister (void)
4167 {
4168   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
4169   vcl_set_worker_index (~0);
4170 }
4171
4172 void
4173 vppcom_worker_index_set (int index)
4174 {
4175   vcl_set_worker_index (index);
4176 }
4177
4178 int
4179 vppcom_worker_index (void)
4180 {
4181   return vcl_get_worker_index ();
4182 }
4183
4184 int
4185 vppcom_worker_mqs_epfd (void)
4186 {
4187   vcl_worker_t *wrk = vcl_worker_get_current ();
4188   if (!vcm->cfg.use_mq_eventfd)
4189     return -1;
4190   return wrk->mqs_epfd;
4191 }
4192
4193 int
4194 vppcom_session_is_connectable_listener (uint32_t session_handle)
4195 {
4196   vcl_session_t *session;
4197   vcl_worker_t *wrk = vcl_worker_get_current ();
4198   session = vcl_session_get_w_handle (wrk, session_handle);
4199   if (!session)
4200     return VPPCOM_EBADFD;
4201   return vcl_session_is_connectable_listener (wrk, session);
4202 }
4203
4204 int
4205 vppcom_session_listener (uint32_t session_handle)
4206 {
4207   vcl_worker_t *wrk = vcl_worker_get_current ();
4208   vcl_session_t *listen_session, *session;
4209   session = vcl_session_get_w_handle (wrk, session_handle);
4210   if (!session)
4211     return VPPCOM_EBADFD;
4212   if (session->listener_index == VCL_INVALID_SESSION_INDEX)
4213     return VPPCOM_EBADFD;
4214   listen_session = vcl_session_get_w_handle (wrk, session->listener_index);
4215   if (!listen_session)
4216     return VPPCOM_EBADFD;
4217   return vcl_session_handle (listen_session);
4218 }
4219
4220 int
4221 vppcom_session_n_accepted (uint32_t session_handle)
4222 {
4223   vcl_worker_t *wrk = vcl_worker_get_current ();
4224   vcl_session_t *session = vcl_session_get_w_handle (wrk, session_handle);
4225   if (!session)
4226     return VPPCOM_EBADFD;
4227   return session->n_accepted_sessions;
4228 }
4229
4230 const char *
4231 vppcom_proto_str (vppcom_proto_t proto)
4232 {
4233   char const *proto_str;
4234
4235   switch (proto)
4236     {
4237     case VPPCOM_PROTO_TCP:
4238       proto_str = "TCP";
4239       break;
4240     case VPPCOM_PROTO_UDP:
4241       proto_str = "UDP";
4242       break;
4243     case VPPCOM_PROTO_TLS:
4244       proto_str = "TLS";
4245       break;
4246     case VPPCOM_PROTO_QUIC:
4247       proto_str = "QUIC";
4248       break;
4249     case VPPCOM_PROTO_DTLS:
4250       proto_str = "DTLS";
4251       break;
4252     case VPPCOM_PROTO_SRTP:
4253       proto_str = "SRTP";
4254       break;
4255     default:
4256       proto_str = "UNKNOWN";
4257       break;
4258     }
4259   return proto_str;
4260 }
4261
4262 const char *
4263 vppcom_retval_str (int retval)
4264 {
4265   char const *st;
4266
4267   switch (retval)
4268     {
4269     case VPPCOM_OK:
4270       st = "VPPCOM_OK";
4271       break;
4272
4273     case VPPCOM_EAGAIN:
4274       st = "VPPCOM_EAGAIN";
4275       break;
4276
4277     case VPPCOM_EFAULT:
4278       st = "VPPCOM_EFAULT";
4279       break;
4280
4281     case VPPCOM_ENOMEM:
4282       st = "VPPCOM_ENOMEM";
4283       break;
4284
4285     case VPPCOM_EINVAL:
4286       st = "VPPCOM_EINVAL";
4287       break;
4288
4289     case VPPCOM_EBADFD:
4290       st = "VPPCOM_EBADFD";
4291       break;
4292
4293     case VPPCOM_EAFNOSUPPORT:
4294       st = "VPPCOM_EAFNOSUPPORT";
4295       break;
4296
4297     case VPPCOM_ECONNABORTED:
4298       st = "VPPCOM_ECONNABORTED";
4299       break;
4300
4301     case VPPCOM_ECONNRESET:
4302       st = "VPPCOM_ECONNRESET";
4303       break;
4304
4305     case VPPCOM_ENOTCONN:
4306       st = "VPPCOM_ENOTCONN";
4307       break;
4308
4309     case VPPCOM_ECONNREFUSED:
4310       st = "VPPCOM_ECONNREFUSED";
4311       break;
4312
4313     case VPPCOM_ETIMEDOUT:
4314       st = "VPPCOM_ETIMEDOUT";
4315       break;
4316
4317     default:
4318       st = "UNKNOWN_STATE";
4319       break;
4320     }
4321
4322   return st;
4323 }
4324
4325 int
4326 vppcom_add_cert_key_pair (vppcom_cert_key_pair_t *ckpair)
4327 {
4328   if (vcm->cfg.vpp_app_socket_api)
4329     {
4330       clib_warning ("not supported");
4331       return VPPCOM_EINVAL;
4332     }
4333   return vcl_bapi_add_cert_key_pair (ckpair);
4334 }
4335
4336 int
4337 vppcom_del_cert_key_pair (uint32_t ckpair_index)
4338 {
4339   if (vcm->cfg.vpp_app_socket_api)
4340     {
4341       clib_warning ("not supported");
4342       return VPPCOM_EINVAL;
4343     }
4344   return vcl_bapi_del_cert_key_pair (ckpair_index);
4345 }
4346
4347 /*
4348  * fd.io coding-style-patch-verification: ON
4349  *
4350  * Local Variables:
4351  * eval: (c-set-style "gnu")
4352  * End:
4353  */