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