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