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 (!_s->rx_fifo))                                       \
1990   break;                                                                \
1991 if (PREDICT_FALSE (svm_fifo_is_empty (_s->rx_fifo)))                    \
1992   {                                                                     \
1993     if (!vcl_session_is_ct (_s))                                        \
1994       {                                                                 \
1995         svm_fifo_unset_event (_s->rx_fifo);                             \
1996         if (svm_fifo_is_empty (_s->rx_fifo))                            \
1997           break;                                                        \
1998       }                                                                 \
1999     else if (svm_fifo_is_empty (_s->ct_rx_fifo))                        \
2000       {                                                                 \
2001         svm_fifo_unset_event (_s->ct_rx_fifo);                          \
2002         if (svm_fifo_is_empty (_s->ct_rx_fifo))                         \
2003           break;                                                        \
2004       }                                                                 \
2005   }                                                                     \
2006
2007 static void
2008 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2009                             unsigned long n_bits, unsigned long *read_map,
2010                             unsigned long *write_map,
2011                             unsigned long *except_map, u32 * bits_set)
2012 {
2013   session_disconnected_msg_t *disconnected_msg;
2014   session_connected_msg_t *connected_msg;
2015   vcl_session_t *session;
2016   u32 sid;
2017
2018   switch (e->event_type)
2019     {
2020     case SESSION_IO_EVT_RX:
2021       sid = e->session_index;
2022       session = vcl_session_get (wrk, sid);
2023       if (!session)
2024         break;
2025       vcl_fifo_rx_evt_valid_or_break (session);
2026       if (sid < n_bits && read_map)
2027         {
2028           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2029           *bits_set += 1;
2030         }
2031       break;
2032     case SESSION_IO_EVT_TX:
2033       sid = e->session_index;
2034       session = vcl_session_get (wrk, sid);
2035       if (!session)
2036         break;
2037       if (sid < n_bits && write_map)
2038         {
2039           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2040           *bits_set += 1;
2041         }
2042       break;
2043     case SESSION_CTRL_EVT_ACCEPTED:
2044       session = vcl_session_accepted (wrk,
2045                                       (session_accepted_msg_t *) e->data);
2046       if (!session)
2047         break;
2048       sid = session->session_index;
2049       if (sid < n_bits && read_map)
2050         {
2051           clib_bitmap_set_no_check ((uword *) read_map, sid, 1);
2052           *bits_set += 1;
2053         }
2054       break;
2055     case SESSION_CTRL_EVT_CONNECTED:
2056       connected_msg = (session_connected_msg_t *) e->data;
2057       sid = vcl_session_connected_handler (wrk, connected_msg);
2058       if (sid == VCL_INVALID_SESSION_INDEX)
2059         break;
2060       if (sid < n_bits && write_map)
2061         {
2062           clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
2063           *bits_set += 1;
2064         }
2065       break;
2066     case SESSION_CTRL_EVT_DISCONNECTED:
2067       disconnected_msg = (session_disconnected_msg_t *) e->data;
2068       session = vcl_session_disconnected_handler (wrk, disconnected_msg);
2069       if (!session)
2070         break;
2071       sid = session->session_index;
2072       if (sid < n_bits && except_map)
2073         {
2074           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2075           *bits_set += 1;
2076         }
2077       break;
2078     case SESSION_CTRL_EVT_RESET:
2079       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2080       if (sid < n_bits && except_map)
2081         {
2082           clib_bitmap_set_no_check ((uword *) except_map, sid, 1);
2083           *bits_set += 1;
2084         }
2085       break;
2086     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
2087       vcl_session_unlisten_reply_handler (wrk, e->data);
2088       break;
2089     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
2090       vcl_session_worker_update_reply_handler (wrk, e->data);
2091       break;
2092     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
2093       vcl_session_req_worker_update_handler (wrk, e->data);
2094       break;
2095     default:
2096       clib_warning ("unhandled: %u", e->event_type);
2097       break;
2098     }
2099 }
2100
2101 static int
2102 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2103                       unsigned long n_bits, unsigned long *read_map,
2104                       unsigned long *write_map, unsigned long *except_map,
2105                       double time_to_wait, u32 * bits_set)
2106 {
2107   svm_msg_q_msg_t *msg;
2108   session_event_t *e;
2109   u32 i;
2110
2111   svm_msg_q_lock (mq);
2112   if (svm_msg_q_is_empty (mq))
2113     {
2114       if (*bits_set)
2115         {
2116           svm_msg_q_unlock (mq);
2117           return 0;
2118         }
2119
2120       if (!time_to_wait)
2121         {
2122           svm_msg_q_unlock (mq);
2123           return 0;
2124         }
2125       else if (time_to_wait < 0)
2126         {
2127           svm_msg_q_wait (mq);
2128         }
2129       else
2130         {
2131           if (svm_msg_q_timedwait (mq, time_to_wait))
2132             {
2133               svm_msg_q_unlock (mq);
2134               return 0;
2135             }
2136         }
2137     }
2138   vcl_mq_dequeue_batch (wrk, mq, ~0);
2139   svm_msg_q_unlock (mq);
2140
2141   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2142     {
2143       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2144       e = svm_msg_q_msg_data (mq, msg);
2145       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
2146                                   except_map, bits_set);
2147       svm_msg_q_free_msg (mq, msg);
2148     }
2149   vec_reset_length (wrk->mq_msg_vector);
2150   vcl_handle_pending_wrk_updates (wrk);
2151   return *bits_set;
2152 }
2153
2154 static int
2155 vppcom_select_condvar (vcl_worker_t * wrk, int n_bits,
2156                        vcl_si_set * read_map, vcl_si_set * write_map,
2157                        vcl_si_set * except_map, double time_to_wait,
2158                        u32 * bits_set)
2159 {
2160   double wait = 0, start = 0;
2161
2162   if (!*bits_set)
2163     {
2164       wait = time_to_wait;
2165       start = clib_time_now (&wrk->clib_time);
2166     }
2167
2168   do
2169     {
2170       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
2171                             write_map, except_map, wait, bits_set);
2172       if (*bits_set)
2173         return *bits_set;
2174       if (wait == -1)
2175         continue;
2176
2177       wait = wait - (clib_time_now (&wrk->clib_time) - start);
2178     }
2179   while (wait > 0);
2180
2181   return 0;
2182 }
2183
2184 static int
2185 vppcom_select_eventfd (vcl_worker_t * wrk, int n_bits,
2186                        vcl_si_set * read_map, vcl_si_set * write_map,
2187                        vcl_si_set * except_map, double time_to_wait,
2188                        u32 * bits_set)
2189 {
2190   vcl_mq_evt_conn_t *mqc;
2191   int __clib_unused n_read;
2192   int n_mq_evts, i;
2193   u64 buf;
2194
2195   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2196   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2197                           vec_len (wrk->mq_events), time_to_wait);
2198   for (i = 0; i < n_mq_evts; i++)
2199     {
2200       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2201       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2202       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
2203                             except_map, 0, bits_set);
2204     }
2205
2206   return (n_mq_evts > 0 ? (int) *bits_set : 0);
2207 }
2208
2209 int
2210 vppcom_select (int n_bits, vcl_si_set * read_map, vcl_si_set * write_map,
2211                vcl_si_set * except_map, double time_to_wait)
2212 {
2213   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
2214   vcl_worker_t *wrk = vcl_worker_get_current ();
2215   vcl_session_t *session = 0;
2216   int rv, i;
2217
2218   if (n_bits && read_map)
2219     {
2220       clib_bitmap_validate (wrk->rd_bitmap, minbits);
2221       clib_memcpy_fast (wrk->rd_bitmap, read_map,
2222                         vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2223       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (vcl_si_set));
2224     }
2225   if (n_bits && write_map)
2226     {
2227       clib_bitmap_validate (wrk->wr_bitmap, minbits);
2228       clib_memcpy_fast (wrk->wr_bitmap, write_map,
2229                         vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2230       memset (write_map, 0, vec_len (wrk->wr_bitmap) * sizeof (vcl_si_set));
2231     }
2232   if (n_bits && except_map)
2233     {
2234       clib_bitmap_validate (wrk->ex_bitmap, minbits);
2235       clib_memcpy_fast (wrk->ex_bitmap, except_map,
2236                         vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2237       memset (except_map, 0, vec_len (wrk->ex_bitmap) * sizeof (vcl_si_set));
2238     }
2239
2240   if (!n_bits)
2241     return 0;
2242
2243   if (!write_map)
2244     goto check_rd;
2245
2246   /* *INDENT-OFF* */
2247   clib_bitmap_foreach (sid, wrk->wr_bitmap, ({
2248     if (!(session = vcl_session_get (wrk, sid)))
2249       {
2250         if (except_map && sid < minbits)
2251           clib_bitmap_set_no_check (except_map, sid, 1);
2252         continue;
2253       }
2254
2255     rv = svm_fifo_is_full_prod (session->tx_fifo);
2256     if (!rv)
2257       {
2258         clib_bitmap_set_no_check ((uword*)write_map, sid, 1);
2259         bits_set++;
2260       }
2261     else
2262       svm_fifo_add_want_deq_ntf (session->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
2263   }));
2264
2265 check_rd:
2266   if (!read_map)
2267     goto check_mq;
2268
2269   clib_bitmap_foreach (sid, wrk->rd_bitmap, ({
2270     if (!(session = vcl_session_get (wrk, sid)))
2271       {
2272         if (except_map && sid < minbits)
2273           clib_bitmap_set_no_check (except_map, sid, 1);
2274         continue;
2275       }
2276
2277     rv = vcl_session_read_ready (session);
2278     if (rv)
2279       {
2280         clib_bitmap_set_no_check ((uword*)read_map, sid, 1);
2281         bits_set++;
2282       }
2283   }));
2284   /* *INDENT-ON* */
2285
2286 check_mq:
2287
2288   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2289     {
2290       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
2291                                   read_map, write_map, except_map, &bits_set);
2292     }
2293   vec_reset_length (wrk->unhandled_evts_vector);
2294
2295   if (vcm->cfg.use_mq_eventfd)
2296     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
2297                            time_to_wait, &bits_set);
2298   else
2299     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
2300                            time_to_wait, &bits_set);
2301
2302   return (bits_set);
2303 }
2304
2305 static inline void
2306 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_handle)
2307 {
2308   vcl_session_t *session;
2309   vppcom_epoll_t *vep;
2310   u32 sh = vep_handle;
2311
2312   if (VPPCOM_DEBUG <= 2)
2313     return;
2314
2315   session = vcl_session_get_w_handle (wrk, vep_handle);
2316   if (PREDICT_FALSE (!session))
2317     {
2318       VDBG (0, "ERROR: Invalid vep_sh (%u)!", vep_handle);
2319       goto done;
2320     }
2321   if (PREDICT_FALSE (!session->is_vep))
2322     {
2323       VDBG (0, "ERROR: vep_sh (%u) is not a vep!", vep_handle);
2324       goto done;
2325     }
2326   vep = &session->vep;
2327   VDBG (0, "vep_sh (%u): Dumping epoll chain\n"
2328         "{\n"
2329         "   is_vep         = %u\n"
2330         "   is_vep_session = %u\n"
2331         "   next_sh        = 0x%x (%u)\n"
2332         "}\n", vep_handle, session->is_vep, session->is_vep_session,
2333         vep->next_sh, vep->next_sh);
2334
2335   for (sh = vep->next_sh; sh != ~0; sh = vep->next_sh)
2336     {
2337       session = vcl_session_get_w_handle (wrk, sh);
2338       if (PREDICT_FALSE (!session))
2339         {
2340           VDBG (0, "ERROR: Invalid sh (%u)!", sh);
2341           goto done;
2342         }
2343       if (PREDICT_FALSE (session->is_vep))
2344         {
2345           VDBG (0, "ERROR: sh (%u) is a vep!", vep_handle);
2346         }
2347       else if (PREDICT_FALSE (!session->is_vep_session))
2348         {
2349           VDBG (0, "ERROR: sh (%u) is not a vep session handle!", sh);
2350           goto done;
2351         }
2352       vep = &session->vep;
2353       if (PREDICT_FALSE (vep->vep_sh != vep_handle))
2354         VDBG (0, "ERROR: session (%u) vep_sh (%u) != vep_sh (%u)!",
2355               sh, session->vep.vep_sh, vep_handle);
2356       if (session->is_vep_session)
2357         {
2358           VDBG (0, "vep_sh[%u]: sh 0x%x (%u)\n"
2359                 "{\n"
2360                 "   next_sh        = 0x%x (%u)\n"
2361                 "   prev_sh        = 0x%x (%u)\n"
2362                 "   vep_sh         = 0x%x (%u)\n"
2363                 "   ev.events      = 0x%x\n"
2364                 "   ev.data.u64    = 0x%llx\n"
2365                 "   et_mask        = 0x%x\n"
2366                 "}\n",
2367                 vep_handle, sh, sh, vep->next_sh, vep->next_sh, vep->prev_sh,
2368                 vep->prev_sh, vep->vep_sh, vep->vep_sh, vep->ev.events,
2369                 vep->ev.data.u64, vep->et_mask);
2370         }
2371     }
2372
2373 done:
2374   VDBG (0, "vep_sh (%u): Dump complete!\n", vep_handle);
2375 }
2376
2377 int
2378 vppcom_epoll_create (void)
2379 {
2380   vcl_worker_t *wrk = vcl_worker_get_current ();
2381   vcl_session_t *vep_session;
2382
2383   vep_session = vcl_session_alloc (wrk);
2384
2385   vep_session->is_vep = 1;
2386   vep_session->vep.vep_sh = ~0;
2387   vep_session->vep.next_sh = ~0;
2388   vep_session->vep.prev_sh = ~0;
2389   vep_session->vpp_handle = ~0;
2390
2391   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_session->session_index);
2392   VDBG (0, "Created vep_idx %u", vep_session->session_index);
2393
2394   return vcl_session_handle (vep_session);
2395 }
2396
2397 int
2398 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2399                   struct epoll_event *event)
2400 {
2401   vcl_worker_t *wrk = vcl_worker_get_current ();
2402   vcl_session_t *vep_session;
2403   vcl_session_t *session;
2404   int rv = VPPCOM_OK;
2405
2406   if (vep_handle == session_handle)
2407     {
2408       VDBG (0, "vep_sh == session handle (%u)!", vep_handle);
2409       return VPPCOM_EINVAL;
2410     }
2411
2412   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2413   if (PREDICT_FALSE (!vep_session))
2414     {
2415       VDBG (0, "Invalid vep_sh (%u)!", vep_handle);
2416       return VPPCOM_EBADFD;
2417     }
2418   if (PREDICT_FALSE (!vep_session->is_vep))
2419     {
2420       VDBG (0, "vep_sh (%u) is not a vep!", vep_handle);
2421       return VPPCOM_EINVAL;
2422     }
2423
2424   ASSERT (vep_session->vep.vep_sh == ~0);
2425   ASSERT (vep_session->vep.prev_sh == ~0);
2426
2427   session = vcl_session_get_w_handle (wrk, session_handle);
2428   if (PREDICT_FALSE (!session))
2429     {
2430       VDBG (0, "Invalid session_handle (%u)!", session_handle);
2431       return VPPCOM_EBADFD;
2432     }
2433   if (PREDICT_FALSE (session->is_vep))
2434     {
2435       VDBG (0, "session_handle (%u) is a vep!", vep_handle);
2436       return VPPCOM_EINVAL;
2437     }
2438
2439   switch (op)
2440     {
2441     case EPOLL_CTL_ADD:
2442       if (PREDICT_FALSE (!event))
2443         {
2444           VDBG (0, "EPOLL_CTL_ADD: NULL pointer to epoll_event structure!");
2445           return VPPCOM_EINVAL;
2446         }
2447       if (vep_session->vep.next_sh != ~0)
2448         {
2449           vcl_session_t *next_session;
2450           next_session = vcl_session_get_w_handle (wrk,
2451                                                    vep_session->vep.next_sh);
2452           if (PREDICT_FALSE (!next_session))
2453             {
2454               VDBG (0, "EPOLL_CTL_ADD: Invalid vep.next_sh (%u) on "
2455                     "vep_idx (%u)!", vep_session->vep.next_sh, vep_handle);
2456               return VPPCOM_EBADFD;
2457             }
2458           ASSERT (next_session->vep.prev_sh == vep_handle);
2459           next_session->vep.prev_sh = session_handle;
2460         }
2461       session->vep.next_sh = vep_session->vep.next_sh;
2462       session->vep.prev_sh = vep_handle;
2463       session->vep.vep_sh = vep_handle;
2464       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2465       session->vep.ev = *event;
2466       session->is_vep = 0;
2467       session->is_vep_session = 1;
2468       vep_session->vep.next_sh = session_handle;
2469
2470       if (session->tx_fifo)
2471         svm_fifo_add_want_deq_ntf (session->tx_fifo,
2472                                    SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL);
2473
2474       VDBG (1, "EPOLL_CTL_ADD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2475             vep_handle, session_handle, event->events, event->data.u64);
2476       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
2477       break;
2478
2479     case EPOLL_CTL_MOD:
2480       if (PREDICT_FALSE (!event))
2481         {
2482           VDBG (0, "EPOLL_CTL_MOD: NULL pointer to epoll_event structure!");
2483           rv = VPPCOM_EINVAL;
2484           goto done;
2485         }
2486       else if (PREDICT_FALSE (!session->is_vep_session))
2487         {
2488           VDBG (0, "sh %u EPOLL_CTL_MOD: not a vep session!", session_handle);
2489           rv = VPPCOM_EINVAL;
2490           goto done;
2491         }
2492       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2493         {
2494           VDBG (0, "EPOLL_CTL_MOD: sh %u vep_sh (%u) != vep_sh (%u)!",
2495                 session_handle, session->vep.vep_sh, vep_handle);
2496           rv = VPPCOM_EINVAL;
2497           goto done;
2498         }
2499       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2500       session->vep.ev = *event;
2501       VDBG (1, "EPOLL_CTL_MOD: vep_sh %u, sh %u, events 0x%x, data 0x%llx!",
2502             vep_handle, session_handle, event->events, event->data.u64);
2503       break;
2504
2505     case EPOLL_CTL_DEL:
2506       if (PREDICT_FALSE (!session->is_vep_session))
2507         {
2508           VDBG (0, "EPOLL_CTL_DEL: %u not a vep session!", session_handle);
2509           rv = VPPCOM_EINVAL;
2510           goto done;
2511         }
2512       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2513         {
2514           VDBG (0, "EPOLL_CTL_DEL: sh %u vep_sh (%u) != vep_sh (%u)!",
2515                 session_handle, session->vep.vep_sh, vep_handle);
2516           rv = VPPCOM_EINVAL;
2517           goto done;
2518         }
2519
2520       if (session->vep.prev_sh == vep_handle)
2521         vep_session->vep.next_sh = session->vep.next_sh;
2522       else
2523         {
2524           vcl_session_t *prev_session;
2525           prev_session = vcl_session_get_w_handle (wrk, session->vep.prev_sh);
2526           if (PREDICT_FALSE (!prev_session))
2527             {
2528               VDBG (0, "EPOLL_CTL_DEL: Invalid prev_sh (%u) on sh (%u)!",
2529                     session->vep.prev_sh, session_handle);
2530               return VPPCOM_EBADFD;
2531             }
2532           ASSERT (prev_session->vep.next_sh == session_handle);
2533           prev_session->vep.next_sh = session->vep.next_sh;
2534         }
2535       if (session->vep.next_sh != ~0)
2536         {
2537           vcl_session_t *next_session;
2538           next_session = vcl_session_get_w_handle (wrk, session->vep.next_sh);
2539           if (PREDICT_FALSE (!next_session))
2540             {
2541               VDBG (0, "EPOLL_CTL_DEL: Invalid next_sh (%u) on sh (%u)!",
2542                     session->vep.next_sh, session_handle);
2543               return VPPCOM_EBADFD;
2544             }
2545           ASSERT (next_session->vep.prev_sh == session_handle);
2546           next_session->vep.prev_sh = session->vep.prev_sh;
2547         }
2548
2549       memset (&session->vep, 0, sizeof (session->vep));
2550       session->vep.next_sh = ~0;
2551       session->vep.prev_sh = ~0;
2552       session->vep.vep_sh = ~0;
2553       session->is_vep_session = 0;
2554
2555       if (session->tx_fifo)
2556         svm_fifo_del_want_deq_ntf (session->tx_fifo, SVM_FIFO_NO_DEQ_NOTIF);
2557
2558       VDBG (1, "EPOLL_CTL_DEL: vep_idx %u, sh %u!", vep_handle,
2559             session_handle);
2560       vcl_evt (VCL_EVT_EPOLL_CTLDEL, session, vep_sh);
2561       break;
2562
2563     default:
2564       VDBG (0, "Invalid operation (%d)!", op);
2565       rv = VPPCOM_EINVAL;
2566     }
2567
2568   vep_verify_epoll_chain (wrk, vep_handle);
2569
2570 done:
2571   return rv;
2572 }
2573
2574 static inline void
2575 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2576                                 struct epoll_event *events, u32 * num_ev)
2577 {
2578   session_disconnected_msg_t *disconnected_msg;
2579   session_connected_msg_t *connected_msg;
2580   u32 sid = ~0, session_events;
2581   u64 session_evt_data = ~0;
2582   vcl_session_t *session;
2583   u8 add_event = 0;
2584
2585   switch (e->event_type)
2586     {
2587     case SESSION_IO_EVT_RX:
2588       sid = e->session_index;
2589       if (!(session = vcl_session_get (wrk, sid)))
2590         break;
2591       vcl_fifo_rx_evt_valid_or_break (session);
2592       session_events = session->vep.ev.events;
2593       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2594         break;
2595       add_event = 1;
2596       events[*num_ev].events |= EPOLLIN;
2597       session_evt_data = session->vep.ev.data.u64;
2598       session->has_rx_evt = 1;
2599       break;
2600     case SESSION_IO_EVT_TX:
2601       sid = e->session_index;
2602       if (!(session = vcl_session_get (wrk, sid)))
2603         break;
2604       session_events = session->vep.ev.events;
2605       if (!(EPOLLOUT & session_events))
2606         break;
2607       add_event = 1;
2608       events[*num_ev].events |= EPOLLOUT;
2609       session_evt_data = session->vep.ev.data.u64;
2610       svm_fifo_reset_has_deq_ntf (session->tx_fifo);
2611       break;
2612     case SESSION_CTRL_EVT_ACCEPTED:
2613       session = vcl_session_accepted (wrk,
2614                                       (session_accepted_msg_t *) e->data);
2615       if (!session)
2616         break;
2617
2618       session_events = session->vep.ev.events;
2619       if (!(EPOLLIN & session_events))
2620         break;
2621
2622       add_event = 1;
2623       events[*num_ev].events |= EPOLLIN;
2624       session_evt_data = session->vep.ev.data.u64;
2625       break;
2626     case SESSION_CTRL_EVT_CONNECTED:
2627       connected_msg = (session_connected_msg_t *) e->data;
2628       vcl_session_connected_handler (wrk, connected_msg);
2629       /* Generate EPOLLOUT because there's no connected event */
2630       sid = vcl_session_index_from_vpp_handle (wrk, connected_msg->handle);
2631       if (!(session = vcl_session_get (wrk, sid)))
2632         break;
2633       session_events = session->vep.ev.events;
2634       if (!(EPOLLOUT & session_events))
2635         break;
2636       add_event = 1;
2637       events[*num_ev].events |= EPOLLOUT;
2638       session_evt_data = session->vep.ev.data.u64;
2639       break;
2640     case SESSION_CTRL_EVT_DISCONNECTED:
2641       disconnected_msg = (session_disconnected_msg_t *) e->data;
2642       session = vcl_session_disconnected_handler (wrk, disconnected_msg);
2643       if (!session)
2644         break;
2645       session_events = session->vep.ev.events;
2646       if (!((EPOLLHUP | EPOLLRDHUP) & session_events))
2647         break;
2648       add_event = 1;
2649       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2650       session_evt_data = session->vep.ev.data.u64;
2651       break;
2652     case SESSION_CTRL_EVT_RESET:
2653       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2654       if (!(session = vcl_session_get (wrk, sid)))
2655         break;
2656       session_events = session->vep.ev.events;
2657       if (!((EPOLLHUP | EPOLLRDHUP) & session_events))
2658         break;
2659       add_event = 1;
2660       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2661       session_evt_data = session->vep.ev.data.u64;
2662       break;
2663     case SESSION_CTRL_EVT_UNLISTEN_REPLY:
2664       vcl_session_unlisten_reply_handler (wrk, e->data);
2665       break;
2666     case SESSION_CTRL_EVT_REQ_WORKER_UPDATE:
2667       vcl_session_req_worker_update_handler (wrk, e->data);
2668       break;
2669     case SESSION_CTRL_EVT_WORKER_UPDATE_REPLY:
2670       vcl_session_worker_update_reply_handler (wrk, e->data);
2671       break;
2672     default:
2673       VDBG (0, "unhandled: %u", e->event_type);
2674       break;
2675     }
2676
2677   if (add_event)
2678     {
2679       events[*num_ev].data.u64 = session_evt_data;
2680       if (EPOLLONESHOT & session_events)
2681         {
2682           session = vcl_session_get (wrk, sid);
2683           session->vep.ev.events = 0;
2684         }
2685       *num_ev += 1;
2686     }
2687 }
2688
2689 static int
2690 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2691                           struct epoll_event *events, u32 maxevents,
2692                           double wait_for_time, u32 * num_ev)
2693 {
2694   svm_msg_q_msg_t *msg;
2695   session_event_t *e;
2696   int i;
2697
2698   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
2699     goto handle_dequeued;
2700
2701   svm_msg_q_lock (mq);
2702   if (svm_msg_q_is_empty (mq))
2703     {
2704       if (!wait_for_time)
2705         {
2706           svm_msg_q_unlock (mq);
2707           return 0;
2708         }
2709       else if (wait_for_time < 0)
2710         {
2711           svm_msg_q_wait (mq);
2712         }
2713       else
2714         {
2715           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
2716             {
2717               svm_msg_q_unlock (mq);
2718               return 0;
2719             }
2720         }
2721     }
2722   ASSERT (maxevents > *num_ev);
2723   vcl_mq_dequeue_batch (wrk, mq, maxevents - *num_ev);
2724   svm_msg_q_unlock (mq);
2725
2726 handle_dequeued:
2727   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2728     {
2729       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2730       e = svm_msg_q_msg_data (mq, msg);
2731       vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
2732       svm_msg_q_free_msg (mq, msg);
2733     }
2734   vec_reset_length (wrk->mq_msg_vector);
2735   vcl_handle_pending_wrk_updates (wrk);
2736   return *num_ev;
2737 }
2738
2739 static int
2740 vppcom_epoll_wait_condvar (vcl_worker_t * wrk, struct epoll_event *events,
2741                            int maxevents, u32 n_evts, double wait_for_time)
2742 {
2743   double wait = 0, start = 0, now;
2744
2745   if (!n_evts)
2746     {
2747       wait = wait_for_time;
2748       start = clib_time_now (&wrk->clib_time);
2749     }
2750
2751   do
2752     {
2753       vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events, maxevents,
2754                                 wait, &n_evts);
2755       if (n_evts)
2756         return n_evts;
2757       if (wait == -1)
2758         continue;
2759
2760       now = clib_time_now (&wrk->clib_time);
2761       wait -= now - start;
2762       start = now;
2763     }
2764   while (wait > 0);
2765
2766   return 0;
2767 }
2768
2769 static int
2770 vppcom_epoll_wait_eventfd (vcl_worker_t * wrk, struct epoll_event *events,
2771                            int maxevents, u32 n_evts, double wait_for_time)
2772 {
2773   vcl_mq_evt_conn_t *mqc;
2774   int __clib_unused n_read;
2775   int n_mq_evts, i;
2776   u64 buf;
2777
2778   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2779 again:
2780   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2781                           vec_len (wrk->mq_events), wait_for_time);
2782   for (i = 0; i < n_mq_evts; i++)
2783     {
2784       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2785       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2786       vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0, &n_evts);
2787     }
2788   if (!n_evts && n_mq_evts > 0)
2789     goto again;
2790
2791   return (int) n_evts;
2792 }
2793
2794 int
2795 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
2796                    int maxevents, double wait_for_time)
2797 {
2798   vcl_worker_t *wrk = vcl_worker_get_current ();
2799   vcl_session_t *vep_session;
2800   u32 n_evts = 0;
2801   int i;
2802
2803   if (PREDICT_FALSE (maxevents <= 0))
2804     {
2805       VDBG (0, "ERROR: Invalid maxevents (%d)!", maxevents);
2806       return VPPCOM_EINVAL;
2807     }
2808
2809   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2810   if (!vep_session)
2811     return VPPCOM_EBADFD;
2812
2813   if (PREDICT_FALSE (!vep_session->is_vep))
2814     {
2815       VDBG (0, "ERROR: vep_idx (%u) is not a vep!", vep_handle);
2816       return VPPCOM_EINVAL;
2817     }
2818
2819   memset (events, 0, sizeof (*events) * maxevents);
2820
2821   if (vec_len (wrk->unhandled_evts_vector))
2822     {
2823       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2824         {
2825           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
2826                                           events, &n_evts);
2827           if (n_evts == maxevents)
2828             {
2829               vec_delete (wrk->unhandled_evts_vector, i + 1, 0);
2830               return n_evts;
2831             }
2832         }
2833       vec_reset_length (wrk->unhandled_evts_vector);
2834     }
2835
2836   if (vcm->cfg.use_mq_eventfd)
2837     return vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
2838                                       wait_for_time);
2839
2840   return vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
2841                                     wait_for_time);
2842 }
2843
2844 int
2845 vppcom_session_attr (uint32_t session_handle, uint32_t op,
2846                      void *buffer, uint32_t * buflen)
2847 {
2848   vcl_worker_t *wrk = vcl_worker_get_current ();
2849   vcl_session_t *session;
2850   int rv = VPPCOM_OK;
2851   u32 *flags = buffer, tmp_flags = 0;
2852   vppcom_endpt_t *ep = buffer;
2853
2854   session = vcl_session_get_w_handle (wrk, session_handle);
2855   if (!session)
2856     return VPPCOM_EBADFD;
2857
2858   switch (op)
2859     {
2860     case VPPCOM_ATTR_GET_NREAD:
2861       rv = vcl_session_read_ready (session);
2862       VDBG (2, "VPPCOM_ATTR_GET_NREAD: sh %u, nread = %d", session_handle,
2863             rv);
2864       break;
2865
2866     case VPPCOM_ATTR_GET_NWRITE:
2867       rv = vcl_session_write_ready (session);
2868       VDBG (2, "VPPCOM_ATTR_GET_NWRITE: sh %u, nwrite = %d", session_handle,
2869             rv);
2870       break;
2871
2872     case VPPCOM_ATTR_GET_FLAGS:
2873       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
2874         {
2875           *flags = O_RDWR | (VCL_SESS_ATTR_TEST (session->attr,
2876                                                  VCL_SESS_ATTR_NONBLOCK));
2877           *buflen = sizeof (*flags);
2878           VDBG (2, "VPPCOM_ATTR_GET_FLAGS: sh %u, flags = 0x%08x, "
2879                 "is_nonblocking = %u", session_handle, *flags,
2880                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2881         }
2882       else
2883         rv = VPPCOM_EINVAL;
2884       break;
2885
2886     case VPPCOM_ATTR_SET_FLAGS:
2887       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
2888         {
2889           if (*flags & O_NONBLOCK)
2890             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
2891           else
2892             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_NONBLOCK);
2893
2894           VDBG (2, "VPPCOM_ATTR_SET_FLAGS: sh %u, flags = 0x%08x,"
2895                 " is_nonblocking = %u", session_handle, *flags,
2896                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2897         }
2898       else
2899         rv = VPPCOM_EINVAL;
2900       break;
2901
2902     case VPPCOM_ATTR_GET_PEER_ADDR:
2903       if (PREDICT_TRUE (buffer && buflen &&
2904                         (*buflen >= sizeof (*ep)) && ep->ip))
2905         {
2906           ep->is_ip4 = session->transport.is_ip4;
2907           ep->port = session->transport.rmt_port;
2908           if (session->transport.is_ip4)
2909             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
2910                               sizeof (ip4_address_t));
2911           else
2912             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
2913                               sizeof (ip6_address_t));
2914           *buflen = sizeof (*ep);
2915           VDBG (1, "VPPCOM_ATTR_GET_PEER_ADDR: sh %u, is_ip4 = %u, "
2916                 "addr = %U, port %u", session_handle, ep->is_ip4,
2917                 format_ip46_address, &session->transport.rmt_ip,
2918                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2919                 clib_net_to_host_u16 (ep->port));
2920         }
2921       else
2922         rv = VPPCOM_EINVAL;
2923       break;
2924
2925     case VPPCOM_ATTR_GET_LCL_ADDR:
2926       if (PREDICT_TRUE (buffer && buflen &&
2927                         (*buflen >= sizeof (*ep)) && ep->ip))
2928         {
2929           ep->is_ip4 = session->transport.is_ip4;
2930           ep->port = session->transport.lcl_port;
2931           if (session->transport.is_ip4)
2932             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
2933                               sizeof (ip4_address_t));
2934           else
2935             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
2936                               sizeof (ip6_address_t));
2937           *buflen = sizeof (*ep);
2938           VDBG (1, "VPPCOM_ATTR_GET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
2939                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
2940                 &session->transport.lcl_ip,
2941                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2942                 clib_net_to_host_u16 (ep->port));
2943         }
2944       else
2945         rv = VPPCOM_EINVAL;
2946       break;
2947
2948     case VPPCOM_ATTR_SET_LCL_ADDR:
2949       if (PREDICT_TRUE (buffer && buflen &&
2950                         (*buflen >= sizeof (*ep)) && ep->ip))
2951         {
2952           session->transport.is_ip4 = ep->is_ip4;
2953           session->transport.lcl_port = ep->port;
2954           vcl_ip_copy_from_ep (&session->transport.lcl_ip, ep);
2955           *buflen = sizeof (*ep);
2956           VDBG (1, "VPPCOM_ATTR_SET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
2957                 " port %d", session_handle, ep->is_ip4, format_ip46_address,
2958                 &session->transport.lcl_ip,
2959                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2960                 clib_net_to_host_u16 (ep->port));
2961         }
2962       else
2963         rv = VPPCOM_EINVAL;
2964       break;
2965
2966     case VPPCOM_ATTR_GET_LIBC_EPFD:
2967       rv = session->libc_epfd;
2968       VDBG (2, "VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d", rv);
2969       break;
2970
2971     case VPPCOM_ATTR_SET_LIBC_EPFD:
2972       if (PREDICT_TRUE (buffer && buflen &&
2973                         (*buflen == sizeof (session->libc_epfd))))
2974         {
2975           session->libc_epfd = *(int *) buffer;
2976           *buflen = sizeof (session->libc_epfd);
2977
2978           VDBG (2, "VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, buflen %d",
2979                 session->libc_epfd, *buflen);
2980         }
2981       else
2982         rv = VPPCOM_EINVAL;
2983       break;
2984
2985     case VPPCOM_ATTR_GET_PROTOCOL:
2986       if (buffer && buflen && (*buflen >= sizeof (int)))
2987         {
2988           *(int *) buffer = session->session_type;
2989           *buflen = sizeof (int);
2990
2991           VDBG (2, "VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
2992                 *(int *) buffer, *(int *) buffer ? "UDP" : "TCP", *buflen);
2993         }
2994       else
2995         rv = VPPCOM_EINVAL;
2996       break;
2997
2998     case VPPCOM_ATTR_GET_LISTEN:
2999       if (buffer && buflen && (*buflen >= sizeof (int)))
3000         {
3001           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3002                                                 VCL_SESS_ATTR_LISTEN);
3003           *buflen = sizeof (int);
3004
3005           VDBG (2, "VPPCOM_ATTR_GET_LISTEN: %d, buflen %d", *(int *) buffer,
3006                 *buflen);
3007         }
3008       else
3009         rv = VPPCOM_EINVAL;
3010       break;
3011
3012     case VPPCOM_ATTR_GET_ERROR:
3013       if (buffer && buflen && (*buflen >= sizeof (int)))
3014         {
3015           *(int *) buffer = 0;
3016           *buflen = sizeof (int);
3017
3018           VDBG (2, "VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
3019                 *(int *) buffer, *buflen);
3020         }
3021       else
3022         rv = VPPCOM_EINVAL;
3023       break;
3024
3025     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
3026       if (buffer && buflen && (*buflen >= sizeof (u32)))
3027         {
3028
3029           /* VPP-TBD */
3030           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
3031                                 session->tx_fifo ? session->tx_fifo->nitems :
3032                                 vcm->cfg.tx_fifo_size);
3033           *buflen = sizeof (u32);
3034
3035           VDBG (2, "VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3036                 " #VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer,
3037                 *buflen);
3038         }
3039       else
3040         rv = VPPCOM_EINVAL;
3041       break;
3042
3043     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
3044       if (buffer && buflen && (*buflen == sizeof (u32)))
3045         {
3046           /* VPP-TBD */
3047           session->sndbuf_size = *(u32 *) buffer;
3048           VDBG (2, "VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), buflen %d,"
3049                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3050                 *buflen);
3051         }
3052       else
3053         rv = VPPCOM_EINVAL;
3054       break;
3055
3056     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
3057       if (buffer && buflen && (*buflen >= sizeof (u32)))
3058         {
3059
3060           /* VPP-TBD */
3061           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
3062                                 session->rx_fifo ? session->rx_fifo->nitems :
3063                                 vcm->cfg.rx_fifo_size);
3064           *buflen = sizeof (u32);
3065
3066           VDBG (2, "VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), buflen %d, "
3067                 "#VPP-TBD#", *(size_t *) buffer, *(size_t *) buffer, *buflen);
3068         }
3069       else
3070         rv = VPPCOM_EINVAL;
3071       break;
3072
3073     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
3074       if (buffer && buflen && (*buflen == sizeof (u32)))
3075         {
3076           /* VPP-TBD */
3077           session->rcvbuf_size = *(u32 *) buffer;
3078           VDBG (2, "VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), buflen %d,"
3079                 " #VPP-TBD#", session->sndbuf_size, session->sndbuf_size,
3080                 *buflen);
3081         }
3082       else
3083         rv = VPPCOM_EINVAL;
3084       break;
3085
3086     case VPPCOM_ATTR_GET_REUSEADDR:
3087       if (buffer && buflen && (*buflen >= sizeof (int)))
3088         {
3089           /* VPP-TBD */
3090           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3091                                                 VCL_SESS_ATTR_REUSEADDR);
3092           *buflen = sizeof (int);
3093
3094           VDBG (2, "VPPCOM_ATTR_GET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3095                 *(int *) buffer, *buflen);
3096         }
3097       else
3098         rv = VPPCOM_EINVAL;
3099       break;
3100
3101     case VPPCOM_ATTR_SET_REUSEADDR:
3102       if (buffer && buflen && (*buflen == sizeof (int)) &&
3103           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
3104         {
3105           /* VPP-TBD */
3106           if (*(int *) buffer)
3107             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEADDR);
3108           else
3109             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEADDR);
3110
3111           VDBG (2, "VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d, #VPP-TBD#",
3112                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_REUSEADDR),
3113                 *buflen);
3114         }
3115       else
3116         rv = VPPCOM_EINVAL;
3117       break;
3118
3119     case VPPCOM_ATTR_GET_REUSEPORT:
3120       if (buffer && buflen && (*buflen >= sizeof (int)))
3121         {
3122           /* VPP-TBD */
3123           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3124                                                 VCL_SESS_ATTR_REUSEPORT);
3125           *buflen = sizeof (int);
3126
3127           VDBG (2, "VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3128                 *(int *) buffer, *buflen);
3129         }
3130       else
3131         rv = VPPCOM_EINVAL;
3132       break;
3133
3134     case VPPCOM_ATTR_SET_REUSEPORT:
3135       if (buffer && buflen && (*buflen == sizeof (int)) &&
3136           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
3137         {
3138           /* VPP-TBD */
3139           if (*(int *) buffer)
3140             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEPORT);
3141           else
3142             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEPORT);
3143
3144           VDBG (2, "VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d, #VPP-TBD#",
3145                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_REUSEPORT),
3146                 *buflen);
3147         }
3148       else
3149         rv = VPPCOM_EINVAL;
3150       break;
3151
3152     case VPPCOM_ATTR_GET_BROADCAST:
3153       if (buffer && buflen && (*buflen >= sizeof (int)))
3154         {
3155           /* VPP-TBD */
3156           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3157                                                 VCL_SESS_ATTR_BROADCAST);
3158           *buflen = sizeof (int);
3159
3160           VDBG (2, "VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3161                 *(int *) buffer, *buflen);
3162         }
3163       else
3164         rv = VPPCOM_EINVAL;
3165       break;
3166
3167     case VPPCOM_ATTR_SET_BROADCAST:
3168       if (buffer && buflen && (*buflen == sizeof (int)))
3169         {
3170           /* VPP-TBD */
3171           if (*(int *) buffer)
3172             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_BROADCAST);
3173           else
3174             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_BROADCAST);
3175
3176           VDBG (2, "VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, #VPP-TBD#",
3177                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_BROADCAST),
3178                 *buflen);
3179         }
3180       else
3181         rv = VPPCOM_EINVAL;
3182       break;
3183
3184     case VPPCOM_ATTR_GET_V6ONLY:
3185       if (buffer && buflen && (*buflen >= sizeof (int)))
3186         {
3187           /* VPP-TBD */
3188           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3189                                                 VCL_SESS_ATTR_V6ONLY);
3190           *buflen = sizeof (int);
3191
3192           VDBG (2, "VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3193                 *(int *) buffer, *buflen);
3194         }
3195       else
3196         rv = VPPCOM_EINVAL;
3197       break;
3198
3199     case VPPCOM_ATTR_SET_V6ONLY:
3200       if (buffer && buflen && (*buflen == sizeof (int)))
3201         {
3202           /* VPP-TBD */
3203           if (*(int *) buffer)
3204             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_V6ONLY);
3205           else
3206             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_V6ONLY);
3207
3208           VDBG (2, "VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, #VPP-TBD#",
3209                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_V6ONLY),
3210                 *buflen);
3211         }
3212       else
3213         rv = VPPCOM_EINVAL;
3214       break;
3215
3216     case VPPCOM_ATTR_GET_KEEPALIVE:
3217       if (buffer && buflen && (*buflen >= sizeof (int)))
3218         {
3219           /* VPP-TBD */
3220           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3221                                                 VCL_SESS_ATTR_KEEPALIVE);
3222           *buflen = sizeof (int);
3223
3224           VDBG (2, "VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3225                 *(int *) buffer, *buflen);
3226         }
3227       else
3228         rv = VPPCOM_EINVAL;
3229       break;
3230
3231     case VPPCOM_ATTR_SET_KEEPALIVE:
3232       if (buffer && buflen && (*buflen == sizeof (int)))
3233         {
3234           /* VPP-TBD */
3235           if (*(int *) buffer)
3236             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3237           else
3238             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3239
3240           VDBG (2, "VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, #VPP-TBD#",
3241                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_KEEPALIVE),
3242                 *buflen);
3243         }
3244       else
3245         rv = VPPCOM_EINVAL;
3246       break;
3247
3248     case VPPCOM_ATTR_GET_TCP_NODELAY:
3249       if (buffer && buflen && (*buflen >= sizeof (int)))
3250         {
3251           /* VPP-TBD */
3252           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3253                                                 VCL_SESS_ATTR_TCP_NODELAY);
3254           *buflen = sizeof (int);
3255
3256           VDBG (2, "VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3257                 *(int *) buffer, *buflen);
3258         }
3259       else
3260         rv = VPPCOM_EINVAL;
3261       break;
3262
3263     case VPPCOM_ATTR_SET_TCP_NODELAY:
3264       if (buffer && buflen && (*buflen == sizeof (int)))
3265         {
3266           /* VPP-TBD */
3267           if (*(int *) buffer)
3268             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3269           else
3270             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3271
3272           VDBG (2, "VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, #VPP-TBD#",
3273                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_TCP_NODELAY),
3274                 *buflen);
3275         }
3276       else
3277         rv = VPPCOM_EINVAL;
3278       break;
3279
3280     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
3281       if (buffer && buflen && (*buflen >= sizeof (int)))
3282         {
3283           /* VPP-TBD */
3284           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3285                                                 VCL_SESS_ATTR_TCP_KEEPIDLE);
3286           *buflen = sizeof (int);
3287
3288           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3289                 *(int *) buffer, *buflen);
3290         }
3291       else
3292         rv = VPPCOM_EINVAL;
3293       break;
3294
3295     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3296       if (buffer && buflen && (*buflen == sizeof (int)))
3297         {
3298           /* VPP-TBD */
3299           if (*(int *) buffer)
3300             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3301           else
3302             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3303
3304           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, #VPP-TBD#",
3305                 VCL_SESS_ATTR_TEST (session->attr,
3306                                     VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
3307         }
3308       else
3309         rv = VPPCOM_EINVAL;
3310       break;
3311
3312     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
3313       if (buffer && buflen && (*buflen >= sizeof (int)))
3314         {
3315           /* VPP-TBD */
3316           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3317                                                 VCL_SESS_ATTR_TCP_KEEPINTVL);
3318           *buflen = sizeof (int);
3319
3320           VDBG (2, "VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3321                 *(int *) buffer, *buflen);
3322         }
3323       else
3324         rv = VPPCOM_EINVAL;
3325       break;
3326
3327     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3328       if (buffer && buflen && (*buflen == sizeof (int)))
3329         {
3330           /* VPP-TBD */
3331           if (*(int *) buffer)
3332             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3333           else
3334             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3335
3336           VDBG (2, "VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, #VPP-TBD#",
3337                 VCL_SESS_ATTR_TEST (session->attr,
3338                                     VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
3339         }
3340       else
3341         rv = VPPCOM_EINVAL;
3342       break;
3343
3344     case VPPCOM_ATTR_GET_TCP_USER_MSS:
3345       if (buffer && buflen && (*buflen >= sizeof (u32)))
3346         {
3347           /* VPP-TBD */
3348           *(u32 *) buffer = session->user_mss;
3349           *buflen = sizeof (int);
3350
3351           VDBG (2, "VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d, #VPP-TBD#",
3352                 *(int *) buffer, *buflen);
3353         }
3354       else
3355         rv = VPPCOM_EINVAL;
3356       break;
3357
3358     case VPPCOM_ATTR_SET_TCP_USER_MSS:
3359       if (buffer && buflen && (*buflen == sizeof (u32)))
3360         {
3361           /* VPP-TBD */
3362           session->user_mss = *(u32 *) buffer;
3363
3364           VDBG (2, "VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d, #VPP-TBD#",
3365                 session->user_mss, *buflen);
3366         }
3367       else
3368         rv = VPPCOM_EINVAL;
3369       break;
3370
3371     case VPPCOM_ATTR_SET_SHUT:
3372       if (*flags == SHUT_RD || *flags == SHUT_RDWR)
3373         VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_SHUT_RD);
3374       if (*flags == SHUT_WR || *flags == SHUT_RDWR)
3375         VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_SHUT_WR);
3376       break;
3377
3378     case VPPCOM_ATTR_GET_SHUT:
3379       if (VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_SHUT_RD))
3380         tmp_flags = 1;
3381       if (VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_SHUT_WR))
3382         tmp_flags |= 2;
3383       if (tmp_flags == 1)
3384         *(int *) buffer = SHUT_RD;
3385       else if (tmp_flags == 2)
3386         *(int *) buffer = SHUT_WR;
3387       else if (tmp_flags == 3)
3388         *(int *) buffer = SHUT_RDWR;
3389       *buflen = sizeof (int);
3390       break;
3391     default:
3392       rv = VPPCOM_EINVAL;
3393       break;
3394     }
3395
3396   return rv;
3397 }
3398
3399 int
3400 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
3401                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3402 {
3403   vcl_worker_t *wrk = vcl_worker_get_current ();
3404   int rv = VPPCOM_OK;
3405   vcl_session_t *session = 0;
3406
3407   if (ep)
3408     {
3409       session = vcl_session_get_w_handle (wrk, session_handle);
3410       if (PREDICT_FALSE (!session))
3411         {
3412           VDBG (0, "sh 0x%llx is closed!", session_handle);
3413           return VPPCOM_EBADFD;
3414         }
3415       ep->is_ip4 = session->transport.is_ip4;
3416       ep->port = session->transport.rmt_port;
3417     }
3418
3419   if (flags == 0)
3420     rv = vppcom_session_read (session_handle, buffer, buflen);
3421   else if (flags & MSG_PEEK)
3422     rv = vppcom_session_peek (session_handle, buffer, buflen);
3423   else
3424     {
3425       VDBG (0, "Unsupport flags for recvfrom %d", flags);
3426       return VPPCOM_EAFNOSUPPORT;
3427     }
3428
3429   if (ep)
3430     {
3431       if (session->transport.is_ip4)
3432         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3433                           sizeof (ip4_address_t));
3434       else
3435         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3436                           sizeof (ip6_address_t));
3437     }
3438
3439   return rv;
3440 }
3441
3442 int
3443 vppcom_session_sendto (uint32_t session_handle, void *buffer,
3444                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
3445 {
3446   if (!buffer)
3447     return VPPCOM_EINVAL;
3448
3449   if (ep)
3450     {
3451       // TBD
3452       return VPPCOM_EINVAL;
3453     }
3454
3455   if (flags)
3456     {
3457       // TBD check the flags and do the right thing
3458       VDBG (2, "handling flags 0x%u (%d) not implemented yet.", flags, flags);
3459     }
3460
3461   return (vppcom_session_write_inline (session_handle, buffer, buflen, 1));
3462 }
3463
3464 int
3465 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
3466 {
3467   vcl_worker_t *wrk = vcl_worker_get_current ();
3468   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
3469   u32 i, keep_trying = 1;
3470   svm_msg_q_msg_t msg;
3471   session_event_t *e;
3472   int rv, num_ev = 0;
3473
3474   VDBG (3, "vp %p, nsids %u, wait_for_time %f", vp, n_sids, wait_for_time);
3475
3476   if (!vp)
3477     return VPPCOM_EFAULT;
3478
3479   do
3480     {
3481       vcl_session_t *session;
3482
3483       /* Dequeue all events and drop all unhandled io events */
3484       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
3485         {
3486           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
3487           vcl_handle_mq_event (wrk, e);
3488           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
3489         }
3490       vec_reset_length (wrk->unhandled_evts_vector);
3491
3492       for (i = 0; i < n_sids; i++)
3493         {
3494           session = vcl_session_get (wrk, vp[i].sh);
3495           if (!session)
3496             {
3497               vp[i].revents = POLLHUP;
3498               num_ev++;
3499               continue;
3500             }
3501
3502           vp[i].revents = 0;
3503
3504           if (POLLIN & vp[i].events)
3505             {
3506               rv = vcl_session_read_ready (session);
3507               if (rv > 0)
3508                 {
3509                   vp[i].revents |= POLLIN;
3510                   num_ev++;
3511                 }
3512               else if (rv < 0)
3513                 {
3514                   switch (rv)
3515                     {
3516                     case VPPCOM_ECONNRESET:
3517                       vp[i].revents = POLLHUP;
3518                       break;
3519
3520                     default:
3521                       vp[i].revents = POLLERR;
3522                       break;
3523                     }
3524                   num_ev++;
3525                 }
3526             }
3527
3528           if (POLLOUT & vp[i].events)
3529             {
3530               rv = vcl_session_write_ready (session);
3531               if (rv > 0)
3532                 {
3533                   vp[i].revents |= POLLOUT;
3534                   num_ev++;
3535                 }
3536               else if (rv < 0)
3537                 {
3538                   switch (rv)
3539                     {
3540                     case VPPCOM_ECONNRESET:
3541                       vp[i].revents = POLLHUP;
3542                       break;
3543
3544                     default:
3545                       vp[i].revents = POLLERR;
3546                       break;
3547                     }
3548                   num_ev++;
3549                 }
3550             }
3551
3552           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
3553             {
3554               vp[i].revents = POLLNVAL;
3555               num_ev++;
3556             }
3557         }
3558       if (wait_for_time != -1)
3559         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
3560     }
3561   while ((num_ev == 0) && keep_trying);
3562
3563   return num_ev;
3564 }
3565
3566 int
3567 vppcom_mq_epoll_fd (void)
3568 {
3569   vcl_worker_t *wrk = vcl_worker_get_current ();
3570   return wrk->mqs_epfd;
3571 }
3572
3573 int
3574 vppcom_session_index (vcl_session_handle_t session_handle)
3575 {
3576   return session_handle & 0xFFFFFF;
3577 }
3578
3579 int
3580 vppcom_session_worker (vcl_session_handle_t session_handle)
3581 {
3582   return session_handle >> 24;
3583 }
3584
3585 int
3586 vppcom_worker_register (void)
3587 {
3588   if (!vcl_worker_alloc_and_init ())
3589     return VPPCOM_EEXIST;
3590
3591   if (vcl_worker_set_bapi ())
3592     return VPPCOM_EEXIST;
3593
3594   if (vcl_worker_register_with_vpp ())
3595     return VPPCOM_EEXIST;
3596
3597   return VPPCOM_OK;
3598 }
3599
3600 void
3601 vppcom_worker_unregister (void)
3602 {
3603   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
3604   vcl_set_worker_index (~0);
3605 }
3606
3607 int
3608 vppcom_worker_index (void)
3609 {
3610   return vcl_get_worker_index ();
3611 }
3612
3613 int
3614 vppcom_worker_mqs_epfd (void)
3615 {
3616   vcl_worker_t *wrk = vcl_worker_get_current ();
3617   if (!vcm->cfg.use_mq_eventfd)
3618     return -1;
3619   return wrk->mqs_epfd;
3620 }
3621
3622 int
3623 vppcom_session_is_connectable_listener (uint32_t session_handle)
3624 {
3625   vcl_session_t *session;
3626   vcl_worker_t *wrk = vcl_worker_get_current ();
3627   session = vcl_session_get_w_handle (wrk, session_handle);
3628   if (!session)
3629     return VPPCOM_EBADFD;
3630   return vcl_session_is_connectable_listener (wrk, session);
3631 }
3632
3633 int
3634 vppcom_session_listener (uint32_t session_handle)
3635 {
3636   vcl_worker_t *wrk = vcl_worker_get_current ();
3637   vcl_session_t *listen_session, *session;
3638   session = vcl_session_get_w_handle (wrk, session_handle);
3639   if (!session)
3640     return VPPCOM_EBADFD;
3641   if (session->listener_index == VCL_INVALID_SESSION_INDEX)
3642     return VPPCOM_EBADFD;
3643   listen_session = vcl_session_get_w_handle (wrk, session->listener_index);
3644   if (!listen_session)
3645     return VPPCOM_EBADFD;
3646   return vcl_session_handle (listen_session);
3647 }
3648
3649 int
3650 vppcom_session_n_accepted (uint32_t session_handle)
3651 {
3652   vcl_worker_t *wrk = vcl_worker_get_current ();
3653   vcl_session_t *session = vcl_session_get_w_handle (wrk, session_handle);
3654   if (!session)
3655     return VPPCOM_EBADFD;
3656   return session->n_accepted_sessions;
3657 }
3658
3659 /*
3660  * fd.io coding-style-patch-verification: ON
3661  *
3662  * Local Variables:
3663  * eval: (c-set-style "gnu")
3664  * End:
3665  */