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