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