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