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