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