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