vcl: handle postponed disconnects with select
[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_nonblocking)
2056         {
2057           if (vcl_session_is_closing (s))
2058             return vcl_session_closing_error (s);
2059           if (is_ct)
2060             svm_fifo_unset_event (s->rx_fifo);
2061           svm_fifo_unset_event (rx_fifo);
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_nonblocking)
2168         {
2169           if (is_ct)
2170             svm_fifo_unset_event (s->rx_fifo);
2171           svm_fifo_unset_event (rx_fifo);
2172           return VPPCOM_EWOULDBLOCK;
2173         }
2174       while (svm_fifo_is_empty_cons (rx_fifo))
2175         {
2176           if (vcl_session_is_closing (s))
2177             return vcl_session_closing_error (s);
2178
2179           if (is_ct)
2180             svm_fifo_unset_event (s->rx_fifo);
2181           svm_fifo_unset_event (rx_fifo);
2182
2183           svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2184           vcl_worker_flush_mq_events (wrk);
2185         }
2186     }
2187
2188   n_read = svm_fifo_segments (rx_fifo, s->rx_bytes_pending,
2189                               (svm_fifo_seg_t *) ds, &n_segments, max_bytes);
2190   if (n_read < 0)
2191     return VPPCOM_EAGAIN;
2192
2193   if (svm_fifo_max_dequeue_cons (rx_fifo) == n_read)
2194     {
2195       if (is_ct)
2196         svm_fifo_unset_event (s->rx_fifo);
2197       svm_fifo_unset_event (rx_fifo);
2198       if (svm_fifo_max_dequeue_cons (rx_fifo) != n_read
2199           && svm_fifo_set_event (rx_fifo)
2200           && vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK))
2201         {
2202           session_event_t *e;
2203           vec_add2 (wrk->unhandled_evts_vector, e, 1);
2204           e->event_type = SESSION_IO_EVT_RX;
2205           e->session_index = s->session_index;
2206         }
2207     }
2208
2209   s->rx_bytes_pending += n_read;
2210   return n_read;
2211 }
2212
2213 void
2214 vppcom_session_free_segments (uint32_t session_handle, uint32_t n_bytes)
2215 {
2216   vcl_worker_t *wrk = vcl_worker_get_current ();
2217   vcl_session_t *s;
2218   u8 is_ct;
2219
2220   s = vcl_session_get_w_handle (wrk, session_handle);
2221   if (PREDICT_FALSE (!s || (s->flags & VCL_SESSION_F_IS_VEP)))
2222     return;
2223
2224   is_ct = vcl_session_is_ct (s);
2225   svm_fifo_dequeue_drop (is_ct ? s->ct_rx_fifo : s->rx_fifo, n_bytes);
2226
2227   ASSERT (s->rx_bytes_pending >= n_bytes);
2228   s->rx_bytes_pending -= n_bytes;
2229 }
2230
2231 always_inline u8
2232 vcl_fifo_is_writeable (svm_fifo_t * f, u32 len, u8 is_dgram)
2233 {
2234   u32 max_enq = svm_fifo_max_enqueue_prod (f);
2235   if (is_dgram)
2236     return max_enq >= (sizeof (session_dgram_hdr_t) + len);
2237   else
2238     return max_enq > 0;
2239 }
2240
2241 always_inline int
2242 vppcom_session_write_inline (vcl_worker_t *wrk, vcl_session_t *s, void *buf,
2243                              size_t n, u8 is_flush, u8 is_dgram)
2244 {
2245   int n_write, is_nonblocking;
2246   session_evt_type_t et;
2247   svm_fifo_t *tx_fifo;
2248   svm_msg_q_t *mq;
2249   u8 is_ct;
2250
2251   /* Accept zero length writes but just return */
2252   if (PREDICT_FALSE (!n))
2253     return VPPCOM_OK;
2254
2255   if (PREDICT_FALSE (!buf))
2256     return VPPCOM_EFAULT;
2257
2258   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_IS_VEP))
2259     {
2260       VDBG (0, "ERROR: session %u [0x%llx]: cannot write to an epoll"
2261             " session!", s->session_index, s->vpp_handle);
2262       return VPPCOM_EBADFD;
2263     }
2264
2265   if (PREDICT_FALSE (!vcl_session_is_open (s)))
2266     {
2267       VDBG (1, "session %u [0x%llx]: is not open! state 0x%x (%s)",
2268             s->session_index, s->vpp_handle, s->session_state,
2269             vcl_session_state_str (s->session_state));
2270       return vcl_session_closed_error (s);;
2271     }
2272
2273   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_WR_SHUTDOWN))
2274     {
2275       VDBG (1, "session %u [0x%llx]: is shutdown! state 0x%x (%s)",
2276             s->session_index, s->vpp_handle, s->session_state,
2277             vcl_session_state_str (s->session_state));
2278       return VPPCOM_EPIPE;
2279     }
2280
2281   is_ct = vcl_session_is_ct (s);
2282   tx_fifo = is_ct ? s->ct_tx_fifo : s->tx_fifo;
2283   is_nonblocking = vcl_session_has_attr (s, VCL_SESS_ATTR_NONBLOCK);
2284
2285   mq = wrk->app_event_queue;
2286   if (!vcl_fifo_is_writeable (tx_fifo, n, is_dgram))
2287     {
2288       if (is_nonblocking)
2289         {
2290           return VPPCOM_EWOULDBLOCK;
2291         }
2292       while (!vcl_fifo_is_writeable (tx_fifo, n, is_dgram))
2293         {
2294           svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
2295           if (vcl_session_is_closing (s))
2296             return vcl_session_closing_error (s);
2297
2298           svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2299           vcl_worker_flush_mq_events (wrk);
2300         }
2301     }
2302
2303   et = SESSION_IO_EVT_TX;
2304   if (is_flush && !is_ct)
2305     et = SESSION_IO_EVT_TX_FLUSH;
2306
2307   if (is_dgram)
2308     {
2309       et = vcl_session_dgram_tx_evt (s, et);
2310       n_write =
2311         app_send_dgram_raw_gso (tx_fifo, &s->transport, s->vpp_evt_q, buf, n,
2312                                 s->gso_size, et, 0 /* do_evt */, SVM_Q_WAIT);
2313     }
2314   else
2315     {
2316       n_write = app_send_stream_raw (tx_fifo, s->vpp_evt_q, buf, n, et,
2317                                      0 /* do_evt */, SVM_Q_WAIT);
2318     }
2319
2320   if (svm_fifo_set_event (s->tx_fifo))
2321     app_send_io_evt_to_vpp (
2322       s->vpp_evt_q, s->tx_fifo->shr->master_session_index, et, SVM_Q_WAIT);
2323
2324   /* The underlying fifo segment can run out of memory */
2325   if (PREDICT_FALSE (n_write < 0))
2326     return VPPCOM_EAGAIN;
2327
2328   VDBG (2, "session %u [0x%llx]: wrote %d bytes", s->session_index,
2329         s->vpp_handle, n_write);
2330
2331   return n_write;
2332 }
2333
2334 int
2335 vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
2336 {
2337   vcl_worker_t *wrk = vcl_worker_get_current ();
2338   vcl_session_t *s;
2339
2340   s = vcl_session_get_w_handle (wrk, session_handle);
2341   if (PREDICT_FALSE (!s))
2342     return VPPCOM_EBADFD;
2343
2344   return vppcom_session_write_inline (wrk, s, buf, n, 0 /* is_flush */,
2345                                       s->is_dgram ? 1 : 0);
2346 }
2347
2348 int
2349 vppcom_session_write_msg (uint32_t session_handle, void *buf, size_t n)
2350 {
2351   vcl_worker_t *wrk = vcl_worker_get_current ();
2352   vcl_session_t *s;
2353
2354   s = vcl_session_get_w_handle (wrk, session_handle);
2355   if (PREDICT_FALSE (!s))
2356     return VPPCOM_EBADFD;
2357
2358   return vppcom_session_write_inline (wrk, s, buf, n, 1 /* is_flush */,
2359                                       s->is_dgram ? 1 : 0);
2360 }
2361
2362 #define vcl_fifo_rx_evt_valid_or_break(_s)                              \
2363 if (PREDICT_FALSE (!_s->rx_fifo))                                       \
2364   break;                                                                \
2365 if (PREDICT_FALSE (svm_fifo_is_empty (_s->rx_fifo)))                    \
2366   {                                                                     \
2367     if (!vcl_session_is_ct (_s))                                        \
2368       {                                                                 \
2369         svm_fifo_unset_event (_s->rx_fifo);                             \
2370         if (svm_fifo_is_empty (_s->rx_fifo))                            \
2371           break;                                                        \
2372       }                                                                 \
2373     else if (svm_fifo_is_empty (_s->ct_rx_fifo))                        \
2374       {                                                                 \
2375         svm_fifo_unset_event (_s->rx_fifo); /* rx evts on actual fifo*/ \
2376         if (svm_fifo_is_empty (_s->ct_rx_fifo))                         \
2377           break;                                                        \
2378       }                                                                 \
2379   }                                                                     \
2380
2381 static void
2382 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2383                             unsigned long n_bits, unsigned long *read_map,
2384                             unsigned long *write_map,
2385                             unsigned long *except_map, u32 * bits_set)
2386 {
2387   session_disconnected_msg_t *disconnected_msg;
2388   session_connected_msg_t *connected_msg;
2389   vcl_session_t *s;
2390   u32 sid;
2391
2392   switch (e->event_type)
2393     {
2394     case SESSION_IO_EVT_RX:
2395       sid = e->session_index;
2396       s = vcl_session_get (wrk, sid);
2397       if (!s || !vcl_session_is_open (s))
2398         break;
2399       vcl_fifo_rx_evt_valid_or_break (s);
2400       if (sid < n_bits && read_map)
2401         {
2402           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2403           *bits_set += 1;
2404         }
2405       break;
2406     case SESSION_IO_EVT_TX:
2407       sid = e->session_index;
2408       s = vcl_session_get (wrk, sid);
2409       if (!s || !vcl_session_is_open (s))
2410         break;
2411       if (sid < n_bits && write_map)
2412         {
2413           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2414           *bits_set += 1;
2415         }
2416       break;
2417     case SESSION_CTRL_EVT_ACCEPTED:
2418       if (!e->postponed)
2419         s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
2420       else
2421         s = vcl_session_get (wrk, e->session_index);
2422       if (!s)
2423         break;
2424       sid = s->session_index;
2425       if (sid < n_bits && read_map)
2426         {
2427           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2428           *bits_set += 1;
2429         }
2430       break;
2431     case SESSION_CTRL_EVT_CONNECTED:
2432       if (!e->postponed)
2433         {
2434           connected_msg = (session_connected_msg_t *) e->data;
2435           sid = vcl_session_connected_handler (wrk, connected_msg);
2436         }
2437       else
2438         sid = e->session_index;
2439       if (sid == VCL_INVALID_SESSION_INDEX)
2440         break;
2441       if (!(sid < n_bits && write_map))
2442         break;
2443       clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2444       *bits_set += 1;
2445       s = vcl_session_get (wrk, sid);
2446       /* We didn't have a fifo when the event was added */
2447       vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2448       break;
2449     case SESSION_CTRL_EVT_DISCONNECTED:
2450       if (!e->postponed)
2451         {
2452           disconnected_msg = (session_disconnected_msg_t *) e->data;
2453           s = vcl_session_disconnected_handler (wrk, disconnected_msg);
2454           if (!s)
2455             break;
2456         }
2457       else
2458         {
2459           s = vcl_session_get (wrk, e->session_index);
2460           s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
2461         }
2462       if (vcl_session_is_closed (s))
2463         {
2464           if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
2465             vcl_session_free (wrk, s);
2466           break;
2467         }
2468       sid = s->session_index;
2469       if (sid < n_bits && except_map)
2470         {
2471           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2472           *bits_set += 1;
2473         }
2474       break;
2475     case SESSION_CTRL_EVT_RESET:
2476       if (!e->postponed)
2477         {
2478           sid =
2479             vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2480           s = vcl_session_get (wrk, sid);
2481         }
2482       else
2483         {
2484           sid = e->session_index;
2485           s = vcl_session_get (wrk, sid);
2486           s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
2487         }
2488       if (vcl_session_is_closed (s))
2489         {
2490           if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
2491             vcl_session_free (wrk, s);
2492           break;
2493         }
2494       if (sid < n_bits && except_map)
2495         {
2496           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2497           *bits_set += 1;
2498         }
2499       break;
2500     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
2501       vcl_session_unlisten_reply_handler (wrk, e->data);
2502       break;
2503     case SESSION_CTRL_EVT_MIGRATED:
2504       vcl_session_migrated_handler (wrk, e->data);
2505       break;
2506     case SESSION_CTRL_EVT_CLEANUP:
2507       vcl_session_cleanup_handler (wrk, e->data);
2508       break;
2509     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
2510       vcl_session_worker_update_reply_handler (wrk, e->data);
2511       break;
2512     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
2513       vcl_session_req_worker_update_handler (wrk, e->data);
2514       break;
2515     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
2516       vcl_session_app_add_segment_handler (wrk, e->data);
2517       break;
2518     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
2519       vcl_session_app_del_segment_handler (wrk, e->data);
2520       break;
2521     case SESSION_CTRL_EVT_APP_WRK_RPC:
2522       vcl_worker_rpc_handler (wrk, e->data);
2523       break;
2524     default:
2525       clib_warning ("unhandled: %u", e->event_type);
2526       break;
2527     }
2528 }
2529
2530 static int
2531 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2532                       unsigned long n_bits, unsigned long *read_map,
2533                       unsigned long *write_map, unsigned long *except_map,
2534                       double time_to_wait, u32 * bits_set)
2535 {
2536   svm_msg_q_msg_t *msg;
2537   session_event_t *e;
2538   u32 i;
2539
2540   if (svm_msg_q_is_empty (mq))
2541     {
2542       if (*bits_set)
2543         return 0;
2544
2545       if (!time_to_wait)
2546         return 0;
2547       else if (time_to_wait < 0)
2548         svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
2549       else
2550         {
2551           if (svm_msg_q_timedwait (mq, time_to_wait))
2552             return 0;
2553         }
2554     }
2555   vcl_mq_dequeue_batch (wrk, mq, ~0);
2556
2557   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2558     {
2559       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2560       e = svm_msg_q_msg_data (mq, msg);
2561       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
2562                                   except_map, bits_set);
2563       svm_msg_q_free_msg (mq, msg);
2564     }
2565   vec_reset_length (wrk->mq_msg_vector);
2566   vcl_handle_pending_wrk_updates (wrk);
2567   return *bits_set;
2568 }
2569
2570 static int
2571 vppcom_select_condvar (vcl_worker_t * wrk, int n_bits,
2572                        vcl_si_set * read_map, vcl_si_set * write_map,
2573                        vcl_si_set * except_map, double time_to_wait,
2574                        u32 * bits_set)
2575 {
2576   double wait = 0, start = 0;
2577
2578   if (!*bits_set)
2579     {
2580       wait = time_to_wait;
2581       start = clib_time_now (&wrk->clib_time);
2582     }
2583
2584   do
2585     {
2586       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
2587                             write_map, except_map, wait, bits_set);
2588       if (*bits_set)
2589         return *bits_set;
2590       if (wait == -1)
2591         continue;
2592
2593       wait = wait - (clib_time_now (&wrk->clib_time) - start);
2594     }
2595   while (wait > 0);
2596
2597   return 0;
2598 }
2599
2600 static int
2601 vppcom_select_eventfd (vcl_worker_t * wrk, int n_bits,
2602                        vcl_si_set * read_map, vcl_si_set * write_map,
2603                        vcl_si_set * except_map, double time_to_wait,
2604                        u32 * bits_set)
2605 {
2606   vcl_mq_evt_conn_t *mqc;
2607   int __clib_unused n_read;
2608   int n_mq_evts, i;
2609   u64 buf;
2610
2611   if (PREDICT_FALSE (wrk->api_client_handle == ~0))
2612     {
2613       vcl_api_retry_attach (wrk);
2614       return 0;
2615     }
2616
2617   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2618   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2619                           vec_len (wrk->mq_events), time_to_wait);
2620   for (i = 0; i < n_mq_evts; i++)
2621     {
2622       if (PREDICT_FALSE (wrk->mq_events[i].data.u32 == ~0))
2623         {
2624           vcl_api_handle_disconnect (wrk);
2625           continue;
2626         }
2627
2628       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2629       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2630       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
2631                             except_map, 0, bits_set);
2632     }
2633
2634   return (n_mq_evts > 0 ? (int) *bits_set : 0);
2635 }
2636
2637 int
2638 vppcom_select (int n_bits, vcl_si_set * read_map, vcl_si_set * write_map,
2639                vcl_si_set * except_map, double time_to_wait)
2640 {
2641   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
2642   vcl_worker_t *wrk = vcl_worker_get_current ();
2643   vcl_session_t *s = 0;
2644   int i;
2645
2646   if (n_bits && read_map)
2647     {
2648       clib_bitmap_validate (wrk->rd_bitmap, minbits);
2649       clib_memcpy_fast (wrk->rd_bitmap, read_map,
2650                         vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2651       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2652     }
2653   if (n_bits && write_map)
2654     {
2655       clib_bitmap_validate (wrk->wr_bitmap, minbits);
2656       clib_memcpy_fast (wrk->wr_bitmap, write_map,
2657                         vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2658       memset (write_map, 0, vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2659     }
2660   if (n_bits && except_map)
2661     {
2662       clib_bitmap_validate (wrk->ex_bitmap, minbits);
2663       clib_memcpy_fast (wrk->ex_bitmap, except_map,
2664                         vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2665       memset (except_map, 0, vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2666     }
2667
2668   if (!n_bits)
2669     return 0;
2670
2671   if (!write_map)
2672     goto check_rd;
2673
2674   clib_bitmap_foreach (sid, wrk->wr_bitmap)
2675     {
2676       if (!(s = vcl_session_get (wrk, sid)))
2677         {
2678           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2679           bits_set++;
2680           continue;
2681         }
2682
2683       if (vcl_session_write_ready (s))
2684         {
2685           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2686           bits_set++;
2687         }
2688       else
2689         {
2690           vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF);
2691         }
2692     }
2693
2694 check_rd:
2695   if (!read_map)
2696     goto check_mq;
2697
2698   clib_bitmap_foreach (sid, wrk->rd_bitmap)
2699     {
2700       if (!(s = vcl_session_get (wrk, sid)))
2701         {
2702           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2703           bits_set++;
2704           continue;
2705         }
2706
2707       if (vcl_session_read_ready (s))
2708         {
2709           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2710           bits_set++;
2711         }
2712     }
2713
2714 check_mq:
2715
2716   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2717     {
2718       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
2719                                   read_map, write_map, except_map, &bits_set);
2720     }
2721   vec_reset_length (wrk->unhandled_evts_vector);
2722
2723   if (vcm->cfg.use_mq_eventfd)
2724     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
2725                            time_to_wait, &bits_set);
2726   else
2727     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
2728                            time_to_wait, &bits_set);
2729
2730   return (bits_set);
2731 }
2732
2733 static inline void
2734 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_handle)
2735 {
2736   vppcom_epoll_t *vep;
2737   u32 sh = vep_handle;
2738   vcl_session_t *s;
2739
2740   if (VPPCOM_DEBUG <= 3)
2741     return;
2742
2743   s = vcl_session_get_w_handle (wrk, vep_handle);
2744   if (PREDICT_FALSE (!s))
2745     {
2746       VDBG (0, "ERROR: Invalid vep_sh (%u)!", vep_handle);
2747       goto done;
2748     }
2749   if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP)))
2750     {
2751       VDBG (0, "ERROR: vep_sh (%u) is not a vep!", vep_handle);
2752       goto done;
2753     }
2754   vep = &s->vep;
2755   VDBG (0, "vep_sh (%u): Dumping epoll chain\n"
2756         "{\n"
2757         "   is_vep         = %u\n"
2758         "   is_vep_session = %u\n"
2759         "   next_sh        = 0x%x (%u)\n"
2760         "}\n", vep_handle, s->flags & VCL_SESSION_F_IS_VEP,
2761         s->flags & VCL_SESSION_F_IS_VEP_SESSION, vep->next_sh, vep->next_sh);
2762
2763   for (sh = vep->next_sh; sh != ~0; sh = vep->next_sh)
2764     {
2765       s = vcl_session_get_w_handle (wrk, sh);
2766       if (PREDICT_FALSE (!s))
2767         {
2768           VDBG (0, "ERROR: Invalid sh (%u)!", sh);
2769           goto done;
2770         }
2771       if (PREDICT_FALSE (s->flags & VCL_SESSION_F_IS_VEP))
2772         {
2773           VDBG (0, "ERROR: sh (%u) is a vep!", vep_handle);
2774         }
2775       else if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2776         {
2777           VDBG (0, "ERROR: sh (%u) is not a vep session handle!", sh);
2778           goto done;
2779         }
2780       vep = &s->vep;
2781       if (PREDICT_FALSE (vep->vep_sh != vep_handle))
2782         VDBG (0, "ERROR: session (%u) vep_sh (%u) != vep_sh (%u)!",
2783               sh, s->vep.vep_sh, vep_handle);
2784       if (s->flags & VCL_SESSION_F_IS_VEP_SESSION)
2785         {
2786           VDBG (0, "vep_sh[%u]: sh 0x%x (%u)\n"
2787                 "{\n"
2788                 "   next_sh        = 0x%x (%u)\n"
2789                 "   prev_sh        = 0x%x (%u)\n"
2790                 "   vep_sh         = 0x%x (%u)\n"
2791                 "   ev.events      = 0x%x\n"
2792                 "   ev.data.u64    = 0x%llx\n"
2793                 "   et_mask        = 0x%x\n"
2794                 "}\n",
2795                 vep_handle, sh, sh, vep->next_sh, vep->next_sh, vep->prev_sh,
2796                 vep->prev_sh, vep->vep_sh, vep->vep_sh, vep->ev.events,
2797                 vep->ev.data.u64, vep->et_mask);
2798         }
2799     }
2800
2801 done:
2802   VDBG (0, "vep_sh (%u): Dump complete!\n", vep_handle);
2803 }
2804
2805 int
2806 vppcom_epoll_create (void)
2807 {
2808   vcl_worker_t *wrk = vcl_worker_get_current ();
2809   vcl_session_t *vep_session;
2810
2811   vep_session = vcl_session_alloc (wrk);
2812
2813   vep_session->flags |= VCL_SESSION_F_IS_VEP;
2814   vep_session->vep.vep_sh = ~0;
2815   vep_session->vep.next_sh = ~0;
2816   vep_session->vep.prev_sh = ~0;
2817   vep_session->vpp_handle = ~0;
2818
2819   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_session->session_index);
2820   VDBG (0, "Created vep_idx %u", vep_session->session_index);
2821
2822   return vcl_session_handle (vep_session);
2823 }
2824
2825 static void
2826 vcl_epoll_ctl_add_unhandled_event (vcl_worker_t *wrk, vcl_session_t *s,
2827                                    u8 is_epollet, session_evt_type_t evt)
2828 {
2829   if (!is_epollet)
2830     {
2831       if (s->vep.lt_next == VCL_INVALID_SESSION_INDEX)
2832         vcl_epoll_lt_add (wrk, s);
2833       return;
2834     }
2835
2836   session_event_t e = { 0 };
2837   e.session_index = s->session_index;
2838   e.event_type = evt;
2839   if (evt == SESSION_IO_EVT_RX)
2840     s->flags &= ~VCL_SESSION_F_HAS_RX_EVT;
2841   vec_add1 (wrk->unhandled_evts_vector, e);
2842 }
2843
2844 int
2845 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2846                   struct epoll_event *event)
2847 {
2848   vcl_worker_t *wrk = vcl_worker_get_current ();
2849   int rv = VPPCOM_OK, add_evt = 0;
2850   vcl_session_t *vep_session;
2851   vcl_session_t *s;
2852
2853   if (vep_handle == session_handle)
2854     {
2855       VDBG (0, "vep_sh == session handle (%u)!", vep_handle);
2856       return VPPCOM_EINVAL;
2857     }
2858
2859   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2860   if (PREDICT_FALSE (!vep_session))
2861     {
2862       VDBG (0, "Invalid vep_sh (%u)!", vep_handle);
2863       return VPPCOM_EBADFD;
2864     }
2865   if (PREDICT_FALSE (!(vep_session->flags & VCL_SESSION_F_IS_VEP)))
2866     {
2867       VDBG (0, "vep_sh (%u) is not a vep!", vep_handle);
2868       return VPPCOM_EINVAL;
2869     }
2870
2871   ASSERT (vep_session->vep.vep_sh == ~0);
2872   ASSERT (vep_session->vep.prev_sh == ~0);
2873
2874   s = vcl_session_get_w_handle (wrk, session_handle);
2875   if (PREDICT_FALSE (!s))
2876     {
2877       VDBG (0, "Invalid session_handle (%u)!", session_handle);
2878       return VPPCOM_EBADFD;
2879     }
2880   if (PREDICT_FALSE (s->flags & VCL_SESSION_F_IS_VEP))
2881     {
2882       VDBG (0, "session_handle (%u) is a vep!", vep_handle);
2883       return VPPCOM_EINVAL;
2884     }
2885
2886   switch (op)
2887     {
2888     case EPOLL_CTL_ADD:
2889       if (PREDICT_FALSE (!event))
2890         {
2891           VDBG (0, "EPOLL_CTL_ADD: NULL pointer to epoll_event structure!");
2892           return VPPCOM_EINVAL;
2893         }
2894       if (s->flags & VCL_SESSION_F_IS_VEP_SESSION)
2895         {
2896           VDBG (0, "EPOLL_CTL_ADD: %u already epolled!", s->session_index);
2897           rv = VPPCOM_EEXIST;
2898           goto done;
2899         }
2900       if (vep_session->vep.next_sh != ~0)
2901         {
2902           vcl_session_t *next_session;
2903           next_session = vcl_session_get_w_handle (wrk,
2904                                                    vep_session->vep.next_sh);
2905           if (PREDICT_FALSE (!next_session))
2906             {
2907               VDBG (0, "EPOLL_CTL_ADD: Invalid vep.next_sh (%u) on "
2908                     "vep_idx (%u)!", vep_session->vep.next_sh, vep_handle);
2909               return VPPCOM_EBADFD;
2910             }
2911           ASSERT (next_session->vep.prev_sh == vep_handle);
2912           next_session->vep.prev_sh = session_handle;
2913         }
2914       s->vep.next_sh = vep_session->vep.next_sh;
2915       s->vep.prev_sh = vep_handle;
2916       s->vep.vep_sh = vep_handle;
2917       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
2918       s->vep.lt_next = VCL_INVALID_SESSION_INDEX;
2919       s->vep.ev = *event;
2920       s->vep.ev.events |= EPOLLHUP | EPOLLERR;
2921       s->flags &= ~VCL_SESSION_F_IS_VEP;
2922       s->flags |= VCL_SESSION_F_IS_VEP_SESSION;
2923       vep_session->vep.next_sh = session_handle;
2924
2925       if ((event->events & EPOLLOUT))
2926         {
2927           int write_ready = vcl_session_write_ready (s);
2928
2929           vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2930           if (write_ready > 0)
2931             {
2932               /* Generate EPOLLOUT if tx fifo not full */
2933               vcl_epoll_ctl_add_unhandled_event (
2934                 wrk, s, event->events & EPOLLET, SESSION_IO_EVT_TX);
2935               add_evt = 1;
2936             }
2937           else
2938             {
2939               vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF);
2940             }
2941         }
2942       /* Generate EPOLLIN if rx fifo has data */
2943       if ((event->events & EPOLLIN) && (vcl_session_read_ready (s) > 0))
2944         {
2945           vcl_epoll_ctl_add_unhandled_event (wrk, s, event->events & EPOLLET,
2946                                              SESSION_IO_EVT_RX);
2947           add_evt = 1;
2948         }
2949       if (!add_evt && vcl_session_is_closing (s))
2950         {
2951           session_event_t e = { 0 };
2952           if (s->session_state == VCL_STATE_VPP_CLOSING)
2953             e.event_type = SESSION_CTRL_EVT_DISCONNECTED;
2954           else
2955             e.event_type = SESSION_CTRL_EVT_RESET;
2956           e.session_index = s->session_index;
2957           e.postponed = 1;
2958           vec_add1 (wrk->unhandled_evts_vector, e);
2959         }
2960       VDBG (1, "EPOLL_CTL_ADD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2961             vep_handle, session_handle, event->events, event->data.u64);
2962       vcl_evt (VCL_EVT_EPOLL_CTLADD, s, event->events, event->data.u64);
2963       break;
2964
2965     case EPOLL_CTL_MOD:
2966       if (PREDICT_FALSE (!event))
2967         {
2968           VDBG (0, "EPOLL_CTL_MOD: NULL pointer to epoll_event structure!");
2969           rv = VPPCOM_EINVAL;
2970           goto done;
2971         }
2972       else if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
2973         {
2974           VDBG (0, "sh %u EPOLL_CTL_MOD: not a vep session!", session_handle);
2975           rv = VPPCOM_ENOENT;
2976           goto done;
2977         }
2978       else if (PREDICT_FALSE (s->vep.vep_sh != vep_handle))
2979         {
2980           VDBG (0, "EPOLL_CTL_MOD: sh %u vep_sh (%u) != vep_sh (%u)!",
2981                 session_handle, s->vep.vep_sh, vep_handle);
2982           rv = VPPCOM_EINVAL;
2983           goto done;
2984         }
2985
2986       /* Generate EPOLLOUT if session write ready and event was not on */
2987       if ((event->events & EPOLLOUT) && !(s->vep.ev.events & EPOLLOUT))
2988         {
2989           /* Fifo size load acq synchronized with update store rel */
2990           int write_ready = vcl_session_write_ready (s);
2991
2992           vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2993           if (write_ready > 0)
2994             vcl_epoll_ctl_add_unhandled_event (wrk, s, event->events & EPOLLET,
2995                                                SESSION_IO_EVT_TX);
2996           else
2997             /* Request deq ntf in case dequeue happened while updating flag */
2998             vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF);
2999         }
3000       else if (!(event->events & EPOLLOUT))
3001         vcl_session_del_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
3002
3003       /* Generate EPOLLIN if session read ready and event was not on */
3004       if ((event->events & EPOLLIN) && !(s->vep.ev.events & EPOLLIN) &&
3005           (vcl_session_read_ready (s) > 0))
3006         {
3007           vcl_epoll_ctl_add_unhandled_event (wrk, s, event->events & EPOLLET,
3008                                              SESSION_IO_EVT_RX);
3009         }
3010       s->vep.et_mask = VEP_DEFAULT_ET_MASK;
3011       s->vep.ev = *event;
3012       s->vep.ev.events |= EPOLLHUP | EPOLLERR;
3013
3014       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
3015             vep_handle, session_handle, event->events, event->data.u64);
3016       break;
3017
3018     case EPOLL_CTL_DEL:
3019       if (PREDICT_FALSE (!(s->flags & VCL_SESSION_F_IS_VEP_SESSION)))
3020         {
3021           VDBG (0, "EPOLL_CTL_DEL: %u not a vep session!", session_handle);
3022           rv = VPPCOM_ENOENT;
3023           goto done;
3024         }
3025       else if (PREDICT_FALSE (s->vep.vep_sh != vep_handle))
3026         {
3027           VDBG (0, "EPOLL_CTL_DEL: sh %u vep_sh (%u) != vep_sh (%u)!",
3028                 session_handle, s->vep.vep_sh, vep_handle);
3029           rv = VPPCOM_EINVAL;
3030           goto done;
3031         }
3032
3033       if (s->vep.prev_sh == vep_handle)
3034         vep_session->vep.next_sh = s->vep.next_sh;
3035       else
3036         {
3037           vcl_session_t *prev_session;
3038           prev_session = vcl_session_get_w_handle (wrk, s->vep.prev_sh);
3039           if (PREDICT_FALSE (!prev_session))
3040             {
3041               VDBG (0, "EPOLL_CTL_DEL: Invalid prev_sh (%u) on sh (%u)!",
3042                     s->vep.prev_sh, session_handle);
3043               return VPPCOM_EBADFD;
3044             }
3045           ASSERT (prev_session->vep.next_sh == session_handle);
3046           prev_session->vep.next_sh = s->vep.next_sh;
3047         }
3048       if (s->vep.next_sh != ~0)
3049         {
3050           vcl_session_t *next_session;
3051           next_session = vcl_session_get_w_handle (wrk, s->vep.next_sh);
3052           if (PREDICT_FALSE (!next_session))
3053             {
3054               VDBG (0, "EPOLL_CTL_DEL: Invalid next_sh (%u) on sh (%u)!",
3055                     s->vep.next_sh, session_handle);
3056               return VPPCOM_EBADFD;
3057             }
3058           ASSERT (next_session->vep.prev_sh == session_handle);
3059           next_session->vep.prev_sh = s->vep.prev_sh;
3060         }
3061
3062       if (s->vep.lt_next != VCL_INVALID_SESSION_INDEX)
3063         vcl_epoll_lt_del (wrk, s);
3064
3065       memset (&s->vep, 0, sizeof (s->vep));
3066       s->vep.next_sh = ~0;
3067       s->vep.prev_sh = ~0;
3068       s->vep.vep_sh = ~0;
3069       s->vep.lt_next = VCL_INVALID_SESSION_INDEX;
3070       s->flags &= ~VCL_SESSION_F_IS_VEP_SESSION;
3071
3072       if (vcl_session_is_open (s))
3073         vcl_session_del_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
3074
3075       VDBG (1, "EPOLL_CTL_DEL: vep_idx %u, sh %u!", vep_handle,
3076             session_handle);
3077       vcl_evt (VCL_EVT_EPOLL_CTLDEL, s, vep_sh);
3078       break;
3079
3080     default:
3081       VDBG (0, "Invalid operation (%d)!", op);
3082       rv = VPPCOM_EINVAL;
3083     }
3084
3085   vep_verify_epoll_chain (wrk, vep_handle);
3086
3087 done:
3088   return rv;
3089 }
3090
3091 always_inline u8
3092 vcl_ep_session_needs_evt (vcl_session_t *s, u32 evt)
3093 {
3094   /* No event if not epolled / events reset on hup or level-trigger on */
3095   return ((s->vep.ev.events & evt) &&
3096           s->vep.lt_next == VCL_INVALID_SESSION_INDEX);
3097 }
3098
3099 static inline void
3100 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
3101                                 struct epoll_event *events, u32 * num_ev)
3102 {
3103   session_disconnected_msg_t *disconnected_msg;
3104   session_connected_msg_t *connected_msg;
3105   u32 sid = ~0, session_events;
3106   u64 session_evt_data = ~0;
3107   vcl_session_t *s;
3108   u8 add_event = 0;
3109
3110   switch (e->event_type)
3111     {
3112     case SESSION_IO_EVT_RX:
3113       sid = e->session_index;
3114       s = vcl_session_get (wrk, sid);
3115       if (vcl_session_is_closed (s))
3116         break;
3117       vcl_fifo_rx_evt_valid_or_break (s);
3118       if (!vcl_ep_session_needs_evt (s, EPOLLIN) ||
3119           (s->flags & VCL_SESSION_F_HAS_RX_EVT))
3120         break;
3121       session_events = s->vep.ev.events;
3122       add_event = 1;
3123       events[*num_ev].events = EPOLLIN;
3124       session_evt_data = s->vep.ev.data.u64;
3125       s->flags |= VCL_SESSION_F_HAS_RX_EVT;
3126       break;
3127     case SESSION_IO_EVT_TX:
3128       sid = e->session_index;
3129       s = vcl_session_get (wrk, sid);
3130       if (!s || !vcl_session_is_open (s))
3131         break;
3132       svm_fifo_reset_has_deq_ntf (vcl_session_is_ct (s) ? s->ct_tx_fifo :
3133                                                                 s->tx_fifo);
3134       if (!vcl_ep_session_needs_evt (s, EPOLLOUT))
3135         break;
3136       session_events = s->vep.ev.events;
3137       add_event = 1;
3138       events[*num_ev].events = EPOLLOUT;
3139       session_evt_data = s->vep.ev.data.u64;
3140       break;
3141     case SESSION_CTRL_EVT_ACCEPTED:
3142       if (!e->postponed)
3143         s = vcl_session_accepted (wrk, (session_accepted_msg_t *) e->data);
3144       else
3145         s = vcl_session_get (wrk, e->session_index);
3146       if (!s || !vcl_ep_session_needs_evt (s, EPOLLIN))
3147         break;
3148       sid = s->session_index;
3149       session_events = s->vep.ev.events;
3150       add_event = 1;
3151       events[*num_ev].events = EPOLLIN;
3152       session_evt_data = s->vep.ev.data.u64;
3153       break;
3154     case SESSION_CTRL_EVT_CONNECTED:
3155       if (!e->postponed)
3156         {
3157           connected_msg = (session_connected_msg_t *) e->data;
3158           sid = vcl_session_connected_handler (wrk, connected_msg);
3159         }
3160       else
3161         sid = e->session_index;
3162       s = vcl_session_get (wrk, sid);
3163       if (vcl_session_is_closed (s) || !vcl_ep_session_needs_evt (s, EPOLLOUT))
3164         break;
3165       /* We didn't have a fifo when the event was added */
3166       vcl_session_add_want_deq_ntf (s, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
3167       add_event = 1;
3168       session_events = s->vep.ev.events;
3169       /* Generate EPOLLOUT because there's no connected event */
3170       events[*num_ev].events = EPOLLOUT;
3171       session_evt_data = s->vep.ev.data.u64;
3172       if (s->session_state == VCL_STATE_DETACHED)
3173         {
3174           events[*num_ev].events |= EPOLLHUP;
3175           s->vep.ev.events = 0;
3176         }
3177       break;
3178     case SESSION_CTRL_EVT_DISCONNECTED:
3179       if (!e->postponed)
3180         {
3181           disconnected_msg = (session_disconnected_msg_t *) e->data;
3182           s = vcl_session_disconnected_handler (wrk, disconnected_msg);
3183         }
3184       else
3185         {
3186           s = vcl_session_get (wrk, e->session_index);
3187           s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
3188         }
3189       if (vcl_session_is_closed (s) || !vcl_ep_session_needs_evt (s, EPOLLHUP))
3190         {
3191           if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
3192             vcl_session_free (wrk, s);
3193           break;
3194         }
3195       sid = s->session_index;
3196       session_events = s->vep.ev.events;
3197       add_event = 1;
3198       if (EPOLLRDHUP & session_events)
3199         {
3200           /* If app can distinguish between RDHUP and HUP,
3201            * we make finer control */
3202           events[*num_ev].events = EPOLLRDHUP;
3203           if (s->flags & VCL_SESSION_F_WR_SHUTDOWN)
3204             {
3205               events[*num_ev].events |= EPOLLHUP;
3206             }
3207         }
3208       else
3209         {
3210           events[*num_ev].events = EPOLLHUP;
3211         }
3212       session_evt_data = s->vep.ev.data.u64;
3213       s->vep.ev.events = 0;
3214       break;
3215     case SESSION_CTRL_EVT_BOUND:
3216       vcl_session_bound_handler (wrk, (session_bound_msg_t *) e->data);
3217       break;
3218     case SESSION_CTRL_EVT_RESET:
3219       if (!e->postponed)
3220         {
3221           sid =
3222             vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
3223           s = vcl_session_get (wrk, sid);
3224         }
3225       else
3226         {
3227           sid = e->session_index;
3228           s = vcl_session_get (wrk, sid);
3229           s->flags &= ~VCL_SESSION_F_PENDING_DISCONNECT;
3230         }
3231       if (vcl_session_is_closed (s) || !vcl_ep_session_needs_evt (s, EPOLLHUP))
3232         {
3233           if (s && (s->flags & VCL_SESSION_F_PENDING_FREE))
3234             vcl_session_free (wrk, s);
3235           break;
3236         }
3237       session_events = s->vep.ev.events;
3238       add_event = 1;
3239       events[*num_ev].events = EPOLLERR | EPOLLHUP;
3240       if ((EPOLLRDHUP & session_events) &&
3241           (s->flags & VCL_SESSION_F_RD_SHUTDOWN))
3242         {
3243           events[*num_ev].events |= EPOLLRDHUP;
3244         }
3245       if ((EPOLLIN & session_events) && (s->flags & VCL_SESSION_F_RD_SHUTDOWN))
3246         {
3247           events[*num_ev].events |= EPOLLIN;
3248         }
3249       session_evt_data = s->vep.ev.data.u64;
3250       s->vep.ev.events = 0;
3251       break;
3252     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
3253       vcl_session_unlisten_reply_handler (wrk, e->data);
3254       break;
3255     case SESSION_CTRL_EVT_MIGRATED:
3256       vcl_session_migrated_handler (wrk, e->data);
3257       break;
3258     case SESSION_CTRL_EVT_CLEANUP:
3259       vcl_session_cleanup_handler (wrk, e->data);
3260       break;
3261     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
3262       vcl_session_req_worker_update_handler (wrk, e->data);
3263       break;
3264     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
3265       vcl_session_worker_update_reply_handler (wrk, e->data);
3266       break;
3267     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
3268       vcl_session_app_add_segment_handler (wrk, e->data);
3269       break;
3270     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
3271       vcl_session_app_del_segment_handler (wrk, e->data);
3272       break;
3273     case SESSION_CTRL_EVT_APP_WRK_RPC:
3274       vcl_worker_rpc_handler (wrk, e->data);
3275       break;
3276     default:
3277       VDBG (0, "unhandled: %u", e->event_type);
3278       break;
3279     }
3280
3281   if (add_event)
3282     {
3283       ASSERT (s->flags & VCL_SESSION_F_IS_VEP_SESSION);
3284       events[*num_ev].data.u64 = session_evt_data;
3285       if (EPOLLONESHOT & session_events)
3286         {
3287           s = vcl_session_get (wrk, sid);
3288           if (!(events[*num_ev].events & EPOLLHUP))
3289             s->vep.ev.events = EPOLLHUP | EPOLLERR;
3290         }
3291       else if (!(EPOLLET & session_events))
3292         {
3293           s = vcl_session_get (wrk, sid);
3294           if (s->vep.lt_next == VCL_INVALID_SESSION_INDEX)
3295             vcl_epoll_lt_add (wrk, s);
3296         }
3297       *num_ev += 1;
3298     }
3299 }
3300
3301 static int
3302 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
3303                           struct epoll_event *events, u32 maxevents,
3304                           double wait_for_time, u32 * num_ev)
3305 {
3306   svm_msg_q_msg_t *msg;
3307   session_event_t *e;
3308   int i;
3309
3310   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
3311     goto handle_dequeued;
3312
3313   if (svm_msg_q_is_empty (mq))
3314     {
3315       if (!wait_for_time)
3316         return 0;
3317       else if (wait_for_time < 0)
3318         svm_msg_q_wait (mq, SVM_MQ_WAIT_EMPTY);
3319       else
3320         {
3321           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
3322             return 0;
3323         }
3324     }
3325   ASSERT (maxevents > *num_ev);
3326   vcl_mq_dequeue_batch (wrk, mq, ~0);
3327
3328 handle_dequeued:
3329   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
3330     {
3331       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
3332       e = svm_msg_q_msg_data (mq, msg);
3333       if (*num_ev < maxevents)
3334         vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
3335       else
3336         vcl_handle_mq_event (wrk, e);
3337       svm_msg_q_free_msg (mq, msg);
3338     }
3339   vec_reset_length (wrk->mq_msg_vector);
3340   vcl_handle_pending_wrk_updates (wrk);
3341   return *num_ev;
3342 }
3343
3344 static int
3345 vppcom_epoll_wait_condvar (vcl_worker_t *wrk, struct epoll_event *events,
3346                            int maxevents, u32 n_evts, double timeout_ms)
3347 {
3348   double end = -1;
3349
3350   if (!n_evts)
3351     {
3352       if (timeout_ms > 0)
3353         end = clib_time_now (&wrk->clib_time) + (timeout_ms / 1e3);
3354     }
3355
3356   do
3357     {
3358       vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events, maxevents,
3359                                 timeout_ms, &n_evts);
3360       if (n_evts || !timeout_ms)
3361         return n_evts;
3362     }
3363   while (end == -1 || clib_time_now (&wrk->clib_time) < end);
3364
3365   return 0;
3366 }
3367
3368 static int
3369 vppcom_epoll_wait_eventfd (vcl_worker_t *wrk, struct epoll_event *events,
3370                            int maxevents, u32 n_evts, double timeout_ms)
3371 {
3372   int __clib_unused n_read;
3373   vcl_mq_evt_conn_t *mqc;
3374   int n_mq_evts, i;
3375   double end = -1;
3376   u64 buf;
3377
3378   if (PREDICT_FALSE (wrk->api_client_handle == ~0))
3379     {
3380       vcl_api_retry_attach (wrk);
3381       return n_evts;
3382     }
3383
3384   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
3385   if (!n_evts)
3386     {
3387       if (timeout_ms > 0)
3388         end = clib_time_now (&wrk->clib_time) + (timeout_ms / 1e3);
3389     }
3390
3391   do
3392     {
3393       n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
3394                               vec_len (wrk->mq_events), timeout_ms);
3395       if (n_mq_evts < 0)
3396         {
3397           VDBG (0, "epoll_wait error %u", errno);
3398           return n_evts;
3399         }
3400
3401       for (i = 0; i < n_mq_evts; i++)
3402         {
3403           if (PREDICT_FALSE (wrk->mq_events[i].data.u32 == ~0))
3404             {
3405               /* api socket was closed */
3406               vcl_api_handle_disconnect (wrk);
3407               continue;
3408             }
3409
3410           mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
3411           n_read = read (mqc->mq_fd, &buf, sizeof (buf));
3412           vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0,
3413                                     &n_evts);
3414         }
3415
3416       if (n_evts || !timeout_ms)
3417         return n_evts;
3418     }
3419   while (end == -1 || clib_time_now (&wrk->clib_time) < end);
3420
3421   return 0;
3422 }
3423
3424 static void
3425 vcl_epoll_wait_handle_lt (vcl_worker_t *wrk, struct epoll_event *events,
3426                           int maxevents, u32 *n_evts)
3427 {
3428   u32 add_event = 0, evt_flags = 0, next, *to_remove = 0, *si;
3429   vcl_session_t *s;
3430   u64 evt_data;
3431   int rv;
3432
3433   ASSERT (wrk->ep_lt_current != VCL_INVALID_SESSION_INDEX);
3434   if (*n_evts >= maxevents)
3435     return;
3436
3437   next = wrk->ep_lt_current;
3438   do
3439     {
3440       s = vcl_session_get (wrk, next);
3441       next = s->vep.lt_next;
3442
3443       if (s->vep.ev.events == 0)
3444         {
3445           vec_add1 (to_remove, s->session_index);
3446           continue;
3447         }
3448       if ((s->vep.ev.events & EPOLLIN) && (rv = vcl_session_read_ready (s)))
3449         {
3450           add_event = 1;
3451           evt_flags |= rv > 0 ? EPOLLIN : EPOLLHUP | EPOLLRDHUP;
3452           evt_data = s->vep.ev.data.u64;
3453         }
3454       if ((s->vep.ev.events & EPOLLOUT) && (rv = vcl_session_write_ready (s)))
3455         {
3456           add_event = 1;
3457           evt_flags |= rv > 0 ? EPOLLOUT : EPOLLHUP | EPOLLRDHUP;
3458           evt_data = s->vep.ev.data.u64;
3459         }
3460       if (!add_event && s->session_state > VCL_STATE_READY)
3461         {
3462           add_event = 1;
3463           evt_flags |= EPOLLHUP | EPOLLRDHUP;
3464           evt_data = s->vep.ev.data.u64;
3465         }
3466       if (add_event)
3467         {
3468           events[*n_evts].events = evt_flags;
3469           events[*n_evts].data.u64 = evt_data;
3470           if (EPOLLONESHOT & s->vep.ev.events)
3471             s->vep.ev.events = EPOLLHUP | EPOLLERR;
3472           if (evt_flags & EPOLLHUP)
3473             s->vep.ev.events = 0;
3474           *n_evts += 1;
3475           add_event = 0;
3476           evt_flags = 0;
3477           if (*n_evts == maxevents)
3478             {
3479               wrk->ep_lt_current = next;
3480               break;
3481             }
3482         }
3483       else
3484         {
3485           vec_add1 (to_remove, s->session_index);
3486         }
3487     }
3488   while (next != wrk->ep_lt_current);
3489
3490   vec_foreach (si, to_remove)
3491     {
3492       s = vcl_session_get (wrk, *si);
3493       vcl_epoll_lt_del (wrk, s);
3494     }
3495   vec_free (to_remove);
3496 }
3497
3498 int
3499 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
3500                    int maxevents, double wait_for_time)
3501 {
3502   vcl_worker_t *wrk = vcl_worker_get_current ();
3503   vcl_session_t *vep_session;
3504   u32 n_evts = 0;
3505   int i;
3506
3507   if (PREDICT_FALSE (maxevents <= 0))
3508     {
3509       VDBG (0, "ERROR: Invalid maxevents (%d)!", maxevents);
3510       return VPPCOM_EINVAL;
3511     }
3512
3513   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
3514   if (!vep_session)
3515     return VPPCOM_EBADFD;
3516
3517   if (PREDICT_FALSE (!(vep_session->flags & VCL_SESSION_F_IS_VEP)))
3518     {
3519       VDBG (0, "ERROR: vep_idx (%u) is not a vep!", vep_handle);
3520       return VPPCOM_EINVAL;
3521     }
3522
3523   if (vec_len (wrk->unhandled_evts_vector))
3524     {
3525       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
3526         {
3527           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
3528                                           events, &n_evts);
3529           if (n_evts == maxevents)
3530             {
3531               vec_delete (wrk->unhandled_evts_vector, i + 1, 0);
3532               return n_evts;
3533             }
3534         }
3535       vec_reset_length (wrk->unhandled_evts_vector);
3536     }
3537
3538   if (PREDICT_FALSE (wrk->ep_lt_current != VCL_INVALID_SESSION_INDEX))
3539     vcl_epoll_wait_handle_lt (wrk, events, maxevents, &n_evts);
3540
3541   /* Request to only drain unhandled */
3542   if ((int) wait_for_time == -2)
3543     return n_evts;
3544
3545
3546   if (vcm->cfg.use_mq_eventfd)
3547     n_evts = vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
3548                                         wait_for_time);
3549   else
3550     n_evts = vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
3551                                         wait_for_time);
3552
3553   return n_evts;
3554 }
3555
3556 int
3557 vppcom_session_attr (uint32_t session_handle, uint32_t op,
3558                      void *buffer, uint32_t * buflen)
3559 {
3560   vcl_worker_t *wrk = vcl_worker_get_current ();
3561   u32 *flags = buffer;
3562   vppcom_endpt_t *ep = buffer;
3563   transport_endpt_attr_t tea;
3564   vcl_session_t *session;
3565   int rv = VPPCOM_OK;
3566
3567   session = vcl_session_get_w_handle (wrk, session_handle);
3568   if (!session)
3569     return VPPCOM_EBADFD;
3570
3571   switch (op)
3572     {
3573     case VPPCOM_ATTR_GET_NREAD:
3574       rv = vcl_session_read_ready (session);
3575       VDBG (2, "VPPCOM_ATTR_GET_NREAD: sh %u, nread = %d", session_handle,
3576             rv);
3577       break;
3578
3579     case VPPCOM_ATTR_GET_NWRITE:
3580       rv = vcl_session_write_ready (session);
3581       VDBG (2, "VPPCOM_ATTR_GET_NWRITE: sh %u, nwrite = %d", session_handle,
3582             rv);
3583       break;
3584
3585     case VPPCOM_ATTR_GET_FLAGS:
3586       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
3587         {
3588           *flags =
3589             O_RDWR |
3590             (vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK) ?
3591              O_NONBLOCK : 0);
3592           *buflen = sizeof (*flags);
3593           VDBG (2, "VPPCOM_ATTR_GET_FLAGS: sh %u, flags = 0x%08x, "
3594                 "is_nonblocking = %u", session_handle, *flags,
3595                 vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK));
3596         }
3597       else
3598         rv = VPPCOM_EINVAL;
3599       break;
3600
3601     case VPPCOM_ATTR_SET_FLAGS:
3602       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
3603         {
3604           if (*flags & O_NONBLOCK)
3605             vcl_session_set_attr (session, VCL_SESS_ATTR_NONBLOCK);
3606           else
3607             vcl_session_clear_attr (session, VCL_SESS_ATTR_NONBLOCK);
3608
3609           VDBG (2, "VPPCOM_ATTR_SET_FLAGS: sh %u, flags = 0x%08x,"
3610                 " is_nonblocking = %u", session_handle, *flags,
3611                 vcl_session_has_attr (session, VCL_SESS_ATTR_NONBLOCK));
3612         }
3613       else
3614         rv = VPPCOM_EINVAL;
3615       break;
3616
3617     case VPPCOM_ATTR_GET_PEER_ADDR:
3618       if (PREDICT_TRUE (buffer && buflen &&
3619                         (*buflen >= sizeof (*ep)) && ep->ip))
3620         {
3621           ep->is_ip4 = session->transport.is_ip4;
3622           ep->port = session->transport.rmt_port;
3623           if (session->transport.is_ip4)
3624             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3625                               sizeof (ip4_address_t));
3626           else
3627             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3628                               sizeof (ip6_address_t));
3629           *buflen = sizeof (*ep);
3630           VDBG (1,
3631                 "VPPCOM_ATTR_GET_PEER_ADDR: sh %u, is_ip4 = %u, "
3632                 "addr = %U, port %u",
3633                 session_handle, ep->is_ip4, vcl_format_ip46_address,
3634                 &session->transport.rmt_ip,
3635                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3636                 clib_net_to_host_u16 (ep->port));
3637         }
3638       else
3639         rv = VPPCOM_EINVAL;
3640       break;
3641
3642     case VPPCOM_ATTR_GET_LCL_ADDR:
3643       if (PREDICT_TRUE (buffer && buflen &&
3644                         (*buflen >= sizeof (*ep)) && ep->ip))
3645         {
3646           ep->is_ip4 = session->transport.is_ip4;
3647           ep->port = session->transport.lcl_port;
3648           if (session->transport.is_ip4)
3649             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
3650                               sizeof (ip4_address_t));
3651           else
3652             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
3653                               sizeof (ip6_address_t));
3654           *buflen = sizeof (*ep);
3655           VDBG (1,
3656                 "VPPCOM_ATTR_GET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3657                 " port %d",
3658                 session_handle, ep->is_ip4, vcl_format_ip46_address,
3659                 &session->transport.lcl_ip,
3660                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3661                 clib_net_to_host_u16 (ep->port));
3662         }
3663       else
3664         rv = VPPCOM_EINVAL;
3665       break;
3666
3667     case VPPCOM_ATTR_GET_ORIGINAL_DST:
3668       if (!session->transport.is_ip4)
3669         {
3670           /* now original dst only support ipv4*/
3671           rv = VPPCOM_EAFNOSUPPORT;
3672           break;
3673         }
3674       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*ep)) &&
3675                         ep->ip))
3676         {
3677           ep->is_ip4 = session->transport.is_ip4;
3678           ep->port = session->original_dst_port;
3679           clib_memcpy_fast (ep->ip, &session->original_dst_ip4,
3680                             sizeof (ip4_address_t));
3681           *buflen = sizeof (*ep);
3682           VDBG (1,
3683                 "VPPCOM_ATTR_GET_ORIGINAL_DST: sh %u, is_ip4 = %u, addr = %U"
3684                 " port %d",
3685                 session_handle, ep->is_ip4, vcl_format_ip4_address,
3686                 (ip4_address_t *) (&session->original_dst_ip4),
3687                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3688                 clib_net_to_host_u16 (ep->port));
3689         }
3690       else
3691         rv = VPPCOM_EINVAL;
3692       break;
3693
3694     case VPPCOM_ATTR_SET_LCL_ADDR:
3695       if (PREDICT_TRUE (buffer && buflen &&
3696                         (*buflen >= sizeof (*ep)) && ep->ip))
3697         {
3698           session->transport.is_ip4 = ep->is_ip4;
3699           session->transport.lcl_port = ep->port;
3700           vcl_ip_copy_from_ep (&session->transport.lcl_ip, ep);
3701           *buflen = sizeof (*ep);
3702           VDBG (1,
3703                 "VPPCOM_ATTR_SET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3704                 " port %d",
3705                 session_handle, ep->is_ip4, vcl_format_ip46_address,
3706                 &session->transport.lcl_ip,
3707                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3708                 clib_net_to_host_u16 (ep->port));
3709         }
3710       else
3711         rv = VPPCOM_EINVAL;
3712       break;
3713
3714     case VPPCOM_ATTR_GET_LIBC_EPFD:
3715       rv = session->libc_epfd;
3716       VDBG (2, "VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d", rv);
3717       break;
3718
3719     case VPPCOM_ATTR_SET_LIBC_EPFD:
3720       if (PREDICT_TRUE (buffer && buflen &&
3721                         (*buflen == sizeof (session->libc_epfd))))
3722         {
3723           session->libc_epfd = *(int *) buffer;
3724           *buflen = sizeof (session->libc_epfd);
3725
3726           VDBG (2, "VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, buflen %d",
3727                 session->libc_epfd, *buflen);
3728         }
3729       else
3730         rv = VPPCOM_EINVAL;
3731       break;
3732
3733     case VPPCOM_ATTR_GET_PROTOCOL:
3734       if (buffer && buflen && (*buflen >= sizeof (int)))
3735         {
3736           *(int *) buffer = session->session_type;
3737           *buflen = sizeof (int);
3738
3739           VDBG (2, "VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
3740                 *(int *) buffer, *(int *) buffer ? "UDP" : "TCP", *buflen);
3741         }
3742       else
3743         rv = VPPCOM_EINVAL;
3744       break;
3745
3746     case VPPCOM_ATTR_GET_LISTEN:
3747       if (buffer && buflen && (*buflen >= sizeof (int)))
3748         {
3749           *(int *) buffer = vcl_session_has_attr (session,
3750                                                   VCL_SESS_ATTR_LISTEN);
3751           *buflen = sizeof (int);
3752
3753           VDBG (2, "VPPCOM_ATTR_GET_LISTEN: %d, buflen %d", *(int *) buffer,
3754                 *buflen);
3755         }
3756       else
3757         rv = VPPCOM_EINVAL;
3758       break;
3759
3760     case VPPCOM_ATTR_GET_ERROR:
3761       if (buffer && buflen && (*buflen >= sizeof (int)))
3762         {
3763           *(int *) buffer = 0;
3764           *buflen = sizeof (int);
3765
3766           VDBG (2, "VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
3767                 *(int *) buffer, *buflen);
3768         }
3769       else
3770         rv = VPPCOM_EINVAL;
3771       break;
3772
3773     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
3774       if (buffer && buflen && (*buflen >= sizeof (u32)))
3775         {
3776
3777           /* VPP-TBD */
3778           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
3779                                 session->tx_fifo ?
3780                                 svm_fifo_size (session->tx_fifo) :
3781                                 vcm->cfg.tx_fifo_size);
3782           *buflen = sizeof (u32);
3783
3784           VDBG (2, "VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3785                 " #VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer,
3786                 *buflen);
3787         }
3788       else
3789         rv = VPPCOM_EINVAL;
3790       break;
3791
3792     case VPPCOM_ATTR_SET_DSCP:
3793       if (buffer && buflen && (*buflen >= sizeof (u8)))
3794         {
3795           session->dscp = *(u8 *) buffer;
3796
3797           VDBG (2, "VPPCOM_ATTR_SET_DSCP: %u (0x%x), buflen %d,",
3798                 *(u8 *) buffer, *(u8 *) buffer, *buflen);
3799         }
3800       else
3801         rv = VPPCOM_EINVAL;
3802       break;
3803
3804     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
3805       if (buffer && buflen && (*buflen == sizeof (u32)))
3806         {
3807           /* VPP-TBD */
3808           session->sndbuf_size = *(u32 *) buffer;
3809           VDBG (2, "VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3810                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3811                 *buflen);
3812         }
3813       else
3814         rv = VPPCOM_EINVAL;
3815       break;
3816
3817     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
3818       if (buffer && buflen && (*buflen >= sizeof (u32)))
3819         {
3820
3821           /* VPP-TBD */
3822           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
3823                                 session->rx_fifo ?
3824                                 svm_fifo_size (session->rx_fifo) :
3825                                 vcm->cfg.rx_fifo_size);
3826           *buflen = sizeof (u32);
3827
3828           VDBG (2, "VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), buflen %d, "
3829                 "#VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer, *buflen);
3830         }
3831       else
3832         rv = VPPCOM_EINVAL;
3833       break;
3834
3835     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
3836       if (buffer && buflen && (*buflen == sizeof (u32)))
3837         {
3838           /* VPP-TBD */
3839           session->rcvbuf_size = *(u32 *) buffer;
3840           VDBG (2, "VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), buflen %d,"
3841                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3842                 *buflen);
3843         }
3844       else
3845         rv = VPPCOM_EINVAL;
3846       break;
3847
3848     case VPPCOM_ATTR_GET_REUSEADDR:
3849       if (buffer && buflen && (*buflen >= sizeof (int)))
3850         {
3851           /* VPP-TBD */
3852           *(int *) buffer = vcl_session_has_attr (session,
3853                                                   VCL_SESS_ATTR_REUSEADDR);
3854           *buflen = sizeof (int);
3855
3856           VDBG (2, "VPPCOM_ATTR_GET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3857                 *(int *) buffer, *buflen);
3858         }
3859       else
3860         rv = VPPCOM_EINVAL;
3861       break;
3862
3863     case VPPCOM_ATTR_SET_REUSEADDR:
3864       if (buffer && buflen && (*buflen == sizeof (int)) &&
3865           !vcl_session_has_attr (session, VCL_SESS_ATTR_LISTEN))
3866         {
3867           /* VPP-TBD */
3868           if (*(int *) buffer)
3869             vcl_session_set_attr (session, VCL_SESS_ATTR_REUSEADDR);
3870           else
3871             vcl_session_clear_attr (session, VCL_SESS_ATTR_REUSEADDR);
3872
3873           VDBG (2, "VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3874                 vcl_session_has_attr (session, VCL_SESS_ATTR_REUSEADDR),
3875                 *buflen);
3876         }
3877       else
3878         rv = VPPCOM_EINVAL;
3879       break;
3880
3881     case VPPCOM_ATTR_GET_REUSEPORT:
3882       if (buffer && buflen && (*buflen >= sizeof (int)))
3883         {
3884           /* VPP-TBD */
3885           *(int *) buffer = vcl_session_has_attr (session,
3886                                                   VCL_SESS_ATTR_REUSEPORT);
3887           *buflen = sizeof (int);
3888
3889           VDBG (2, "VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3890                 *(int *) buffer, *buflen);
3891         }
3892       else
3893         rv = VPPCOM_EINVAL;
3894       break;
3895
3896     case VPPCOM_ATTR_SET_REUSEPORT:
3897       if (buffer && buflen && (*buflen == sizeof (int)) &&
3898           !vcl_session_has_attr (session, VCL_SESS_ATTR_LISTEN))
3899         {
3900           /* VPP-TBD */
3901           if (*(int *) buffer)
3902             vcl_session_set_attr (session, VCL_SESS_ATTR_REUSEPORT);
3903           else
3904             vcl_session_clear_attr (session, VCL_SESS_ATTR_REUSEPORT);
3905
3906           VDBG (2, "VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3907                 vcl_session_has_attr (session, VCL_SESS_ATTR_REUSEPORT),
3908                 *buflen);
3909         }
3910       else
3911         rv = VPPCOM_EINVAL;
3912       break;
3913
3914     case VPPCOM_ATTR_GET_BROADCAST:
3915       if (buffer && buflen && (*buflen >= sizeof (int)))
3916         {
3917           /* VPP-TBD */
3918           *(int *) buffer = vcl_session_has_attr (session,
3919                                                   VCL_SESS_ATTR_BROADCAST);
3920           *buflen = sizeof (int);
3921
3922           VDBG (2, "VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3923                 *(int *) buffer, *buflen);
3924         }
3925       else
3926         rv = VPPCOM_EINVAL;
3927       break;
3928
3929     case VPPCOM_ATTR_SET_BROADCAST:
3930       if (buffer && buflen && (*buflen == sizeof (int)))
3931         {
3932           /* VPP-TBD */
3933           if (*(int *) buffer)
3934             vcl_session_set_attr (session, VCL_SESS_ATTR_BROADCAST);
3935           else
3936             vcl_session_clear_attr (session, VCL_SESS_ATTR_BROADCAST);
3937
3938           VDBG (2, "VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3939                 vcl_session_has_attr (session, VCL_SESS_ATTR_BROADCAST),
3940                 *buflen);
3941         }
3942       else
3943         rv = VPPCOM_EINVAL;
3944       break;
3945
3946     case VPPCOM_ATTR_GET_V6ONLY:
3947       if (buffer && buflen && (*buflen >= sizeof (int)))
3948         {
3949           /* VPP-TBD */
3950           *(int *) buffer = vcl_session_has_attr (session,
3951                                                   VCL_SESS_ATTR_V6ONLY);
3952           *buflen = sizeof (int);
3953
3954           VDBG (2, "VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3955                 *(int *) buffer, *buflen);
3956         }
3957       else
3958         rv = VPPCOM_EINVAL;
3959       break;
3960
3961     case VPPCOM_ATTR_SET_V6ONLY:
3962       if (buffer && buflen && (*buflen == sizeof (int)))
3963         {
3964           /* VPP-TBD */
3965           if (*(int *) buffer)
3966             vcl_session_set_attr (session, VCL_SESS_ATTR_V6ONLY);
3967           else
3968             vcl_session_clear_attr (session, VCL_SESS_ATTR_V6ONLY);
3969
3970           VDBG (2, "VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3971                 vcl_session_has_attr (session, VCL_SESS_ATTR_V6ONLY),
3972                 *buflen);
3973         }
3974       else
3975         rv = VPPCOM_EINVAL;
3976       break;
3977
3978     case VPPCOM_ATTR_GET_KEEPALIVE:
3979       if (buffer && buflen && (*buflen >= sizeof (int)))
3980         {
3981           /* VPP-TBD */
3982           *(int *) buffer = vcl_session_has_attr (session,
3983                                                   VCL_SESS_ATTR_KEEPALIVE);
3984           *buflen = sizeof (int);
3985
3986           VDBG (2, "VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3987                 *(int *) buffer, *buflen);
3988         }
3989       else
3990         rv = VPPCOM_EINVAL;
3991       break;
3992
3993     case VPPCOM_ATTR_SET_KEEPALIVE:
3994       if (buffer && buflen && (*buflen == sizeof (int)))
3995         {
3996           /* VPP-TBD */
3997           if (*(int *) buffer)
3998             vcl_session_set_attr (session, VCL_SESS_ATTR_KEEPALIVE);
3999           else
4000             vcl_session_clear_attr (session, VCL_SESS_ATTR_KEEPALIVE);
4001
4002           VDBG (2, "VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
4003                 vcl_session_has_attr (session, VCL_SESS_ATTR_KEEPALIVE),
4004                 *buflen);
4005         }
4006       else
4007         rv = VPPCOM_EINVAL;
4008       break;
4009
4010     case VPPCOM_ATTR_GET_TCP_NODELAY:
4011       if (buffer && buflen && (*buflen >= sizeof (int)))
4012         {
4013           /* VPP-TBD */
4014           *(int *) buffer = vcl_session_has_attr (session,
4015                                                   VCL_SESS_ATTR_TCP_NODELAY);
4016           *buflen = sizeof (int);
4017
4018           VDBG (2, "VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
4019                 *(int *) buffer, *buflen);
4020         }
4021       else
4022         rv = VPPCOM_EINVAL;
4023       break;
4024
4025     case VPPCOM_ATTR_SET_TCP_NODELAY:
4026       if (buffer && buflen && (*buflen == sizeof (int)))
4027         {
4028           /* VPP-TBD */
4029           if (*(int *) buffer)
4030             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_NODELAY);
4031           else
4032             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_NODELAY);
4033
4034           VDBG (2, "VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
4035                 vcl_session_has_attr (session, VCL_SESS_ATTR_TCP_NODELAY),
4036                 *buflen);
4037         }
4038       else
4039         rv = VPPCOM_EINVAL;
4040       break;
4041
4042     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
4043       if (buffer && buflen && (*buflen >= sizeof (int)))
4044         {
4045           /* VPP-TBD */
4046           *(int *) buffer = vcl_session_has_attr (session,
4047                                                   VCL_SESS_ATTR_TCP_KEEPIDLE);
4048           *buflen = sizeof (int);
4049
4050           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
4051                 *(int *) buffer, *buflen);
4052         }
4053       else
4054         rv = VPPCOM_EINVAL;
4055       break;
4056
4057     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
4058       if (buffer && buflen && (*buflen == sizeof (int)))
4059         {
4060           /* VPP-TBD */
4061           if (*(int *) buffer)
4062             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_KEEPIDLE);
4063           else
4064             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_KEEPIDLE);
4065
4066           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
4067                 vcl_session_has_attr (session,
4068                                       VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
4069         }
4070       else
4071         rv = VPPCOM_EINVAL;
4072       break;
4073
4074     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
4075       if (buffer && buflen && (*buflen >= sizeof (int)))
4076         {
4077           /* VPP-TBD */
4078           *(int *) buffer = vcl_session_has_attr (session,
4079                                                   VCL_SESS_ATTR_TCP_KEEPINTVL);
4080           *buflen = sizeof (int);
4081
4082           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
4083                 *(int *) buffer, *buflen);
4084         }
4085       else
4086         rv = VPPCOM_EINVAL;
4087       break;
4088
4089     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
4090       if (buffer && buflen && (*buflen == sizeof (int)))
4091         {
4092           /* VPP-TBD */
4093           if (*(int *) buffer)
4094             vcl_session_set_attr (session, VCL_SESS_ATTR_TCP_KEEPINTVL);
4095           else
4096             vcl_session_clear_attr (session, VCL_SESS_ATTR_TCP_KEEPINTVL);
4097
4098           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
4099                 vcl_session_has_attr (session,
4100                                       VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
4101         }
4102       else
4103         rv = VPPCOM_EINVAL;
4104       break;
4105
4106     case VPPCOM_ATTR_GET_TCP_USER_MSS:
4107       if (!(buffer && buflen && (*buflen >= sizeof (u32))))
4108         {
4109           rv = VPPCOM_EINVAL;
4110           break;
4111         }
4112
4113       tea.type = TRANSPORT_ENDPT_ATTR_MSS;
4114       tea.mss = *(u32 *) buffer;
4115       if (vcl_session_transport_attr (wrk, session, 1 /* is_get */, &tea))
4116         rv = VPPCOM_ENOPROTOOPT;
4117
4118       if (!rv)
4119         {
4120           *(u32 *) buffer = tea.mss;
4121           *buflen = sizeof (int);
4122         }
4123
4124       VDBG (2, "VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d", *(int *) buffer,
4125             *buflen);
4126       break;
4127     case VPPCOM_ATTR_SET_TCP_USER_MSS:
4128       if (!(buffer && buflen && (*buflen == sizeof (u32))))
4129         {
4130           rv = VPPCOM_EINVAL;
4131           break;
4132         }
4133
4134       tea.type = TRANSPORT_ENDPT_ATTR_MSS;
4135       tea.mss = *(u32 *) buffer;
4136       if (vcl_session_transport_attr (wrk, session, 0 /* is_get */, &tea))
4137         rv = VPPCOM_ENOPROTOOPT;
4138
4139       VDBG (2, "VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d", tea.mss,
4140             *buflen);
4141       break;
4142
4143     case VPPCOM_ATTR_SET_CONNECTED:
4144       session->flags |= VCL_SESSION_F_CONNECTED;
4145       break;
4146
4147     case VPPCOM_ATTR_SET_CKPAIR:
4148       if (!(buffer && buflen && (*buflen == sizeof (int))) ||
4149           !vcl_session_has_crypto (session))
4150         {
4151           rv = VPPCOM_EINVAL;
4152           break;
4153         }
4154       if (!session->ext_config)
4155         {
4156           vcl_session_alloc_ext_cfg (session, TRANSPORT_ENDPT_EXT_CFG_CRYPTO,
4157                                      sizeof (transport_endpt_ext_cfg_t));
4158         }
4159       else if (session->ext_config->type != TRANSPORT_ENDPT_EXT_CFG_CRYPTO)
4160         {
4161           rv = VPPCOM_EINVAL;
4162           break;
4163         }
4164
4165       session->ext_config->crypto.ckpair_index = *(uint32_t *) buffer;
4166       break;
4167
4168     case VPPCOM_ATTR_SET_VRF:
4169       if (!(buffer && buflen && (*buflen == sizeof (u32))))
4170         {
4171           rv = VPPCOM_EINVAL;
4172           break;
4173         }
4174       session->vrf = *(u32 *) buffer;
4175       break;
4176
4177     case VPPCOM_ATTR_GET_VRF:
4178       if (!(buffer && buflen && (*buflen >= sizeof (u32))))
4179         {
4180           rv = VPPCOM_EINVAL;
4181           break;
4182         }
4183       *(u32 *) buffer = session->vrf;
4184       *buflen = sizeof (u32);
4185       break;
4186
4187     case VPPCOM_ATTR_GET_DOMAIN:
4188       if (!(buffer && buflen && (*buflen >= sizeof (int))))
4189         {
4190           rv = VPPCOM_EINVAL;
4191           break;
4192         }
4193
4194       if (session->transport.is_ip4)
4195         *(int *) buffer = AF_INET;
4196       else
4197         *(int *) buffer = AF_INET6;
4198       *buflen = sizeof (int);
4199
4200       VDBG (2, "VPPCOM_ATTR_GET_DOMAIN: %d, buflen %u", *(int *) buffer,
4201             *buflen);
4202       break;
4203
4204     case VPPCOM_ATTR_SET_ENDPT_EXT_CFG:
4205       if (!(buffer && buflen && (*buflen > 0)))
4206         {
4207           rv = VPPCOM_EINVAL;
4208           break;
4209         }
4210       if (session->ext_config)
4211         {
4212           rv = VPPCOM_EINVAL;
4213           break;
4214         }
4215       vcl_session_alloc_ext_cfg (session, TRANSPORT_ENDPT_EXT_CFG_NONE,
4216                                  *buflen + sizeof (u32));
4217       clib_memcpy (session->ext_config->data, buffer, *buflen);
4218       session->ext_config->len = *buflen;
4219       break;
4220     case VPPCOM_ATTR_SET_IP_PKTINFO:
4221       if (buffer && buflen && (*buflen == sizeof (int)) &&
4222           !vcl_session_has_attr (session, VCL_SESS_ATTR_IP_PKTINFO))
4223         {
4224           if (*(int *) buffer)
4225             vcl_session_set_attr (session, VCL_SESS_ATTR_IP_PKTINFO);
4226           else
4227             vcl_session_clear_attr (session, VCL_SESS_ATTR_IP_PKTINFO);
4228
4229           VDBG (2, "VCL_SESS_ATTR_IP_PKTINFO: %d, buflen %d",
4230                 vcl_session_has_attr (session, VCL_SESS_ATTR_IP_PKTINFO),
4231                 *buflen);
4232         }
4233       else
4234         rv = VPPCOM_EINVAL;
4235       break;
4236
4237     case VPPCOM_ATTR_GET_IP_PKTINFO:
4238       if (buffer && buflen && (*buflen >= sizeof (int)))
4239         {
4240           *(int *) buffer =
4241             vcl_session_has_attr (session, VCL_SESS_ATTR_IP_PKTINFO);
4242           *buflen = sizeof (int);
4243
4244           VDBG (2, "VCL_SESS_ATTR_IP_PKTINFO: %d, buflen %d", *(int *) buffer,
4245                 *buflen);
4246         }
4247       else
4248         rv = VPPCOM_EINVAL;
4249       break;
4250
4251     default:
4252       rv = VPPCOM_EINVAL;
4253       break;
4254     }
4255
4256   return rv;
4257 }
4258
4259 int
4260 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
4261                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
4262 {
4263   vcl_worker_t *wrk = vcl_worker_get_current ();
4264   vcl_session_t *session;
4265   int rv = VPPCOM_OK;
4266
4267   if (flags == 0)
4268     rv = vppcom_session_read (session_handle, buffer, buflen);
4269   else if (flags & MSG_PEEK)
4270     rv = vppcom_session_peek (session_handle, buffer, buflen);
4271   else
4272     {
4273       VDBG (0, "Unsupport flags for recvfrom %d", flags);
4274       return VPPCOM_EAFNOSUPPORT;
4275     }
4276
4277   if (ep && rv > 0)
4278     {
4279       session = vcl_session_get_w_handle (wrk, session_handle);
4280       if (session->transport.is_ip4)
4281         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
4282                           sizeof (ip4_address_t));
4283       else
4284         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
4285                           sizeof (ip6_address_t));
4286       ep->is_ip4 = session->transport.is_ip4;
4287       ep->port = session->transport.rmt_port;
4288     }
4289
4290   return rv;
4291 }
4292
4293 static void
4294 vcl_handle_ep_app_tlvs (vcl_session_t *s, vppcom_endpt_t *ep)
4295 {
4296   vppcom_endpt_tlv_t *tlv = ep->app_tlvs;
4297
4298   do
4299     {
4300       switch (tlv->data_type)
4301         {
4302         case VCL_UDP_SEGMENT:
4303           s->gso_size = *(u16 *) tlv->data;
4304           break;
4305         case VCL_IP_PKTINFO:
4306           clib_memcpy_fast (&s->transport.lcl_ip, (ip4_address_t *) tlv->data,
4307                             sizeof (ip4_address_t));
4308           break;
4309         default:
4310           VDBG (0, "Ignorning unsupported app tlv %u", tlv->data_type);
4311           break;
4312         }
4313       tlv = VCL_EP_NEXT_APP_TLV (ep, tlv);
4314     }
4315   while (tlv);
4316 }
4317
4318 int
4319 vppcom_session_sendto (uint32_t session_handle, void *buffer,
4320                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
4321 {
4322   vcl_worker_t *wrk = vcl_worker_get_current ();
4323   vcl_session_t *s;
4324
4325   s = vcl_session_get_w_handle (wrk, session_handle);
4326   if (PREDICT_FALSE (!s))
4327     return VPPCOM_EBADFD;
4328
4329   if (ep)
4330     {
4331       if (!vcl_session_is_cl (s))
4332         return VPPCOM_EINVAL;
4333
4334       s->transport.is_ip4 = ep->is_ip4;
4335       s->transport.rmt_port = ep->port;
4336       vcl_ip_copy_from_ep (&s->transport.rmt_ip, ep);
4337
4338       if (ep->app_tlvs)
4339         vcl_handle_ep_app_tlvs (s, ep);
4340
4341       /* Session not connected/bound in vpp. Create it by 'connecting' it */
4342       if (PREDICT_FALSE (s->session_state == VCL_STATE_CLOSED))
4343         {
4344           u32 session_index = s->session_index;
4345           f64 timeout = vcm->cfg.session_timeout;
4346           int rv;
4347
4348           vcl_send_session_connect (wrk, s);
4349           rv = vppcom_wait_for_session_state_change (session_index,
4350                                                      VCL_STATE_READY,
4351                                                      timeout);
4352           if (rv < 0)
4353             return rv;
4354           s = vcl_session_get (wrk, session_index);
4355         }
4356     }
4357
4358   if (flags)
4359     {
4360       // TBD check the flags and do the right thing
4361       VDBG (2, "handling flags 0x%u (%d) not implemented yet.", flags, flags);
4362     }
4363
4364   return (vppcom_session_write_inline (wrk, s, buffer, buflen, 1,
4365                                        s->is_dgram ? 1 : 0));
4366 }
4367
4368 int
4369 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
4370 {
4371   vcl_worker_t *wrk = vcl_worker_get_current ();
4372   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
4373   u32 i, keep_trying = 1;
4374   svm_msg_q_msg_t msg;
4375   session_event_t *e;
4376   int rv, num_ev = 0;
4377
4378   VDBG (3, "vp %p, nsids %u, wait_for_time %f", vp, n_sids, wait_for_time);
4379
4380   if (!vp)
4381     return VPPCOM_EFAULT;
4382
4383   do
4384     {
4385       vcl_session_t *session;
4386
4387       /* Dequeue all events and drop all unhandled io events */
4388       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
4389         {
4390           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
4391           vcl_handle_mq_event (wrk, e);
4392           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
4393         }
4394       vec_reset_length (wrk->unhandled_evts_vector);
4395
4396       for (i = 0; i < n_sids; i++)
4397         {
4398           session = vcl_session_get (wrk, vp[i].sh);
4399           if (!session)
4400             {
4401               vp[i].revents = POLLHUP;
4402               num_ev++;
4403               continue;
4404             }
4405
4406           vp[i].revents = 0;
4407
4408           if (POLLIN & vp[i].events)
4409             {
4410               rv = vcl_session_read_ready (session);
4411               if (rv > 0)
4412                 {
4413                   vp[i].revents |= POLLIN;
4414                   num_ev++;
4415                 }
4416               else if (rv < 0)
4417                 {
4418                   switch (rv)
4419                     {
4420                     case VPPCOM_ECONNRESET:
4421                       vp[i].revents = POLLHUP;
4422                       break;
4423
4424                     default:
4425                       vp[i].revents = POLLERR;
4426                       break;
4427                     }
4428                   num_ev++;
4429                 }
4430             }
4431
4432           if (POLLOUT & vp[i].events)
4433             {
4434               rv = vcl_session_write_ready (session);
4435               if (rv > 0)
4436                 {
4437                   vp[i].revents |= POLLOUT;
4438                   num_ev++;
4439                 }
4440               else if (rv < 0)
4441                 {
4442                   switch (rv)
4443                     {
4444                     case VPPCOM_ECONNRESET:
4445                       vp[i].revents = POLLHUP;
4446                       break;
4447
4448                     default:
4449                       vp[i].revents = POLLERR;
4450                       break;
4451                     }
4452                   num_ev++;
4453                 }
4454             }
4455
4456           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
4457             {
4458               vp[i].revents = POLLNVAL;
4459               num_ev++;
4460             }
4461         }
4462       if (wait_for_time != -1)
4463         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
4464     }
4465   while ((num_ev == 0) && keep_trying);
4466
4467   return num_ev;
4468 }
4469
4470 int
4471 vppcom_mq_epoll_fd (void)
4472 {
4473   vcl_worker_t *wrk = vcl_worker_get_current ();
4474   return wrk->mqs_epfd;
4475 }
4476
4477 int
4478 vppcom_session_index (vcl_session_handle_t session_handle)
4479 {
4480   return session_handle & 0xFFFFFF;
4481 }
4482
4483 int
4484 vppcom_session_worker (vcl_session_handle_t session_handle)
4485 {
4486   return session_handle >> 24;
4487 }
4488
4489 int
4490 vppcom_worker_register (void)
4491 {
4492   if (!vcl_worker_alloc_and_init ())
4493     return VPPCOM_EEXIST;
4494
4495   if (vcl_worker_register_with_vpp ())
4496     return VPPCOM_EEXIST;
4497
4498   return VPPCOM_OK;
4499 }
4500
4501 void
4502 vppcom_worker_unregister (void)
4503 {
4504   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
4505   vcl_set_worker_index (~0);
4506 }
4507
4508 void
4509 vppcom_worker_index_set (int index)
4510 {
4511   vcl_set_worker_index (index);
4512 }
4513
4514 int
4515 vppcom_worker_index (void)
4516 {
4517   return vcl_get_worker_index ();
4518 }
4519
4520 int
4521 vppcom_worker_mqs_epfd (void)
4522 {
4523   vcl_worker_t *wrk = vcl_worker_get_current ();
4524   if (!vcm->cfg.use_mq_eventfd)
4525     return -1;
4526   return wrk->mqs_epfd;
4527 }
4528
4529 int
4530 vppcom_session_is_connectable_listener (uint32_t session_handle)
4531 {
4532   vcl_session_t *session;
4533   vcl_worker_t *wrk = vcl_worker_get_current ();
4534   session = vcl_session_get_w_handle (wrk, session_handle);
4535   if (!session)
4536     return VPPCOM_EBADFD;
4537   return vcl_session_is_connectable_listener (wrk, session);
4538 }
4539
4540 int
4541 vppcom_session_listener (uint32_t session_handle)
4542 {
4543   vcl_worker_t *wrk = vcl_worker_get_current ();
4544   vcl_session_t *listen_session, *session;
4545   session = vcl_session_get_w_handle (wrk, session_handle);
4546   if (!session)
4547     return VPPCOM_EBADFD;
4548   if (session->listener_index == VCL_INVALID_SESSION_INDEX)
4549     return VPPCOM_EBADFD;
4550   listen_session = vcl_session_get_w_handle (wrk, session->listener_index);
4551   if (!listen_session)
4552     return VPPCOM_EBADFD;
4553   return vcl_session_handle (listen_session);
4554 }
4555
4556 int
4557 vppcom_session_n_accepted (uint32_t session_handle)
4558 {
4559   vcl_worker_t *wrk = vcl_worker_get_current ();
4560   vcl_session_t *session = vcl_session_get_w_handle (wrk, session_handle);
4561   if (!session)
4562     return VPPCOM_EBADFD;
4563   return session->n_accepted_sessions;
4564 }
4565
4566 const char *
4567 vppcom_proto_str (vppcom_proto_t proto)
4568 {
4569   char const *proto_str;
4570
4571   switch (proto)
4572     {
4573     case VPPCOM_PROTO_TCP:
4574       proto_str = "TCP";
4575       break;
4576     case VPPCOM_PROTO_UDP:
4577       proto_str = "UDP";
4578       break;
4579     case VPPCOM_PROTO_TLS:
4580       proto_str = "TLS";
4581       break;
4582     case VPPCOM_PROTO_QUIC:
4583       proto_str = "QUIC";
4584       break;
4585     case VPPCOM_PROTO_DTLS:
4586       proto_str = "DTLS";
4587       break;
4588     case VPPCOM_PROTO_SRTP:
4589       proto_str = "SRTP";
4590       break;
4591     default:
4592       proto_str = "UNKNOWN";
4593       break;
4594     }
4595   return proto_str;
4596 }
4597
4598 const char *
4599 vppcom_retval_str (int retval)
4600 {
4601   char const *st;
4602
4603   switch (retval)
4604     {
4605     case VPPCOM_OK:
4606       st = "VPPCOM_OK";
4607       break;
4608
4609     case VPPCOM_EAGAIN:
4610       st = "VPPCOM_EAGAIN";
4611       break;
4612
4613     case VPPCOM_EFAULT:
4614       st = "VPPCOM_EFAULT";
4615       break;
4616
4617     case VPPCOM_ENOMEM:
4618       st = "VPPCOM_ENOMEM";
4619       break;
4620
4621     case VPPCOM_EINVAL:
4622       st = "VPPCOM_EINVAL";
4623       break;
4624
4625     case VPPCOM_EBADFD:
4626       st = "VPPCOM_EBADFD";
4627       break;
4628
4629     case VPPCOM_EAFNOSUPPORT:
4630       st = "VPPCOM_EAFNOSUPPORT";
4631       break;
4632
4633     case VPPCOM_ECONNABORTED:
4634       st = "VPPCOM_ECONNABORTED";
4635       break;
4636
4637     case VPPCOM_ECONNRESET:
4638       st = "VPPCOM_ECONNRESET";
4639       break;
4640
4641     case VPPCOM_ENOTCONN:
4642       st = "VPPCOM_ENOTCONN";
4643       break;
4644
4645     case VPPCOM_ECONNREFUSED:
4646       st = "VPPCOM_ECONNREFUSED";
4647       break;
4648
4649     case VPPCOM_ETIMEDOUT:
4650       st = "VPPCOM_ETIMEDOUT";
4651       break;
4652
4653     case VPPCOM_EADDRINUSE:
4654       st = "VPPCOM_EADDRINUSE";
4655       break;
4656
4657     default:
4658       st = "UNKNOWN_STATE";
4659       break;
4660     }
4661
4662   return st;
4663 }
4664
4665 int
4666 vppcom_add_cert_key_pair (vppcom_cert_key_pair_t *ckpair)
4667 {
4668   if (vcm->cfg.vpp_app_socket_api)
4669     return vcl_sapi_add_cert_key_pair (ckpair);
4670   else
4671     return vcl_bapi_add_cert_key_pair (ckpair);
4672 }
4673
4674 int
4675 vppcom_del_cert_key_pair (uint32_t ckpair_index)
4676 {
4677   if (vcm->cfg.vpp_app_socket_api)
4678     return vcl_sapi_del_cert_key_pair (ckpair_index);
4679   else
4680     return vcl_bapi_del_cert_key_pair (ckpair_index);
4681 }
4682
4683 int
4684 vppcom_session_get_error (uint32_t session_handle)
4685 {
4686   vcl_worker_t *wrk = vcl_worker_get_current ();
4687   vcl_session_t *session = 0;
4688
4689   session = vcl_session_get_w_handle (wrk, session_handle);
4690   if (!session)
4691     return VPPCOM_EBADFD;
4692
4693   if (PREDICT_FALSE (session->flags & VCL_SESSION_F_IS_VEP))
4694     {
4695       VWRN ("epoll session %u! will not have connect", session->session_index);
4696       return VPPCOM_EBADFD;
4697     }
4698
4699   if (session->vpp_error == SESSION_E_PORTINUSE)
4700     return VPPCOM_EADDRINUSE;
4701   else if (session->vpp_error == SESSION_E_REFUSED)
4702     return VPPCOM_ECONNREFUSED;
4703   else if (session->vpp_error != SESSION_E_NONE)
4704     return VPPCOM_EFAULT;
4705   else
4706     return VPPCOM_OK;
4707 }
4708
4709 int
4710 vppcom_worker_is_detached (void)
4711 {
4712   vcl_worker_t *wrk = vcl_worker_get_current ();
4713
4714   if (!vcm->cfg.use_mq_eventfd)
4715     return VPPCOM_ENOTSUP;
4716
4717   return wrk->api_client_handle == ~0;
4718 }
4719
4720 /*
4721  * fd.io coding-style-patch-verification: ON
4722  *
4723  * Local Variables:
4724  * eval: (c-set-style "gnu")
4725  * End:
4726  */