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