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