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