tcp: avoid updating rcv wnd in resets
[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, "SCTP"))
1534     *proto = VPPCOM_PROTO_SCTP;
1535   else if (!strcmp (proto_str, "sctp"))
1536     *proto = VPPCOM_PROTO_SCTP;
1537   else if (!strcmp (proto_str, "TLS"))
1538     *proto = VPPCOM_PROTO_TLS;
1539   else if (!strcmp (proto_str, "tls"))
1540     *proto = VPPCOM_PROTO_TLS;
1541   else if (!strcmp (proto_str, "QUIC"))
1542     *proto = VPPCOM_PROTO_QUIC;
1543   else if (!strcmp (proto_str, "quic"))
1544     *proto = VPPCOM_PROTO_QUIC;
1545   else
1546     return 1;
1547   return 0;
1548 }
1549
1550 int
1551 vppcom_session_accept (uint32_t listen_session_handle, vppcom_endpt_t * ep,
1552                        uint32_t flags)
1553 {
1554   u32 client_session_index = ~0, listen_session_index, accept_flags = 0;
1555   vcl_worker_t *wrk = vcl_worker_get_current ();
1556   session_accepted_msg_t accepted_msg;
1557   vcl_session_t *listen_session = 0;
1558   vcl_session_t *client_session = 0;
1559   vcl_session_msg_t *evt;
1560   svm_msg_q_msg_t msg;
1561   session_event_t *e;
1562   u8 is_nonblocking;
1563   int rv;
1564
1565   listen_session = vcl_session_get_w_handle (wrk, listen_session_handle);
1566   if (!listen_session)
1567     return VPPCOM_EBADFD;
1568
1569   listen_session_index = listen_session->session_index;
1570   if ((rv = validate_args_session_accept_ (wrk, listen_session)))
1571     return rv;
1572
1573   if (clib_fifo_elts (listen_session->accept_evts_fifo))
1574     {
1575       clib_fifo_sub2 (listen_session->accept_evts_fifo, evt);
1576       accept_flags = evt->flags;
1577       accepted_msg = evt->accepted_msg;
1578       goto handle;
1579     }
1580
1581   is_nonblocking = VCL_SESS_ATTR_TEST (listen_session->attr,
1582                                        VCL_SESS_ATTR_NONBLOCK);
1583   while (1)
1584     {
1585       if (svm_msg_q_is_empty (wrk->app_event_queue) && is_nonblocking)
1586         return VPPCOM_EAGAIN;
1587
1588       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_WAIT, 0))
1589         return VPPCOM_EAGAIN;
1590
1591       e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
1592       if (e->event_type != SESSION_CTRL_EVT_ACCEPTED)
1593         {
1594           vcl_handle_mq_event (wrk, e);
1595           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1596           continue;
1597         }
1598       clib_memcpy_fast (&accepted_msg, e->data, sizeof (accepted_msg));
1599       svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1600       break;
1601     }
1602
1603 handle:
1604
1605   client_session_index = vcl_session_accepted_handler (wrk, &accepted_msg,
1606                                                        listen_session_index);
1607   if (client_session_index == VCL_INVALID_SESSION_INDEX)
1608     return VPPCOM_ECONNABORTED;
1609
1610   listen_session = vcl_session_get (wrk, listen_session_index);
1611   client_session = vcl_session_get (wrk, client_session_index);
1612
1613   if (flags & O_NONBLOCK)
1614     VCL_SESS_ATTR_SET (client_session->attr, VCL_SESS_ATTR_NONBLOCK);
1615
1616   VDBG (1, "listener %u [0x%llx]: Got a connect request! session %u [0x%llx],"
1617         " flags %d, is_nonblocking %u", listen_session->session_index,
1618         listen_session->vpp_handle, client_session_index,
1619         client_session->vpp_handle, flags,
1620         VCL_SESS_ATTR_TEST (client_session->attr, VCL_SESS_ATTR_NONBLOCK));
1621
1622   if (ep)
1623     {
1624       ep->is_ip4 = client_session->transport.is_ip4;
1625       ep->port = client_session->transport.rmt_port;
1626       if (client_session->transport.is_ip4)
1627         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip4,
1628                           sizeof (ip4_address_t));
1629       else
1630         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip6,
1631                           sizeof (ip6_address_t));
1632     }
1633
1634   VDBG (0, "listener %u [0x%llx] accepted %u [0x%llx] peer: %U:%u "
1635         "local: %U:%u", listen_session_handle, listen_session->vpp_handle,
1636         client_session_index, client_session->vpp_handle,
1637         format_ip46_address, &client_session->transport.rmt_ip,
1638         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1639         clib_net_to_host_u16 (client_session->transport.rmt_port),
1640         format_ip46_address, &client_session->transport.lcl_ip,
1641         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1642         clib_net_to_host_u16 (client_session->transport.lcl_port));
1643   vcl_evt (VCL_EVT_ACCEPT, client_session, listen_session,
1644            client_session_index);
1645
1646   /*
1647    * Session might have been closed already
1648    */
1649   if (accept_flags)
1650     {
1651       if (accept_flags & VCL_ACCEPTED_F_CLOSED)
1652         client_session->session_state = STATE_VPP_CLOSING;
1653       else if (accept_flags & VCL_ACCEPTED_F_RESET)
1654         client_session->session_state = STATE_DISCONNECT;
1655     }
1656   return vcl_session_handle (client_session);
1657 }
1658
1659 static void
1660 vcl_ip_copy_from_ep (ip46_address_t * ip, vppcom_endpt_t * ep)
1661 {
1662   if (ep->is_ip4)
1663     clib_memcpy_fast (&ip->ip4, ep->ip, sizeof (ip4_address_t));
1664   else
1665     clib_memcpy_fast (&ip->ip6, ep->ip, sizeof (ip6_address_t));
1666 }
1667
1668 void
1669 vcl_ip_copy_to_ep (ip46_address_t * ip, vppcom_endpt_t * ep, u8 is_ip4)
1670 {
1671   ep->is_ip4 = is_ip4;
1672   if (is_ip4)
1673     clib_memcpy_fast (ep->ip, &ip->ip4, sizeof (ip4_address_t));
1674   else
1675     clib_memcpy_fast (ep->ip, &ip->ip6, sizeof (ip6_address_t));
1676 }
1677
1678 int
1679 vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
1680 {
1681   vcl_worker_t *wrk = vcl_worker_get_current ();
1682   vcl_session_t *session = 0;
1683   u32 session_index;
1684   int rv;
1685
1686   session = vcl_session_get_w_handle (wrk, session_handle);
1687   if (!session)
1688     return VPPCOM_EBADFD;
1689   session_index = session->session_index;
1690
1691   if (PREDICT_FALSE (session->is_vep))
1692     {
1693       VDBG (0, "ERROR: cannot connect epoll session %u!",
1694             session->session_index);
1695       return VPPCOM_EBADFD;
1696     }
1697
1698   if (PREDICT_FALSE (session->session_state & CLIENT_STATE_OPEN))
1699     {
1700       VDBG (0, "session handle %u [0x%llx]: session already "
1701             "connected to %s %U port %d proto %s, state 0x%x (%s)",
1702             session_handle, session->vpp_handle,
1703             session->transport.is_ip4 ? "IPv4" : "IPv6", format_ip46_address,
1704             &session->transport.rmt_ip, session->transport.is_ip4 ?
1705             IP46_TYPE_IP4 : IP46_TYPE_IP6,
1706             clib_net_to_host_u16 (session->transport.rmt_port),
1707             vppcom_proto_str (session->session_type), session->session_state,
1708             vppcom_session_state_str (session->session_state));
1709       return VPPCOM_OK;
1710     }
1711
1712   session->transport.is_ip4 = server_ep->is_ip4;
1713   vcl_ip_copy_from_ep (&session->transport.rmt_ip, server_ep);
1714   session->transport.rmt_port = server_ep->port;
1715   session->parent_handle = VCL_INVALID_SESSION_HANDLE;
1716
1717   VDBG (0, "session handle %u: connecting to server %s %U "
1718         "port %d proto %s", session_handle,
1719         session->transport.is_ip4 ? "IPv4" : "IPv6",
1720         format_ip46_address,
1721         &session->transport.rmt_ip, session->transport.is_ip4 ?
1722         IP46_TYPE_IP4 : IP46_TYPE_IP6,
1723         clib_net_to_host_u16 (session->transport.rmt_port),
1724         vppcom_proto_str (session->session_type));
1725
1726   vcl_send_session_connect (wrk, session);
1727
1728   if (VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK))
1729     return VPPCOM_EINPROGRESS;
1730
1731   /*
1732    * Wait for reply from vpp if blocking
1733    */
1734   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
1735                                              vcm->cfg.session_timeout);
1736
1737   session = vcl_session_get (wrk, session_index);
1738   VDBG (0, "session %u [0x%llx]: connect %s!", session->session_index,
1739         session->vpp_handle, rv ? "failed" : "succeeded");
1740
1741   return rv;
1742 }
1743
1744 int
1745 vppcom_session_stream_connect (uint32_t session_handle,
1746                                uint32_t parent_session_handle)
1747 {
1748   vcl_worker_t *wrk = vcl_worker_get_current ();
1749   vcl_session_t *session, *parent_session;
1750   u32 session_index, parent_session_index;
1751   int rv;
1752
1753   session = vcl_session_get_w_handle (wrk, session_handle);
1754   if (!session)
1755     return VPPCOM_EBADFD;
1756   parent_session = vcl_session_get_w_handle (wrk, parent_session_handle);
1757   if (!parent_session)
1758     return VPPCOM_EBADFD;
1759
1760   session_index = session->session_index;
1761   parent_session_index = parent_session->session_index;
1762   if (PREDICT_FALSE (session->is_vep))
1763     {
1764       VDBG (0, "ERROR: cannot connect epoll session %u!",
1765             session->session_index);
1766       return VPPCOM_EBADFD;
1767     }
1768
1769   if (PREDICT_FALSE (session->session_state & CLIENT_STATE_OPEN))
1770     {
1771       VDBG (0, "session handle %u [0x%llx]: session already "
1772             "connected to session %u [0x%llx] proto %s, state 0x%x (%s)",
1773             session_handle, session->vpp_handle,
1774             parent_session_handle, parent_session->vpp_handle,
1775             vppcom_proto_str (session->session_type), session->session_state,
1776             vppcom_session_state_str (session->session_state));
1777       return VPPCOM_OK;
1778     }
1779
1780   /* Connect to quic session specifics */
1781   session->transport.is_ip4 = parent_session->transport.is_ip4;
1782   session->transport.rmt_ip.ip4.as_u32 = (uint32_t) 1;
1783   session->transport.rmt_port = 0;
1784   session->parent_handle = parent_session->vpp_handle;
1785
1786   VDBG (0, "session handle %u: connecting to session %u [0x%llx]",
1787         session_handle, parent_session_handle, parent_session->vpp_handle);
1788
1789   /*
1790    * Send connect request and wait for reply from vpp
1791    */
1792   vcl_send_session_connect (wrk, session);
1793   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
1794                                              vcm->cfg.session_timeout);
1795
1796   session->listener_index = parent_session_index;
1797   parent_session = vcl_session_get_w_handle (wrk, parent_session_handle);
1798   if (parent_session)
1799     parent_session->n_accepted_sessions++;
1800
1801   session = vcl_session_get (wrk, session_index);
1802   VDBG (0, "session %u [0x%llx]: connect %s!", session->session_index,
1803         session->vpp_handle, rv ? "failed" : "succeeded");
1804
1805   return rv;
1806 }
1807
1808 static u8
1809 vcl_is_rx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
1810 {
1811   return (e->event_type == SESSION_IO_EVT_RX && e->session_index == sid);
1812 }
1813
1814 static inline int
1815 vppcom_session_read_internal (uint32_t session_handle, void *buf, int n,
1816                               u8 peek)
1817 {
1818   vcl_worker_t *wrk = vcl_worker_get_current ();
1819   int n_read = 0, is_nonblocking;
1820   vcl_session_t *s = 0;
1821   svm_fifo_t *rx_fifo;
1822   svm_msg_q_msg_t msg;
1823   session_event_t *e;
1824   svm_msg_q_t *mq;
1825   u8 is_ct;
1826
1827   if (PREDICT_FALSE (!buf))
1828     return VPPCOM_EINVAL;
1829
1830   s = vcl_session_get_w_handle (wrk, session_handle);
1831   if (PREDICT_FALSE (!s || s->is_vep))
1832     return VPPCOM_EBADFD;
1833
1834   if (PREDICT_FALSE (!vcl_session_is_open (s)))
1835     {
1836       VDBG (0, "session %u[0x%llx] is not open! state 0x%x (%s)",
1837             s->session_index, s->vpp_handle, s->session_state,
1838             vppcom_session_state_str (s->session_state));
1839       return vcl_session_closed_error (s);
1840     }
1841
1842   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1843   is_ct = vcl_session_is_ct (s);
1844   mq = wrk->app_event_queue;
1845   rx_fifo = is_ct ? s->ct_rx_fifo : s->rx_fifo;
1846   s->has_rx_evt = 0;
1847
1848   if (svm_fifo_is_empty_cons (rx_fifo))
1849     {
1850       if (is_nonblocking)
1851         {
1852           if (vcl_session_is_closing (s))
1853             return vcl_session_closing_error (s);
1854           svm_fifo_unset_event (s->rx_fifo);
1855           return VPPCOM_EWOULDBLOCK;
1856         }
1857       while (svm_fifo_is_empty_cons (rx_fifo))
1858         {
1859           if (vcl_session_is_closing (s))
1860             return vcl_session_closing_error (s);
1861
1862           svm_fifo_unset_event (s->rx_fifo);
1863           svm_msg_q_lock (mq);
1864           if (svm_msg_q_is_empty (mq))
1865             svm_msg_q_wait (mq);
1866
1867           svm_msg_q_sub_w_lock (mq, &msg);
1868           e = svm_msg_q_msg_data (mq, &msg);
1869           svm_msg_q_unlock (mq);
1870           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1871             vcl_handle_mq_event (wrk, e);
1872           svm_msg_q_free_msg (mq, &msg);
1873         }
1874     }
1875
1876   if (s->is_dgram)
1877     n_read = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek);
1878   else
1879     n_read = app_recv_stream_raw (rx_fifo, buf, n, 0, peek);
1880
1881   if (svm_fifo_is_empty_cons (rx_fifo))
1882     svm_fifo_unset_event (s->rx_fifo);
1883
1884   /* Cut-through sessions might request tx notifications on rx fifos */
1885   if (PREDICT_FALSE (rx_fifo->want_deq_ntf))
1886     {
1887       app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo->master_session_index,
1888                               SESSION_IO_EVT_RX, SVM_Q_WAIT);
1889       svm_fifo_reset_has_deq_ntf (s->rx_fifo);
1890     }
1891
1892   VDBG (2, "session %u[0x%llx]: read %d bytes from (%p)", s->session_index,
1893         s->vpp_handle, n_read, rx_fifo);
1894
1895   return n_read;
1896 }
1897
1898 int
1899 vppcom_session_read (uint32_t session_handle, void *buf, size_t n)
1900 {
1901   return (vppcom_session_read_internal (session_handle, buf, n, 0));
1902 }
1903
1904 static int
1905 vppcom_session_peek (uint32_t session_handle, void *buf, int n)
1906 {
1907   return (vppcom_session_read_internal (session_handle, buf, n, 1));
1908 }
1909
1910 int
1911 vppcom_session_read_segments (uint32_t session_handle,
1912                               vppcom_data_segments_t ds)
1913 {
1914   vcl_worker_t *wrk = vcl_worker_get_current ();
1915   int n_read = 0, is_nonblocking;
1916   vcl_session_t *s = 0;
1917   svm_fifo_t *rx_fifo;
1918   svm_msg_q_msg_t msg;
1919   session_event_t *e;
1920   svm_msg_q_t *mq;
1921   u8 is_ct;
1922
1923   s = vcl_session_get_w_handle (wrk, session_handle);
1924   if (PREDICT_FALSE (!s || s->is_vep))
1925     return VPPCOM_EBADFD;
1926
1927   if (PREDICT_FALSE (!vcl_session_is_open (s)))
1928     return vcl_session_closed_error (s);
1929
1930   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1931   is_ct = vcl_session_is_ct (s);
1932   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1933   rx_fifo = s->rx_fifo;
1934   s->has_rx_evt = 0;
1935
1936   if (is_ct)
1937     svm_fifo_unset_event (s->rx_fifo);
1938
1939   if (svm_fifo_is_empty_cons (rx_fifo))
1940     {
1941       if (is_nonblocking)
1942         {
1943           svm_fifo_unset_event (rx_fifo);
1944           return VPPCOM_EWOULDBLOCK;
1945         }
1946       while (svm_fifo_is_empty_cons (rx_fifo))
1947         {
1948           if (vcl_session_is_closing (s))
1949             return vcl_session_closing_error (s);
1950
1951           svm_fifo_unset_event (rx_fifo);
1952           svm_msg_q_lock (mq);
1953           if (svm_msg_q_is_empty (mq))
1954             svm_msg_q_wait (mq);
1955
1956           svm_msg_q_sub_w_lock (mq, &msg);
1957           e = svm_msg_q_msg_data (mq, &msg);
1958           svm_msg_q_unlock (mq);
1959           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1960             vcl_handle_mq_event (wrk, e);
1961           svm_msg_q_free_msg (mq, &msg);
1962         }
1963     }
1964
1965   n_read = svm_fifo_segments (rx_fifo, (svm_fifo_seg_t *) ds);
1966   svm_fifo_unset_event (rx_fifo);
1967
1968   return n_read;
1969 }
1970
1971 void
1972 vppcom_session_free_segments (uint32_t session_handle,
1973                               vppcom_data_segments_t ds)
1974 {
1975   vcl_worker_t *wrk = vcl_worker_get_current ();
1976   vcl_session_t *s;
1977
1978   s = vcl_session_get_w_handle (wrk, session_handle);
1979   if (PREDICT_FALSE (!s || s->is_vep))
1980     return;
1981
1982   svm_fifo_segments_free (s->rx_fifo, (svm_fifo_seg_t *) ds);
1983 }
1984
1985 int
1986 vppcom_data_segment_copy (void *buf, vppcom_data_segments_t ds, u32 max_bytes)
1987 {
1988   u32 first_copy = clib_min (ds[0].len, max_bytes);
1989   clib_memcpy_fast (buf, ds[0].data, first_copy);
1990   if (first_copy < max_bytes)
1991     {
1992       clib_memcpy_fast (buf + first_copy, ds[1].data,
1993                         clib_min (ds[1].len, max_bytes - first_copy));
1994     }
1995   return 0;
1996 }
1997
1998 static u8
1999 vcl_is_tx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
2000 {
2001   return (e->event_type == SESSION_IO_EVT_TX && e->session_index == sid);
2002 }
2003
2004 static inline int
2005 vppcom_session_write_inline (uint32_t session_handle, void *buf, size_t n,
2006                              u8 is_flush)
2007 {
2008   vcl_worker_t *wrk = vcl_worker_get_current ();
2009   int n_write, is_nonblocking;
2010   vcl_session_t *s = 0;
2011   session_evt_type_t et;
2012   svm_msg_q_msg_t msg;
2013   svm_fifo_t *tx_fifo;
2014   session_event_t *e;
2015   svm_msg_q_t *mq;
2016   u8 is_ct;
2017
2018   if (PREDICT_FALSE (!buf || n == 0))
2019     return VPPCOM_EINVAL;
2020
2021   s = vcl_session_get_w_handle (wrk, session_handle);
2022   if (PREDICT_FALSE (!s))
2023     return VPPCOM_EBADFD;
2024
2025   if (PREDICT_FALSE (s->is_vep))
2026     {
2027       VDBG (0, "ERROR: session %u [0x%llx]: cannot write to an epoll"
2028             " session!", s->session_index, s->vpp_handle);
2029       return VPPCOM_EBADFD;
2030     }
2031
2032   if (PREDICT_FALSE (!vcl_session_is_open (s)))
2033     {
2034       VDBG (1, "session %u [0x%llx]: is not open! state 0x%x (%s)",
2035             s->session_index, s->vpp_handle, s->session_state,
2036             vppcom_session_state_str (s->session_state));
2037       return vcl_session_closed_error (s);;
2038     }
2039
2040   is_ct = vcl_session_is_ct (s);
2041   tx_fifo = is_ct ? s->ct_tx_fifo : s->tx_fifo;
2042   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
2043
2044   mq = wrk->app_event_queue;
2045   if (svm_fifo_is_full_prod (tx_fifo))
2046     {
2047       if (is_nonblocking)
2048         {
2049           return VPPCOM_EWOULDBLOCK;
2050         }
2051       while (svm_fifo_is_full_prod (tx_fifo))
2052         {
2053           svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
2054           if (vcl_session_is_closing (s))
2055             return vcl_session_closing_error (s);
2056           svm_msg_q_lock (mq);
2057           if (svm_msg_q_is_empty (mq))
2058             svm_msg_q_wait (mq);
2059
2060           svm_msg_q_sub_w_lock (mq, &msg);
2061           e = svm_msg_q_msg_data (mq, &msg);
2062           svm_msg_q_unlock (mq);
2063
2064           if (!vcl_is_tx_evt_for_session (e, s->session_index, is_ct))
2065             vcl_handle_mq_event (wrk, e);
2066           svm_msg_q_free_msg (mq, &msg);
2067         }
2068     }
2069
2070   et = SESSION_IO_EVT_TX;
2071   if (is_flush && !is_ct)
2072     et = SESSION_IO_EVT_TX_FLUSH;
2073
2074   if (s->is_dgram)
2075     n_write = app_send_dgram_raw (tx_fifo, &s->transport,
2076                                   s->vpp_evt_q, buf, n, et,
2077                                   0 /* do_evt */ , SVM_Q_WAIT);
2078   else
2079     n_write = app_send_stream_raw (tx_fifo, s->vpp_evt_q, buf, n, et,
2080                                    0 /* do_evt */ , SVM_Q_WAIT);
2081
2082   if (svm_fifo_set_event (s->tx_fifo))
2083     app_send_io_evt_to_vpp (s->vpp_evt_q, s->tx_fifo->master_session_index,
2084                             et, SVM_Q_WAIT);
2085
2086   ASSERT (n_write > 0);
2087
2088   VDBG (2, "session %u [0x%llx]: wrote %d bytes", s->session_index,
2089         s->vpp_handle, n_write);
2090
2091   return n_write;
2092 }
2093
2094 int
2095 vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
2096 {
2097   return vppcom_session_write_inline (session_handle, buf, n,
2098                                       0 /* is_flush */ );
2099 }
2100
2101 int
2102 vppcom_session_write_msg (uint32_t session_handle, void *buf, size_t n)
2103 {
2104   return vppcom_session_write_inline (session_handle, buf, n,
2105                                       1 /* is_flush */ );
2106 }
2107
2108 #define vcl_fifo_rx_evt_valid_or_break(_s)                              \
2109 if (PREDICT_FALSE (!_s->rx_fifo))                                       \
2110   break;                                                                \
2111 if (PREDICT_FALSE (svm_fifo_is_empty (_s->rx_fifo)))                    \
2112   {                                                                     \
2113     if (!vcl_session_is_ct (_s))                                        \
2114       {                                                                 \
2115         svm_fifo_unset_event (_s->rx_fifo);                             \
2116         if (svm_fifo_is_empty (_s->rx_fifo))                            \
2117           break;                                                        \
2118       }                                                                 \
2119     else if (svm_fifo_is_empty (_s->ct_rx_fifo))                        \
2120       {                                                                 \
2121         svm_fifo_unset_event (_s->ct_rx_fifo);                          \
2122         if (svm_fifo_is_empty (_s->ct_rx_fifo))                         \
2123           break;                                                        \
2124       }                                                                 \
2125   }                                                                     \
2126
2127 static void
2128 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2129                             unsigned long n_bits, unsigned long *read_map,
2130                             unsigned long *write_map,
2131                             unsigned long *except_map, u32 * bits_set)
2132 {
2133   session_disconnected_msg_t *disconnected_msg;
2134   session_connected_msg_t *connected_msg;
2135   vcl_session_t *session;
2136   u32 sid;
2137
2138   switch (e->event_type)
2139     {
2140     case SESSION_IO_EVT_RX:
2141       sid = e->session_index;
2142       session = vcl_session_get (wrk, sid);
2143       if (!session)
2144         break;
2145       vcl_fifo_rx_evt_valid_or_break (session);
2146       if (sid < n_bits && read_map)
2147         {
2148           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2149           *bits_set += 1;
2150         }
2151       break;
2152     case SESSION_IO_EVT_TX:
2153       sid = e->session_index;
2154       session = vcl_session_get (wrk, sid);
2155       if (!session)
2156         break;
2157       if (sid < n_bits && write_map)
2158         {
2159           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2160           *bits_set += 1;
2161         }
2162       break;
2163     case SESSION_CTRL_EVT_ACCEPTED:
2164       session = vcl_session_accepted (wrk,
2165                                       (session_accepted_msg_t *) e->data);
2166       if (!session)
2167         break;
2168       sid = session->session_index;
2169       if (sid < n_bits && read_map)
2170         {
2171           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2172           *bits_set += 1;
2173         }
2174       break;
2175     case SESSION_CTRL_EVT_CONNECTED:
2176       connected_msg = (session_connected_msg_t *) e->data;
2177       sid = vcl_session_connected_handler (wrk, connected_msg);
2178       if (sid == VCL_INVALID_SESSION_INDEX)
2179         break;
2180       if (sid < n_bits && write_map)
2181         {
2182           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2183           *bits_set += 1;
2184         }
2185       break;
2186     case SESSION_CTRL_EVT_DISCONNECTED:
2187       disconnected_msg = (session_disconnected_msg_t *) e->data;
2188       session = vcl_session_disconnected_handler (wrk, disconnected_msg);
2189       if (!session)
2190         break;
2191       sid = session->session_index;
2192       if (sid < n_bits && except_map)
2193         {
2194           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2195           *bits_set += 1;
2196         }
2197       break;
2198     case SESSION_CTRL_EVT_RESET:
2199       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2200       if (sid < n_bits && except_map)
2201         {
2202           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2203           *bits_set += 1;
2204         }
2205       break;
2206     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
2207       vcl_session_unlisten_reply_handler (wrk, e->data);
2208       break;
2209     case SESSION_CTRL_EVT_MIGRATED:
2210       vcl_session_migrated_handler (wrk, e->data);
2211       break;
2212     case SESSION_CTRL_EVT_CLEANUP:
2213       vcl_session_cleanup_handler (wrk, e->data);
2214       break;
2215     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
2216       vcl_session_worker_update_reply_handler (wrk, e->data);
2217       break;
2218     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
2219       vcl_session_req_worker_update_handler (wrk, e->data);
2220       break;
2221     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
2222       vcl_session_app_add_segment_handler (wrk, e->data);
2223       break;
2224     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
2225       vcl_session_app_del_segment_handler (wrk, e->data);
2226       break;
2227     default:
2228       clib_warning ("unhandled: %u", e->event_type);
2229       break;
2230     }
2231 }
2232
2233 static int
2234 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2235                       unsigned long n_bits, unsigned long *read_map,
2236                       unsigned long *write_map, unsigned long *except_map,
2237                       double time_to_wait, u32 * bits_set)
2238 {
2239   svm_msg_q_msg_t *msg;
2240   session_event_t *e;
2241   u32 i;
2242
2243   svm_msg_q_lock (mq);
2244   if (svm_msg_q_is_empty (mq))
2245     {
2246       if (*bits_set)
2247         {
2248           svm_msg_q_unlock (mq);
2249           return 0;
2250         }
2251
2252       if (!time_to_wait)
2253         {
2254           svm_msg_q_unlock (mq);
2255           return 0;
2256         }
2257       else if (time_to_wait < 0)
2258         {
2259           svm_msg_q_wait (mq);
2260         }
2261       else
2262         {
2263           if (svm_msg_q_timedwait (mq, time_to_wait))
2264             {
2265               svm_msg_q_unlock (mq);
2266               return 0;
2267             }
2268         }
2269     }
2270   vcl_mq_dequeue_batch (wrk, mq, ~0);
2271   svm_msg_q_unlock (mq);
2272
2273   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2274     {
2275       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2276       e = svm_msg_q_msg_data (mq, msg);
2277       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
2278                                   except_map, bits_set);
2279       svm_msg_q_free_msg (mq, msg);
2280     }
2281   vec_reset_length (wrk->mq_msg_vector);
2282   vcl_handle_pending_wrk_updates (wrk);
2283   return *bits_set;
2284 }
2285
2286 static int
2287 vppcom_select_condvar (vcl_worker_t * wrk, int n_bits,
2288                        vcl_si_set * read_map, vcl_si_set * write_map,
2289                        vcl_si_set * except_map, double time_to_wait,
2290                        u32 * bits_set)
2291 {
2292   double wait = 0, start = 0;
2293
2294   if (!*bits_set)
2295     {
2296       wait = time_to_wait;
2297       start = clib_time_now (&wrk->clib_time);
2298     }
2299
2300   do
2301     {
2302       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
2303                             write_map, except_map, wait, bits_set);
2304       if (*bits_set)
2305         return *bits_set;
2306       if (wait == -1)
2307         continue;
2308
2309       wait = wait - (clib_time_now (&wrk->clib_time) - start);
2310     }
2311   while (wait > 0);
2312
2313   return 0;
2314 }
2315
2316 static int
2317 vppcom_select_eventfd (vcl_worker_t * wrk, int n_bits,
2318                        vcl_si_set * read_map, vcl_si_set * write_map,
2319                        vcl_si_set * except_map, double time_to_wait,
2320                        u32 * bits_set)
2321 {
2322   vcl_mq_evt_conn_t *mqc;
2323   int __clib_unused n_read;
2324   int n_mq_evts, i;
2325   u64 buf;
2326
2327   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2328   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2329                           vec_len (wrk->mq_events), time_to_wait);
2330   for (i = 0; i < n_mq_evts; i++)
2331     {
2332       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2333       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2334       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
2335                             except_map, 0, bits_set);
2336     }
2337
2338   return (n_mq_evts > 0 ? (int) *bits_set : 0);
2339 }
2340
2341 int
2342 vppcom_select (int n_bits, vcl_si_set * read_map, vcl_si_set * write_map,
2343                vcl_si_set * except_map, double time_to_wait)
2344 {
2345   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
2346   vcl_worker_t *wrk = vcl_worker_get_current ();
2347   vcl_session_t *session = 0;
2348   int rv, i;
2349
2350   if (n_bits && read_map)
2351     {
2352       clib_bitmap_validate (wrk->rd_bitmap, minbits);
2353       clib_memcpy_fast (wrk->rd_bitmap, read_map,
2354                         vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2355       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2356     }
2357   if (n_bits && write_map)
2358     {
2359       clib_bitmap_validate (wrk->wr_bitmap, minbits);
2360       clib_memcpy_fast (wrk->wr_bitmap, write_map,
2361                         vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2362       memset (write_map, 0, vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2363     }
2364   if (n_bits && except_map)
2365     {
2366       clib_bitmap_validate (wrk->ex_bitmap, minbits);
2367       clib_memcpy_fast (wrk->ex_bitmap, except_map,
2368                         vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2369       memset (except_map, 0, vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2370     }
2371
2372   if (!n_bits)
2373     return 0;
2374
2375   if (!write_map)
2376     goto check_rd;
2377
2378   /* *INDENT-OFF* */
2379   clib_bitmap_foreach (sid, wrk->wr_bitmap, ({
2380     if (!(session = vcl_session_get (wrk, sid)))
2381       {
2382         if (except_map && sid < minbits)
2383           clib_bitmap_set_no_check (except_map, sid, 1);
2384         continue;
2385       }
2386
2387     rv = svm_fifo_is_full_prod (session->tx_fifo);
2388     if (!rv)
2389       {
2390         clib_bitmap_set_no_check ((uword*)write_map, sid, 1);
2391         bits_set++;
2392       }
2393     else
2394       svm_fifo_add_want_deq_ntf (session->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
2395   }));
2396
2397 check_rd:
2398   if (!read_map)
2399     goto check_mq;
2400
2401   clib_bitmap_foreach (sid, wrk->rd_bitmap, ({
2402     if (!(session = vcl_session_get (wrk, sid)))
2403       {
2404         if (except_map && sid < minbits)
2405           clib_bitmap_set_no_check (except_map, sid, 1);
2406         continue;
2407       }
2408
2409     rv = vcl_session_read_ready (session);
2410     if (rv)
2411       {
2412         clib_bitmap_set_no_check ((uword*)read_map, sid, 1);
2413         bits_set++;
2414       }
2415   }));
2416   /* *INDENT-ON* */
2417
2418 check_mq:
2419
2420   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2421     {
2422       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
2423                                   read_map, write_map, except_map, &bits_set);
2424     }
2425   vec_reset_length (wrk->unhandled_evts_vector);
2426
2427   if (vcm->cfg.use_mq_eventfd)
2428     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
2429                            time_to_wait, &bits_set);
2430   else
2431     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
2432                            time_to_wait, &bits_set);
2433
2434   return (bits_set);
2435 }
2436
2437 static inline void
2438 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_handle)
2439 {
2440   vcl_session_t *session;
2441   vppcom_epoll_t *vep;
2442   u32 sh = vep_handle;
2443
2444   if (VPPCOM_DEBUG <= 2)
2445     return;
2446
2447   session = vcl_session_get_w_handle (wrk, vep_handle);
2448   if (PREDICT_FALSE (!session))
2449     {
2450       VDBG (0, "ERROR: Invalid vep_sh (%u)!", vep_handle);
2451       goto done;
2452     }
2453   if (PREDICT_FALSE (!session->is_vep))
2454     {
2455       VDBG (0, "ERROR: vep_sh (%u) is not a vep!", vep_handle);
2456       goto done;
2457     }
2458   vep = &session->vep;
2459   VDBG (0, "vep_sh (%u): Dumping epoll chain\n"
2460         "{\n"
2461         "   is_vep         = %u\n"
2462         "   is_vep_session = %u\n"
2463         "   next_sh        = 0x%x (%u)\n"
2464         "}\n", vep_handle, session->is_vep, session->is_vep_session,
2465         vep->next_sh, vep->next_sh);
2466
2467   for (sh = vep->next_sh; sh != ~0; sh = vep->next_sh)
2468     {
2469       session = vcl_session_get_w_handle (wrk, sh);
2470       if (PREDICT_FALSE (!session))
2471         {
2472           VDBG (0, "ERROR: Invalid sh (%u)!", sh);
2473           goto done;
2474         }
2475       if (PREDICT_FALSE (session->is_vep))
2476         {
2477           VDBG (0, "ERROR: sh (%u) is a vep!", vep_handle);
2478         }
2479       else if (PREDICT_FALSE (!session->is_vep_session))
2480         {
2481           VDBG (0, "ERROR: sh (%u) is not a vep session handle!", sh);
2482           goto done;
2483         }
2484       vep = &session->vep;
2485       if (PREDICT_FALSE (vep->vep_sh != vep_handle))
2486         VDBG (0, "ERROR: session (%u) vep_sh (%u) != vep_sh (%u)!",
2487               sh, session->vep.vep_sh, vep_handle);
2488       if (session->is_vep_session)
2489         {
2490           VDBG (0, "vep_sh[%u]: sh 0x%x (%u)\n"
2491                 "{\n"
2492                 "   next_sh        = 0x%x (%u)\n"
2493                 "   prev_sh        = 0x%x (%u)\n"
2494                 "   vep_sh         = 0x%x (%u)\n"
2495                 "   ev.events      = 0x%x\n"
2496                 "   ev.data.u64    = 0x%llx\n"
2497                 "   et_mask        = 0x%x\n"
2498                 "}\n",
2499                 vep_handle, sh, sh, vep->next_sh, vep->next_sh, vep->prev_sh,
2500                 vep->prev_sh, vep->vep_sh, vep->vep_sh, vep->ev.events,
2501                 vep->ev.data.u64, vep->et_mask);
2502         }
2503     }
2504
2505 done:
2506   VDBG (0, "vep_sh (%u): Dump complete!\n", vep_handle);
2507 }
2508
2509 int
2510 vppcom_epoll_create (void)
2511 {
2512   vcl_worker_t *wrk = vcl_worker_get_current ();
2513   vcl_session_t *vep_session;
2514
2515   vep_session = vcl_session_alloc (wrk);
2516
2517   vep_session->is_vep = 1;
2518   vep_session->vep.vep_sh = ~0;
2519   vep_session->vep.next_sh = ~0;
2520   vep_session->vep.prev_sh = ~0;
2521   vep_session->vpp_handle = ~0;
2522
2523   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_session->session_index);
2524   VDBG (0, "Created vep_idx %u", vep_session->session_index);
2525
2526   return vcl_session_handle (vep_session);
2527 }
2528
2529 int
2530 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2531                   struct epoll_event *event)
2532 {
2533   vcl_worker_t *wrk = vcl_worker_get_current ();
2534   vcl_session_t *vep_session;
2535   vcl_session_t *session;
2536   int rv = VPPCOM_OK;
2537
2538   if (vep_handle == session_handle)
2539     {
2540       VDBG (0, "vep_sh == session handle (%u)!", vep_handle);
2541       return VPPCOM_EINVAL;
2542     }
2543
2544   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2545   if (PREDICT_FALSE (!vep_session))
2546     {
2547       VDBG (0, "Invalid vep_sh (%u)!", vep_handle);
2548       return VPPCOM_EBADFD;
2549     }
2550   if (PREDICT_FALSE (!vep_session->is_vep))
2551     {
2552       VDBG (0, "vep_sh (%u) is not a vep!", vep_handle);
2553       return VPPCOM_EINVAL;
2554     }
2555
2556   ASSERT (vep_session->vep.vep_sh == ~0);
2557   ASSERT (vep_session->vep.prev_sh == ~0);
2558
2559   session = vcl_session_get_w_handle (wrk, session_handle);
2560   if (PREDICT_FALSE (!session))
2561     {
2562       VDBG (0, "Invalid session_handle (%u)!", session_handle);
2563       return VPPCOM_EBADFD;
2564     }
2565   if (PREDICT_FALSE (session->is_vep))
2566     {
2567       VDBG (0, "session_handle (%u) is a vep!", vep_handle);
2568       return VPPCOM_EINVAL;
2569     }
2570
2571   switch (op)
2572     {
2573     case EPOLL_CTL_ADD:
2574       if (PREDICT_FALSE (!event))
2575         {
2576           VDBG (0, "EPOLL_CTL_ADD: NULL pointer to epoll_event structure!");
2577           return VPPCOM_EINVAL;
2578         }
2579       if (vep_session->vep.next_sh != ~0)
2580         {
2581           vcl_session_t *next_session;
2582           next_session = vcl_session_get_w_handle (wrk,
2583                                                    vep_session->vep.next_sh);
2584           if (PREDICT_FALSE (!next_session))
2585             {
2586               VDBG (0, "EPOLL_CTL_ADD: Invalid vep.next_sh (%u) on "
2587                     "vep_idx (%u)!", vep_session->vep.next_sh, vep_handle);
2588               return VPPCOM_EBADFD;
2589             }
2590           ASSERT (next_session->vep.prev_sh == vep_handle);
2591           next_session->vep.prev_sh = session_handle;
2592         }
2593       session->vep.next_sh = vep_session->vep.next_sh;
2594       session->vep.prev_sh = vep_handle;
2595       session->vep.vep_sh = vep_handle;
2596       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2597       session->vep.ev = *event;
2598       session->is_vep = 0;
2599       session->is_vep_session = 1;
2600       vep_session->vep.next_sh = session_handle;
2601
2602       if (session->tx_fifo)
2603         svm_fifo_add_want_deq_ntf (session->tx_fifo,
2604                                    SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2605
2606       /* Generate EPOLLOUT if tx fifo not full */
2607       if ((event->events & EPOLLOUT) &&
2608           (vcl_session_write_ready (session) > 0))
2609         {
2610           session_event_t e = { 0 };
2611           e.event_type = SESSION_IO_EVT_TX;
2612           e.session_index = session->session_index;
2613           vec_add1 (wrk->unhandled_evts_vector, e);
2614         }
2615       /* Generate EPOLLIN if rx fifo has data */
2616       if ((event->events & EPOLLIN) && (vcl_session_read_ready (session) > 0))
2617         {
2618           session_event_t e = { 0 };
2619           e.event_type = SESSION_IO_EVT_RX;
2620           e.session_index = session->session_index;
2621           vec_add1 (wrk->unhandled_evts_vector, e);
2622         }
2623       VDBG (1, "EPOLL_CTL_ADD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2624             vep_handle, session_handle, event->events, event->data.u64);
2625       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
2626       break;
2627
2628     case EPOLL_CTL_MOD:
2629       if (PREDICT_FALSE (!event))
2630         {
2631           VDBG (0, "EPOLL_CTL_MOD: NULL pointer to epoll_event structure!");
2632           rv = VPPCOM_EINVAL;
2633           goto done;
2634         }
2635       else if (PREDICT_FALSE (!session->is_vep_session))
2636         {
2637           VDBG (0, "sh %u EPOLL_CTL_MOD: not a vep session!", session_handle);
2638           rv = VPPCOM_EINVAL;
2639           goto done;
2640         }
2641       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2642         {
2643           VDBG (0, "EPOLL_CTL_MOD: sh %u vep_sh (%u) != vep_sh (%u)!",
2644                 session_handle, session->vep.vep_sh, vep_handle);
2645           rv = VPPCOM_EINVAL;
2646           goto done;
2647         }
2648
2649       /* Generate EPOLLOUT when tx_fifo/ct_tx_fifo not full */
2650       if ((event->events & EPOLLOUT) &&
2651           !(session->vep.ev.events & EPOLLOUT) &&
2652           (vcl_session_write_ready (session) > 0))
2653         {
2654           session_event_t e = { 0 };
2655           e.event_type = SESSION_IO_EVT_TX;
2656           e.session_index = session->session_index;
2657           vec_add1 (wrk->unhandled_evts_vector, e);
2658         }
2659       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2660       session->vep.ev = *event;
2661       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2662             vep_handle, session_handle, event->events, event->data.u64);
2663       break;
2664
2665     case EPOLL_CTL_DEL:
2666       if (PREDICT_FALSE (!session->is_vep_session))
2667         {
2668           VDBG (0, "EPOLL_CTL_DEL: %u not a vep session!", session_handle);
2669           rv = VPPCOM_EINVAL;
2670           goto done;
2671         }
2672       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2673         {
2674           VDBG (0, "EPOLL_CTL_DEL: sh %u vep_sh (%u) != vep_sh (%u)!",
2675                 session_handle, session->vep.vep_sh, vep_handle);
2676           rv = VPPCOM_EINVAL;
2677           goto done;
2678         }
2679
2680       if (session->vep.prev_sh == vep_handle)
2681         vep_session->vep.next_sh = session->vep.next_sh;
2682       else
2683         {
2684           vcl_session_t *prev_session;
2685           prev_session = vcl_session_get_w_handle (wrk, session->vep.prev_sh);
2686           if (PREDICT_FALSE (!prev_session))
2687             {
2688               VDBG (0, "EPOLL_CTL_DEL: Invalid prev_sh (%u) on sh (%u)!",
2689                     session->vep.prev_sh, session_handle);
2690               return VPPCOM_EBADFD;
2691             }
2692           ASSERT (prev_session->vep.next_sh == session_handle);
2693           prev_session->vep.next_sh = session->vep.next_sh;
2694         }
2695       if (session->vep.next_sh != ~0)
2696         {
2697           vcl_session_t *next_session;
2698           next_session = vcl_session_get_w_handle (wrk, session->vep.next_sh);
2699           if (PREDICT_FALSE (!next_session))
2700             {
2701               VDBG (0, "EPOLL_CTL_DEL: Invalid next_sh (%u) on sh (%u)!",
2702                     session->vep.next_sh, session_handle);
2703               return VPPCOM_EBADFD;
2704             }
2705           ASSERT (next_session->vep.prev_sh == session_handle);
2706           next_session->vep.prev_sh = session->vep.prev_sh;
2707         }
2708
2709       memset (&session->vep, 0, sizeof (session->vep));
2710       session->vep.next_sh = ~0;
2711       session->vep.prev_sh = ~0;
2712       session->vep.vep_sh = ~0;
2713       session->is_vep_session = 0;
2714
2715       if (session->tx_fifo)
2716         svm_fifo_del_want_deq_ntf (session->tx_fifo, SVM_FIFO_NO_DEQ_NOTIF);
2717
2718       VDBG (1, "EPOLL_CTL_DEL: vep_idx %u, sh %u!", vep_handle,
2719             session_handle);
2720       vcl_evt (VCL_EVT_EPOLL_CTLDEL, session, vep_sh);
2721       break;
2722
2723     default:
2724       VDBG (0, "Invalid operation (%d)!", op);
2725       rv = VPPCOM_EINVAL;
2726     }
2727
2728   vep_verify_epoll_chain (wrk, vep_handle);
2729
2730 done:
2731   return rv;
2732 }
2733
2734 static inline void
2735 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2736                                 struct epoll_event *events, u32 * num_ev)
2737 {
2738   session_disconnected_msg_t *disconnected_msg;
2739   session_connected_msg_t *connected_msg;
2740   u32 sid = ~0, session_events;
2741   u64 session_evt_data = ~0;
2742   vcl_session_t *session;
2743   u8 add_event = 0;
2744
2745   switch (e->event_type)
2746     {
2747     case SESSION_IO_EVT_RX:
2748       sid = e->session_index;
2749       if (!(session = vcl_session_get (wrk, sid)))
2750         break;
2751       vcl_fifo_rx_evt_valid_or_break (session);
2752       session_events = session->vep.ev.events;
2753       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2754         break;
2755       add_event = 1;
2756       events[*num_ev].events |= EPOLLIN;
2757       session_evt_data = session->vep.ev.data.u64;
2758       session->has_rx_evt = 1;
2759       break;
2760     case SESSION_IO_EVT_TX:
2761       sid = e->session_index;
2762       if (!(session = vcl_session_get (wrk, sid)))
2763         break;
2764       session_events = session->vep.ev.events;
2765       if (!(EPOLLOUT & session_events))
2766         break;
2767       add_event = 1;
2768       events[*num_ev].events |= EPOLLOUT;
2769       session_evt_data = session->vep.ev.data.u64;
2770       svm_fifo_reset_has_deq_ntf (session->tx_fifo);
2771       break;
2772     case SESSION_CTRL_EVT_ACCEPTED:
2773       session = vcl_session_accepted (wrk,
2774                                       (session_accepted_msg_t *) e->data);
2775       if (!session)
2776         break;
2777
2778       session_events = session->vep.ev.events;
2779       if (!(EPOLLIN & session_events))
2780         break;
2781
2782       add_event = 1;
2783       events[*num_ev].events |= EPOLLIN;
2784       session_evt_data = session->vep.ev.data.u64;
2785       break;
2786     case SESSION_CTRL_EVT_CONNECTED:
2787       connected_msg = (session_connected_msg_t *) e->data;
2788       sid = vcl_session_connected_handler (wrk, connected_msg);
2789       /* Generate EPOLLOUT because there's no connected event */
2790       if (!(session = vcl_session_get (wrk, sid)))
2791         break;
2792       session_events = session->vep.ev.events;
2793       if (!(EPOLLOUT & session_events))
2794         break;
2795       add_event = 1;
2796       events[*num_ev].events |= EPOLLOUT;
2797       session_evt_data = session->vep.ev.data.u64;
2798       if (session->session_state & STATE_DETACHED)
2799         events[*num_ev].events |= EPOLLHUP;
2800       break;
2801     case SESSION_CTRL_EVT_DISCONNECTED:
2802       disconnected_msg = (session_disconnected_msg_t *) e->data;
2803       session = vcl_session_disconnected_handler (wrk, disconnected_msg);
2804       if (!session)
2805         break;
2806       session_events = session->vep.ev.events;
2807       add_event = 1;
2808       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2809       session_evt_data = session->vep.ev.data.u64;
2810       break;
2811     case SESSION_CTRL_EVT_RESET:
2812       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2813       if (!(session = vcl_session_get (wrk, sid)))
2814         break;
2815       session_events = session->vep.ev.events;
2816       add_event = 1;
2817       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2818       session_evt_data = session->vep.ev.data.u64;
2819       break;
2820     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
2821       vcl_session_unlisten_reply_handler (wrk, e->data);
2822       break;
2823     case SESSION_CTRL_EVT_MIGRATED:
2824       vcl_session_migrated_handler (wrk, e->data);
2825       break;
2826     case SESSION_CTRL_EVT_CLEANUP:
2827       vcl_session_cleanup_handler (wrk, e->data);
2828       break;
2829     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
2830       vcl_session_req_worker_update_handler (wrk, e->data);
2831       break;
2832     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
2833       vcl_session_worker_update_reply_handler (wrk, e->data);
2834       break;
2835     case SESSION_CTRL_EVT_APP_ADD_SEGMENT:
2836       vcl_session_app_add_segment_handler (wrk, e->data);
2837       break;
2838     case SESSION_CTRL_EVT_APP_DEL_SEGMENT:
2839       vcl_session_app_del_segment_handler (wrk, e->data);
2840       break;
2841     default:
2842       VDBG (0, "unhandled: %u", e->event_type);
2843       break;
2844     }
2845
2846   if (add_event)
2847     {
2848       events[*num_ev].data.u64 = session_evt_data;
2849       if (EPOLLONESHOT & session_events)
2850         {
2851           session = vcl_session_get (wrk, sid);
2852           session->vep.ev.events = 0;
2853         }
2854       *num_ev += 1;
2855     }
2856 }
2857
2858 static int
2859 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2860                           struct epoll_event *events, u32 maxevents,
2861                           double wait_for_time, u32 * num_ev)
2862 {
2863   svm_msg_q_msg_t *msg;
2864   session_event_t *e;
2865   int i;
2866
2867   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
2868     goto handle_dequeued;
2869
2870   svm_msg_q_lock (mq);
2871   if (svm_msg_q_is_empty (mq))
2872     {
2873       if (!wait_for_time)
2874         {
2875           svm_msg_q_unlock (mq);
2876           return 0;
2877         }
2878       else if (wait_for_time < 0)
2879         {
2880           svm_msg_q_wait (mq);
2881         }
2882       else
2883         {
2884           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
2885             {
2886               svm_msg_q_unlock (mq);
2887               return 0;
2888             }
2889         }
2890     }
2891   ASSERT (maxevents > *num_ev);
2892   vcl_mq_dequeue_batch (wrk, mq, maxevents - *num_ev);
2893   svm_msg_q_unlock (mq);
2894
2895 handle_dequeued:
2896   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2897     {
2898       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2899       e = svm_msg_q_msg_data (mq, msg);
2900       vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
2901       svm_msg_q_free_msg (mq, msg);
2902     }
2903   vec_reset_length (wrk->mq_msg_vector);
2904   vcl_handle_pending_wrk_updates (wrk);
2905   return *num_ev;
2906 }
2907
2908 static int
2909 vppcom_epoll_wait_condvar (vcl_worker_t * wrk, struct epoll_event *events,
2910                            int maxevents, u32 n_evts, double wait_for_time)
2911 {
2912   double wait = 0, start = 0, now;
2913
2914   if (!n_evts)
2915     {
2916       wait = wait_for_time;
2917       start = clib_time_now (&wrk->clib_time);
2918     }
2919
2920   do
2921     {
2922       vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events, maxevents,
2923                                 wait, &n_evts);
2924       if (n_evts)
2925         return n_evts;
2926       if (wait == -1)
2927         continue;
2928
2929       now = clib_time_now (&wrk->clib_time);
2930       wait -= now - start;
2931       start = now;
2932     }
2933   while (wait > 0);
2934
2935   return 0;
2936 }
2937
2938 static int
2939 vppcom_epoll_wait_eventfd (vcl_worker_t * wrk, struct epoll_event *events,
2940                            int maxevents, u32 n_evts, double wait_for_time)
2941 {
2942   vcl_mq_evt_conn_t *mqc;
2943   int __clib_unused n_read;
2944   int n_mq_evts, i;
2945   u64 buf;
2946
2947   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2948 again:
2949   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2950                           vec_len (wrk->mq_events), wait_for_time);
2951   for (i = 0; i < n_mq_evts; i++)
2952     {
2953       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2954       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2955       vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0, &n_evts);
2956     }
2957   if (!n_evts && n_mq_evts > 0)
2958     goto again;
2959
2960   return (int) n_evts;
2961 }
2962
2963 int
2964 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
2965                    int maxevents, double wait_for_time)
2966 {
2967   vcl_worker_t *wrk = vcl_worker_get_current ();
2968   vcl_session_t *vep_session;
2969   u32 n_evts = 0;
2970   int i;
2971
2972   if (PREDICT_FALSE (maxevents <= 0))
2973     {
2974       VDBG (0, "ERROR: Invalid maxevents (%d)!", maxevents);
2975       return VPPCOM_EINVAL;
2976     }
2977
2978   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2979   if (!vep_session)
2980     return VPPCOM_EBADFD;
2981
2982   if (PREDICT_FALSE (!vep_session->is_vep))
2983     {
2984       VDBG (0, "ERROR: vep_idx (%u) is not a vep!", vep_handle);
2985       return VPPCOM_EINVAL;
2986     }
2987
2988   memset (events, 0, sizeof (*events) * maxevents);
2989
2990   if (vec_len (wrk->unhandled_evts_vector))
2991     {
2992       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2993         {
2994           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
2995                                           events, &n_evts);
2996           if (n_evts == maxevents)
2997             {
2998               vec_delete (wrk->unhandled_evts_vector, i + 1, 0);
2999               return n_evts;
3000             }
3001         }
3002       vec_reset_length (wrk->unhandled_evts_vector);
3003     }
3004
3005   if (vcm->cfg.use_mq_eventfd)
3006     return vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
3007                                       wait_for_time);
3008
3009   return vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
3010                                     wait_for_time);
3011 }
3012
3013 int
3014 vppcom_session_attr (uint32_t session_handle, uint32_t op,
3015                      void *buffer, uint32_t * buflen)
3016 {
3017   vcl_worker_t *wrk = vcl_worker_get_current ();
3018   vcl_session_t *session;
3019   int rv = VPPCOM_OK;
3020   u32 *flags = buffer, tmp_flags = 0;
3021   vppcom_endpt_t *ep = buffer;
3022
3023   session = vcl_session_get_w_handle (wrk, session_handle);
3024   if (!session)
3025     return VPPCOM_EBADFD;
3026
3027   switch (op)
3028     {
3029     case VPPCOM_ATTR_GET_NREAD:
3030       rv = vcl_session_read_ready (session);
3031       VDBG (2, "VPPCOM_ATTR_GET_NREAD: sh %u, nread = %d", session_handle,
3032             rv);
3033       break;
3034
3035     case VPPCOM_ATTR_GET_NWRITE:
3036       rv = vcl_session_write_ready (session);
3037       VDBG (2, "VPPCOM_ATTR_GET_NWRITE: sh %u, nwrite = %d", session_handle,
3038             rv);
3039       break;
3040
3041     case VPPCOM_ATTR_GET_FLAGS:
3042       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
3043         {
3044           *flags = O_RDWR | (VCL_SESS_ATTR_TEST (session->attr,
3045                                                  VCL_SESS_ATTR_NONBLOCK));
3046           *buflen = sizeof (*flags);
3047           VDBG (2, "VPPCOM_ATTR_GET_FLAGS: sh %u, flags = 0x%08x, "
3048                 "is_nonblocking = %u", session_handle, *flags,
3049                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
3050         }
3051       else
3052         rv = VPPCOM_EINVAL;
3053       break;
3054
3055     case VPPCOM_ATTR_SET_FLAGS:
3056       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
3057         {
3058           if (*flags & O_NONBLOCK)
3059             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
3060           else
3061             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_NONBLOCK);
3062
3063           VDBG (2, "VPPCOM_ATTR_SET_FLAGS: sh %u, flags = 0x%08x,"
3064                 " is_nonblocking = %u", session_handle, *flags,
3065                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
3066         }
3067       else
3068         rv = VPPCOM_EINVAL;
3069       break;
3070
3071     case VPPCOM_ATTR_GET_PEER_ADDR:
3072       if (PREDICT_TRUE (buffer && buflen &&
3073                         (*buflen >= sizeof (*ep)) && ep->ip))
3074         {
3075           ep->is_ip4 = session->transport.is_ip4;
3076           ep->port = session->transport.rmt_port;
3077           if (session->transport.is_ip4)
3078             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3079                               sizeof (ip4_address_t));
3080           else
3081             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3082                               sizeof (ip6_address_t));
3083           *buflen = sizeof (*ep);
3084           VDBG (1, "VPPCOM_ATTR_GET_PEER_ADDR: sh %u, is_ip4 = %u, "
3085                 "addr = %U, port %u", session_handle, ep->is_ip4,
3086                 format_ip46_address, &session->transport.rmt_ip,
3087                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3088                 clib_net_to_host_u16 (ep->port));
3089         }
3090       else
3091         rv = VPPCOM_EINVAL;
3092       break;
3093
3094     case VPPCOM_ATTR_GET_LCL_ADDR:
3095       if (PREDICT_TRUE (buffer && buflen &&
3096                         (*buflen >= sizeof (*ep)) && ep->ip))
3097         {
3098           ep->is_ip4 = session->transport.is_ip4;
3099           ep->port = session->transport.lcl_port;
3100           if (session->transport.is_ip4)
3101             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
3102                               sizeof (ip4_address_t));
3103           else
3104             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
3105                               sizeof (ip6_address_t));
3106           *buflen = sizeof (*ep);
3107           VDBG (1, "VPPCOM_ATTR_GET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3108                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
3109                 &session->transport.lcl_ip,
3110                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3111                 clib_net_to_host_u16 (ep->port));
3112         }
3113       else
3114         rv = VPPCOM_EINVAL;
3115       break;
3116
3117     case VPPCOM_ATTR_SET_LCL_ADDR:
3118       if (PREDICT_TRUE (buffer && buflen &&
3119                         (*buflen >= sizeof (*ep)) && ep->ip))
3120         {
3121           session->transport.is_ip4 = ep->is_ip4;
3122           session->transport.lcl_port = ep->port;
3123           vcl_ip_copy_from_ep (&session->transport.lcl_ip, ep);
3124           *buflen = sizeof (*ep);
3125           VDBG (1, "VPPCOM_ATTR_SET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
3126                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
3127                 &session->transport.lcl_ip,
3128                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
3129                 clib_net_to_host_u16 (ep->port));
3130         }
3131       else
3132         rv = VPPCOM_EINVAL;
3133       break;
3134
3135     case VPPCOM_ATTR_GET_LIBC_EPFD:
3136       rv = session->libc_epfd;
3137       VDBG (2, "VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d", rv);
3138       break;
3139
3140     case VPPCOM_ATTR_SET_LIBC_EPFD:
3141       if (PREDICT_TRUE (buffer && buflen &&
3142                         (*buflen == sizeof (session->libc_epfd))))
3143         {
3144           session->libc_epfd = *(int *) buffer;
3145           *buflen = sizeof (session->libc_epfd);
3146
3147           VDBG (2, "VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, buflen %d",
3148                 session->libc_epfd, *buflen);
3149         }
3150       else
3151         rv = VPPCOM_EINVAL;
3152       break;
3153
3154     case VPPCOM_ATTR_GET_PROTOCOL:
3155       if (buffer && buflen && (*buflen >= sizeof (int)))
3156         {
3157           *(int *) buffer = session->session_type;
3158           *buflen = sizeof (int);
3159
3160           VDBG (2, "VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
3161                 *(int *) buffer, *(int *) buffer ? "UDP" : "TCP", *buflen);
3162         }
3163       else
3164         rv = VPPCOM_EINVAL;
3165       break;
3166
3167     case VPPCOM_ATTR_GET_LISTEN:
3168       if (buffer && buflen && (*buflen >= sizeof (int)))
3169         {
3170           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3171                                                 VCL_SESS_ATTR_LISTEN);
3172           *buflen = sizeof (int);
3173
3174           VDBG (2, "VPPCOM_ATTR_GET_LISTEN: %d, buflen %d", *(int *) buffer,
3175                 *buflen);
3176         }
3177       else
3178         rv = VPPCOM_EINVAL;
3179       break;
3180
3181     case VPPCOM_ATTR_GET_ERROR:
3182       if (buffer && buflen && (*buflen >= sizeof (int)))
3183         {
3184           *(int *) buffer = 0;
3185           *buflen = sizeof (int);
3186
3187           VDBG (2, "VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
3188                 *(int *) buffer, *buflen);
3189         }
3190       else
3191         rv = VPPCOM_EINVAL;
3192       break;
3193
3194     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
3195       if (buffer && buflen && (*buflen >= sizeof (u32)))
3196         {
3197
3198           /* VPP-TBD */
3199           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
3200                                 session->tx_fifo ?
3201                                 svm_fifo_size (session->tx_fifo) :
3202                                 vcm->cfg.tx_fifo_size);
3203           *buflen = sizeof (u32);
3204
3205           VDBG (2, "VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3206                 " #VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer,
3207                 *buflen);
3208         }
3209       else
3210         rv = VPPCOM_EINVAL;
3211       break;
3212
3213     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
3214       if (buffer && buflen && (*buflen == sizeof (u32)))
3215         {
3216           /* VPP-TBD */
3217           session->sndbuf_size = *(u32 *) buffer;
3218           VDBG (2, "VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3219                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3220                 *buflen);
3221         }
3222       else
3223         rv = VPPCOM_EINVAL;
3224       break;
3225
3226     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
3227       if (buffer && buflen && (*buflen >= sizeof (u32)))
3228         {
3229
3230           /* VPP-TBD */
3231           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
3232                                 session->rx_fifo ?
3233                                 svm_fifo_size (session->rx_fifo) :
3234                                 vcm->cfg.rx_fifo_size);
3235           *buflen = sizeof (u32);
3236
3237           VDBG (2, "VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), buflen %d, "
3238                 "#VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer, *buflen);
3239         }
3240       else
3241         rv = VPPCOM_EINVAL;
3242       break;
3243
3244     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
3245       if (buffer && buflen && (*buflen == sizeof (u32)))
3246         {
3247           /* VPP-TBD */
3248           session->rcvbuf_size = *(u32 *) buffer;
3249           VDBG (2, "VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), buflen %d,"
3250                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3251                 *buflen);
3252         }
3253       else
3254         rv = VPPCOM_EINVAL;
3255       break;
3256
3257     case VPPCOM_ATTR_GET_REUSEADDR:
3258       if (buffer && buflen && (*buflen >= sizeof (int)))
3259         {
3260           /* VPP-TBD */
3261           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3262                                                 VCL_SESS_ATTR_REUSEADDR);
3263           *buflen = sizeof (int);
3264
3265           VDBG (2, "VPPCOM_ATTR_GET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3266                 *(int *) buffer, *buflen);
3267         }
3268       else
3269         rv = VPPCOM_EINVAL;
3270       break;
3271
3272     case VPPCOM_ATTR_SET_REUSEADDR:
3273       if (buffer && buflen && (*buflen == sizeof (int)) &&
3274           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
3275         {
3276           /* VPP-TBD */
3277           if (*(int *) buffer)
3278             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEADDR);
3279           else
3280             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEADDR);
3281
3282           VDBG (2, "VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3283                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_REUSEADDR),
3284                 *buflen);
3285         }
3286       else
3287         rv = VPPCOM_EINVAL;
3288       break;
3289
3290     case VPPCOM_ATTR_GET_REUSEPORT:
3291       if (buffer && buflen && (*buflen >= sizeof (int)))
3292         {
3293           /* VPP-TBD */
3294           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3295                                                 VCL_SESS_ATTR_REUSEPORT);
3296           *buflen = sizeof (int);
3297
3298           VDBG (2, "VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3299                 *(int *) buffer, *buflen);
3300         }
3301       else
3302         rv = VPPCOM_EINVAL;
3303       break;
3304
3305     case VPPCOM_ATTR_SET_REUSEPORT:
3306       if (buffer && buflen && (*buflen == sizeof (int)) &&
3307           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
3308         {
3309           /* VPP-TBD */
3310           if (*(int *) buffer)
3311             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEPORT);
3312           else
3313             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEPORT);
3314
3315           VDBG (2, "VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3316                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_REUSEPORT),
3317                 *buflen);
3318         }
3319       else
3320         rv = VPPCOM_EINVAL;
3321       break;
3322
3323     case VPPCOM_ATTR_GET_BROADCAST:
3324       if (buffer && buflen && (*buflen >= sizeof (int)))
3325         {
3326           /* VPP-TBD */
3327           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3328                                                 VCL_SESS_ATTR_BROADCAST);
3329           *buflen = sizeof (int);
3330
3331           VDBG (2, "VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3332                 *(int *) buffer, *buflen);
3333         }
3334       else
3335         rv = VPPCOM_EINVAL;
3336       break;
3337
3338     case VPPCOM_ATTR_SET_BROADCAST:
3339       if (buffer && buflen && (*buflen == sizeof (int)))
3340         {
3341           /* VPP-TBD */
3342           if (*(int *) buffer)
3343             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_BROADCAST);
3344           else
3345             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_BROADCAST);
3346
3347           VDBG (2, "VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3348                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_BROADCAST),
3349                 *buflen);
3350         }
3351       else
3352         rv = VPPCOM_EINVAL;
3353       break;
3354
3355     case VPPCOM_ATTR_GET_V6ONLY:
3356       if (buffer && buflen && (*buflen >= sizeof (int)))
3357         {
3358           /* VPP-TBD */
3359           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3360                                                 VCL_SESS_ATTR_V6ONLY);
3361           *buflen = sizeof (int);
3362
3363           VDBG (2, "VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3364                 *(int *) buffer, *buflen);
3365         }
3366       else
3367         rv = VPPCOM_EINVAL;
3368       break;
3369
3370     case VPPCOM_ATTR_SET_V6ONLY:
3371       if (buffer && buflen && (*buflen == sizeof (int)))
3372         {
3373           /* VPP-TBD */
3374           if (*(int *) buffer)
3375             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_V6ONLY);
3376           else
3377             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_V6ONLY);
3378
3379           VDBG (2, "VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3380                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_V6ONLY),
3381                 *buflen);
3382         }
3383       else
3384         rv = VPPCOM_EINVAL;
3385       break;
3386
3387     case VPPCOM_ATTR_GET_KEEPALIVE:
3388       if (buffer && buflen && (*buflen >= sizeof (int)))
3389         {
3390           /* VPP-TBD */
3391           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3392                                                 VCL_SESS_ATTR_KEEPALIVE);
3393           *buflen = sizeof (int);
3394
3395           VDBG (2, "VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3396                 *(int *) buffer, *buflen);
3397         }
3398       else
3399         rv = VPPCOM_EINVAL;
3400       break;
3401
3402     case VPPCOM_ATTR_SET_KEEPALIVE:
3403       if (buffer && buflen && (*buflen == sizeof (int)))
3404         {
3405           /* VPP-TBD */
3406           if (*(int *) buffer)
3407             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3408           else
3409             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3410
3411           VDBG (2, "VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3412                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_KEEPALIVE),
3413                 *buflen);
3414         }
3415       else
3416         rv = VPPCOM_EINVAL;
3417       break;
3418
3419     case VPPCOM_ATTR_GET_TCP_NODELAY:
3420       if (buffer && buflen && (*buflen >= sizeof (int)))
3421         {
3422           /* VPP-TBD */
3423           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3424                                                 VCL_SESS_ATTR_TCP_NODELAY);
3425           *buflen = sizeof (int);
3426
3427           VDBG (2, "VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3428                 *(int *) buffer, *buflen);
3429         }
3430       else
3431         rv = VPPCOM_EINVAL;
3432       break;
3433
3434     case VPPCOM_ATTR_SET_TCP_NODELAY:
3435       if (buffer && buflen && (*buflen == sizeof (int)))
3436         {
3437           /* VPP-TBD */
3438           if (*(int *) buffer)
3439             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3440           else
3441             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3442
3443           VDBG (2, "VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3444                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_TCP_NODELAY),
3445                 *buflen);
3446         }
3447       else
3448         rv = VPPCOM_EINVAL;
3449       break;
3450
3451     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
3452       if (buffer && buflen && (*buflen >= sizeof (int)))
3453         {
3454           /* VPP-TBD */
3455           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3456                                                 VCL_SESS_ATTR_TCP_KEEPIDLE);
3457           *buflen = sizeof (int);
3458
3459           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3460                 *(int *) buffer, *buflen);
3461         }
3462       else
3463         rv = VPPCOM_EINVAL;
3464       break;
3465
3466     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3467       if (buffer && buflen && (*buflen == sizeof (int)))
3468         {
3469           /* VPP-TBD */
3470           if (*(int *) buffer)
3471             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3472           else
3473             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3474
3475           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3476                 VCL_SESS_ATTR_TEST (session->attr,
3477                                     VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
3478         }
3479       else
3480         rv = VPPCOM_EINVAL;
3481       break;
3482
3483     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
3484       if (buffer && buflen && (*buflen >= sizeof (int)))
3485         {
3486           /* VPP-TBD */
3487           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3488                                                 VCL_SESS_ATTR_TCP_KEEPINTVL);
3489           *buflen = sizeof (int);
3490
3491           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3492                 *(int *) buffer, *buflen);
3493         }
3494       else
3495         rv = VPPCOM_EINVAL;
3496       break;
3497
3498     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3499       if (buffer && buflen && (*buflen == sizeof (int)))
3500         {
3501           /* VPP-TBD */
3502           if (*(int *) buffer)
3503             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3504           else
3505             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3506
3507           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3508                 VCL_SESS_ATTR_TEST (session->attr,
3509                                     VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
3510         }
3511       else
3512         rv = VPPCOM_EINVAL;
3513       break;
3514
3515     case VPPCOM_ATTR_GET_TCP_USER_MSS:
3516       if (buffer && buflen && (*buflen >= sizeof (u32)))
3517         {
3518           /* VPP-TBD */
3519           *(u32 *) buffer = session->user_mss;
3520           *buflen = sizeof (int);
3521
3522           VDBG (2, "VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d, #VPP-TBD#",
3523                 *(int *) buffer, *buflen);
3524         }
3525       else
3526         rv = VPPCOM_EINVAL;
3527       break;
3528
3529     case VPPCOM_ATTR_SET_TCP_USER_MSS:
3530       if (buffer && buflen && (*buflen == sizeof (u32)))
3531         {
3532           /* VPP-TBD */
3533           session->user_mss = *(u32 *) buffer;
3534
3535           VDBG (2, "VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d, #VPP-TBD#",
3536                 session->user_mss, *buflen);
3537         }
3538       else
3539         rv = VPPCOM_EINVAL;
3540       break;
3541
3542     case VPPCOM_ATTR_SET_SHUT:
3543       if (*flags == SHUT_RD || *flags == SHUT_RDWR)
3544         VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_SHUT_RD);
3545       if (*flags == SHUT_WR || *flags == SHUT_RDWR)
3546         VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_SHUT_WR);
3547       break;
3548
3549     case VPPCOM_ATTR_GET_SHUT:
3550       if (VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_SHUT_RD))
3551         tmp_flags = 1;
3552       if (VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_SHUT_WR))
3553         tmp_flags |= 2;
3554       if (tmp_flags == 1)
3555         *(int *) buffer = SHUT_RD;
3556       else if (tmp_flags == 2)
3557         *(int *) buffer = SHUT_WR;
3558       else if (tmp_flags == 3)
3559         *(int *) buffer = SHUT_RDWR;
3560       *buflen = sizeof (int);
3561       break;
3562     default:
3563       rv = VPPCOM_EINVAL;
3564       break;
3565     }
3566
3567   return rv;
3568 }
3569
3570 int
3571 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
3572                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3573 {
3574   vcl_worker_t *wrk = vcl_worker_get_current ();
3575   int rv = VPPCOM_OK;
3576   vcl_session_t *session = 0;
3577
3578   if (ep)
3579     {
3580       session = vcl_session_get_w_handle (wrk, session_handle);
3581       if (PREDICT_FALSE (!session))
3582         {
3583           VDBG (0, "sh 0x%llx is closed!", session_handle);
3584           return VPPCOM_EBADFD;
3585         }
3586       ep->is_ip4 = session->transport.is_ip4;
3587       ep->port = session->transport.rmt_port;
3588     }
3589
3590   if (flags == 0)
3591     rv = vppcom_session_read (session_handle, buffer, buflen);
3592   else if (flags & MSG_PEEK)
3593     rv = vppcom_session_peek (session_handle, buffer, buflen);
3594   else
3595     {
3596       VDBG (0, "Unsupport flags for recvfrom %d", flags);
3597       return VPPCOM_EAFNOSUPPORT;
3598     }
3599
3600   if (ep)
3601     {
3602       if (session->transport.is_ip4)
3603         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3604                           sizeof (ip4_address_t));
3605       else
3606         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3607                           sizeof (ip6_address_t));
3608     }
3609
3610   return rv;
3611 }
3612
3613 int
3614 vppcom_session_sendto (uint32_t session_handle, void *buffer,
3615                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
3616 {
3617   if (!buffer)
3618     return VPPCOM_EINVAL;
3619
3620   if (ep)
3621     {
3622       // TBD
3623       return VPPCOM_EINVAL;
3624     }
3625
3626   if (flags)
3627     {
3628       // TBD check the flags and do the right thing
3629       VDBG (2, "handling flags 0x%u (%d) not implemented yet.", flags, flags);
3630     }
3631
3632   return (vppcom_session_write_inline (session_handle, buffer, buflen, 1));
3633 }
3634
3635 int
3636 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
3637 {
3638   vcl_worker_t *wrk = vcl_worker_get_current ();
3639   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
3640   u32 i, keep_trying = 1;
3641   svm_msg_q_msg_t msg;
3642   session_event_t *e;
3643   int rv, num_ev = 0;
3644
3645   VDBG (3, "vp %p, nsids %u, wait_for_time %f", vp, n_sids, wait_for_time);
3646
3647   if (!vp)
3648     return VPPCOM_EFAULT;
3649
3650   do
3651     {
3652       vcl_session_t *session;
3653
3654       /* Dequeue all events and drop all unhandled io events */
3655       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
3656         {
3657           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
3658           vcl_handle_mq_event (wrk, e);
3659           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
3660         }
3661       vec_reset_length (wrk->unhandled_evts_vector);
3662
3663       for (i = 0; i < n_sids; i++)
3664         {
3665           session = vcl_session_get (wrk, vp[i].sh);
3666           if (!session)
3667             {
3668               vp[i].revents = POLLHUP;
3669               num_ev++;
3670               continue;
3671             }
3672
3673           vp[i].revents = 0;
3674
3675           if (POLLIN & vp[i].events)
3676             {
3677               rv = vcl_session_read_ready (session);
3678               if (rv > 0)
3679                 {
3680                   vp[i].revents |= POLLIN;
3681                   num_ev++;
3682                 }
3683               else if (rv < 0)
3684                 {
3685                   switch (rv)
3686                     {
3687                     case VPPCOM_ECONNRESET:
3688                       vp[i].revents = POLLHUP;
3689                       break;
3690
3691                     default:
3692                       vp[i].revents = POLLERR;
3693                       break;
3694                     }
3695                   num_ev++;
3696                 }
3697             }
3698
3699           if (POLLOUT & vp[i].events)
3700             {
3701               rv = vcl_session_write_ready (session);
3702               if (rv > 0)
3703                 {
3704                   vp[i].revents |= POLLOUT;
3705                   num_ev++;
3706                 }
3707               else if (rv < 0)
3708                 {
3709                   switch (rv)
3710                     {
3711                     case VPPCOM_ECONNRESET:
3712                       vp[i].revents = POLLHUP;
3713                       break;
3714
3715                     default:
3716                       vp[i].revents = POLLERR;
3717                       break;
3718                     }
3719                   num_ev++;
3720                 }
3721             }
3722
3723           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
3724             {
3725               vp[i].revents = POLLNVAL;
3726               num_ev++;
3727             }
3728         }
3729       if (wait_for_time != -1)
3730         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
3731     }
3732   while ((num_ev == 0) && keep_trying);
3733
3734   return num_ev;
3735 }
3736
3737 int
3738 vppcom_mq_epoll_fd (void)
3739 {
3740   vcl_worker_t *wrk = vcl_worker_get_current ();
3741   return wrk->mqs_epfd;
3742 }
3743
3744 int
3745 vppcom_session_index (vcl_session_handle_t session_handle)
3746 {
3747   return session_handle & 0xFFFFFF;
3748 }
3749
3750 int
3751 vppcom_session_worker (vcl_session_handle_t session_handle)
3752 {
3753   return session_handle >> 24;
3754 }
3755
3756 int
3757 vppcom_worker_register (void)
3758 {
3759   vcl_worker_t *wrk;
3760   u8 *wrk_name = 0;
3761   int rv;
3762
3763   if (!vcl_worker_alloc_and_init ())
3764     return VPPCOM_EEXIST;
3765
3766   wrk = vcl_worker_get_current ();
3767   wrk_name = format (0, "%s-wrk-%u", vcm->app_name, wrk->wrk_index);
3768
3769   rv = vppcom_connect_to_vpp ((char *) wrk_name);
3770   vec_free (wrk_name);
3771
3772   if (rv)
3773     return VPPCOM_EFAULT;
3774
3775   if (vcl_worker_register_with_vpp ())
3776     return VPPCOM_EEXIST;
3777
3778   return VPPCOM_OK;
3779 }
3780
3781 void
3782 vppcom_worker_unregister (void)
3783 {
3784   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
3785   vcl_set_worker_index (~0);
3786 }
3787
3788 int
3789 vppcom_worker_index (void)
3790 {
3791   return vcl_get_worker_index ();
3792 }
3793
3794 int
3795 vppcom_worker_mqs_epfd (void)
3796 {
3797   vcl_worker_t *wrk = vcl_worker_get_current ();
3798   if (!vcm->cfg.use_mq_eventfd)
3799     return -1;
3800   return wrk->mqs_epfd;
3801 }
3802
3803 int
3804 vppcom_session_is_connectable_listener (uint32_t session_handle)
3805 {
3806   vcl_session_t *session;
3807   vcl_worker_t *wrk = vcl_worker_get_current ();
3808   session = vcl_session_get_w_handle (wrk, session_handle);
3809   if (!session)
3810     return VPPCOM_EBADFD;
3811   return vcl_session_is_connectable_listener (wrk, session);
3812 }
3813
3814 int
3815 vppcom_session_listener (uint32_t session_handle)
3816 {
3817   vcl_worker_t *wrk = vcl_worker_get_current ();
3818   vcl_session_t *listen_session, *session;
3819   session = vcl_session_get_w_handle (wrk, session_handle);
3820   if (!session)
3821     return VPPCOM_EBADFD;
3822   if (session->listener_index == VCL_INVALID_SESSION_INDEX)
3823     return VPPCOM_EBADFD;
3824   listen_session = vcl_session_get_w_handle (wrk, session->listener_index);
3825   if (!listen_session)
3826     return VPPCOM_EBADFD;
3827   return vcl_session_handle (listen_session);
3828 }
3829
3830 int
3831 vppcom_session_n_accepted (uint32_t session_handle)
3832 {
3833   vcl_worker_t *wrk = vcl_worker_get_current ();
3834   vcl_session_t *session = vcl_session_get_w_handle (wrk, session_handle);
3835   if (!session)
3836     return VPPCOM_EBADFD;
3837   return session->n_accepted_sessions;
3838 }
3839
3840 /*
3841  * fd.io coding-style-patch-verification: ON
3842  *
3843  * Local Variables:
3844  * eval: (c-set-style "gnu")
3845  * End:
3846  */