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