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