Add config option to use dlmalloc instead of mheap
[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_event.h>
21 #include <vcl/vcl_debug.h>
22 #include <vcl/vcl_private.h>
23
24 static const char *
25 vppcom_app_state_str (app_state_t state)
26 {
27   char *st;
28
29   switch (state)
30     {
31     case STATE_APP_START:
32       st = "STATE_APP_START";
33       break;
34
35     case STATE_APP_CONN_VPP:
36       st = "STATE_APP_CONN_VPP";
37       break;
38
39     case STATE_APP_ENABLED:
40       st = "STATE_APP_ENABLED";
41       break;
42
43     case STATE_APP_ATTACHED:
44       st = "STATE_APP_ATTACHED";
45       break;
46
47     default:
48       st = "UNKNOWN_APP_STATE";
49       break;
50     }
51
52   return st;
53 }
54
55 const char *
56 vppcom_session_state_str (session_state_t state)
57 {
58   char *st;
59
60   switch (state)
61     {
62     case STATE_START:
63       st = "STATE_START";
64       break;
65
66     case STATE_CONNECT:
67       st = "STATE_CONNECT";
68       break;
69
70     case STATE_LISTEN:
71       st = "STATE_LISTEN";
72       break;
73
74     case STATE_ACCEPT:
75       st = "STATE_ACCEPT";
76       break;
77
78     case STATE_CLOSE_ON_EMPTY:
79       st = "STATE_CLOSE_ON_EMPTY";
80       break;
81
82     case STATE_DISCONNECT:
83       st = "STATE_DISCONNECT";
84       break;
85
86     case STATE_FAILED:
87       st = "STATE_FAILED";
88       break;
89
90     default:
91       st = "UNKNOWN_STATE";
92       break;
93     }
94
95   return st;
96 }
97
98 u8 *
99 format_ip4_address (u8 * s, va_list * args)
100 {
101   u8 *a = va_arg (*args, u8 *);
102   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
103 }
104
105 u8 *
106 format_ip6_address (u8 * s, va_list * args)
107 {
108   ip6_address_t *a = va_arg (*args, ip6_address_t *);
109   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
110
111   i_max_n_zero = ARRAY_LEN (a->as_u16);
112   max_n_zeros = 0;
113   i_first_zero = i_max_n_zero;
114   n_zeros = 0;
115   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
116     {
117       u32 is_zero = a->as_u16[i] == 0;
118       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
119         {
120           i_first_zero = i;
121           n_zeros = 0;
122         }
123       n_zeros += is_zero;
124       if ((!is_zero && n_zeros > max_n_zeros)
125           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
126         {
127           i_max_n_zero = i_first_zero;
128           max_n_zeros = n_zeros;
129           i_first_zero = ARRAY_LEN (a->as_u16);
130           n_zeros = 0;
131         }
132     }
133
134   last_double_colon = 0;
135   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
136     {
137       if (i == i_max_n_zero && max_n_zeros > 1)
138         {
139           s = format (s, "::");
140           i += max_n_zeros - 1;
141           last_double_colon = 1;
142         }
143       else
144         {
145           s = format (s, "%s%x",
146                       (last_double_colon || i == 0) ? "" : ":",
147                       clib_net_to_host_u16 (a->as_u16[i]));
148           last_double_colon = 0;
149         }
150     }
151
152   return s;
153 }
154
155 /* Format an IP46 address. */
156 u8 *
157 format_ip46_address (u8 * s, va_list * args)
158 {
159   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
160   ip46_type_t type = va_arg (*args, ip46_type_t);
161   int is_ip4 = 1;
162
163   switch (type)
164     {
165     case IP46_TYPE_ANY:
166       is_ip4 = ip46_address_is_ip4 (ip46);
167       break;
168     case IP46_TYPE_IP4:
169       is_ip4 = 1;
170       break;
171     case IP46_TYPE_IP6:
172       is_ip4 = 0;
173       break;
174     }
175
176   return is_ip4 ?
177     format (s, "%U", format_ip4_address, &ip46->ip4) :
178     format (s, "%U", format_ip6_address, &ip46->ip6);
179 }
180
181 /*
182  * VPPCOM Utility Functions
183  */
184
185 static inline void
186 vppcom_session_table_del_listener (u64 listener_handle)
187 {
188   listener_handle |= 1ULL << 63;
189   hash_unset (vcm->session_index_by_vpp_handles, listener_handle);
190 }
191
192 static inline int
193 vppcom_wait_for_app_state_change (app_state_t app_state)
194 {
195   f64 timeout = clib_time_now (&vcm->clib_time) + vcm->cfg.app_timeout;
196
197   while (clib_time_now (&vcm->clib_time) < timeout)
198     {
199       if (vcm->app_state == app_state)
200         return VPPCOM_OK;
201     }
202   VDBG (0, "VCL<%d>: timeout waiting for state %s (%d)", getpid (),
203         vppcom_app_state_str (app_state), app_state);
204   vcl_evt (VCL_EVT_SESSION_TIMEOUT, vcm, app_state);
205
206   return VPPCOM_ETIMEDOUT;
207 }
208
209 static inline int
210 vppcom_wait_for_session_state_change (u32 session_index,
211                                       session_state_t state,
212                                       f64 wait_for_time)
213 {
214   f64 timeout = clib_time_now (&vcm->clib_time) + wait_for_time;
215   vcl_session_t *volatile session;
216   int rv;
217
218   do
219     {
220       VCL_SESSION_LOCK ();
221       rv = vppcom_session_at_index (session_index, &session);
222       if (PREDICT_FALSE (rv))
223         {
224           VCL_SESSION_UNLOCK ();
225           return rv;
226         }
227       if (session->session_state & state)
228         {
229           VCL_SESSION_UNLOCK ();
230           return VPPCOM_OK;
231         }
232       if (session->session_state & STATE_FAILED)
233         {
234           VCL_SESSION_UNLOCK ();
235           return VPPCOM_ECONNREFUSED;
236         }
237
238       VCL_SESSION_UNLOCK ();
239     }
240   while (clib_time_now (&vcm->clib_time) < timeout);
241
242   VDBG (0, "VCL<%d>: timeout waiting for state 0x%x (%s)", getpid (), state,
243         vppcom_session_state_str (state));
244   vcl_evt (VCL_EVT_SESSION_TIMEOUT, session, session_state);
245
246   return VPPCOM_ETIMEDOUT;
247 }
248
249 static int
250 vppcom_app_session_enable (void)
251 {
252   int rv;
253
254   if (vcm->app_state != STATE_APP_ENABLED)
255     {
256       vppcom_send_session_enable_disable (1 /* is_enabled == TRUE */ );
257       rv = vppcom_wait_for_app_state_change (STATE_APP_ENABLED);
258       if (PREDICT_FALSE (rv))
259         {
260           VDBG (0, "VCL<%d>: application session enable timed out! "
261                 "returning %d (%s)", getpid (), rv, vppcom_retval_str (rv));
262           return rv;
263         }
264     }
265   return VPPCOM_OK;
266 }
267
268 static int
269 vppcom_app_attach (void)
270 {
271   int rv;
272
273   vppcom_app_send_attach ();
274   rv = vppcom_wait_for_app_state_change (STATE_APP_ATTACHED);
275   if (PREDICT_FALSE (rv))
276     {
277       VDBG (0, "VCL<%d>: application attach timed out! returning %d (%s)",
278             getpid (), rv, vppcom_retval_str (rv));
279       return rv;
280     }
281
282   return VPPCOM_OK;
283 }
284
285 static int
286 vppcom_session_unbind (u32 session_index)
287 {
288   vcl_session_t *session = 0;
289   int rv;
290   u64 vpp_handle;
291
292   VCL_SESSION_LOCK_AND_GET (session_index, &session);
293
294   vpp_handle = session->vpp_handle;
295   vppcom_session_table_del_listener (vpp_handle);
296   session->vpp_handle = ~0;
297   session->session_state = STATE_DISCONNECT;
298
299   VCL_SESSION_UNLOCK ();
300
301   VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending unbind msg! new state"
302         " 0x%x (%s)", getpid (), vpp_handle, session_index, STATE_DISCONNECT,
303         vppcom_session_state_str (STATE_DISCONNECT));
304   vcl_evt (VCL_EVT_UNBIND, session);
305   vppcom_send_unbind_sock (vpp_handle);
306
307 done:
308   return rv;
309 }
310
311 static int
312 vppcom_session_disconnect (u32 session_index)
313 {
314   int rv;
315   vcl_session_t *session;
316   u64 vpp_handle;
317   session_state_t state;
318
319   VCL_SESSION_LOCK_AND_GET (session_index, &session);
320
321   vpp_handle = session->vpp_handle;
322   state = session->session_state;
323   VCL_SESSION_UNLOCK ();
324
325   VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u state 0x%x (%s)", getpid (),
326         vpp_handle, session_index, state, vppcom_session_state_str (state));
327
328   if (PREDICT_FALSE (state & STATE_LISTEN))
329     {
330       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
331                     "Cannot disconnect a listen socket!",
332                     getpid (), vpp_handle, session_index);
333       rv = VPPCOM_EBADFD;
334       goto done;
335     }
336
337   /* The peer has already initiated the close,
338    * so send the disconnect session reply.
339    */
340   if (state & STATE_CLOSE_ON_EMPTY)
341     {
342       //XXX alagalah - Check and drain here?
343       vppcom_send_disconnect_session_reply (vpp_handle,
344                                             session_index, 0 /* rv */ );
345       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending disconnect "
346             "REPLY...", getpid (), vpp_handle, session_index);
347     }
348
349   /* Otherwise, send a disconnect session msg...
350    */
351   else
352     {
353       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending disconnect...",
354             getpid (), vpp_handle, session_index);
355
356       vppcom_send_disconnect_session (vpp_handle, session_index);
357     }
358
359 done:
360   return rv;
361 }
362
363 /*
364  * VPPCOM Public API functions
365  */
366 int
367 vppcom_app_create (char *app_name)
368 {
369   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
370   int rv;
371
372   if (!vcm->init)
373     {
374       vcm->init = 1;
375       vppcom_cfg (&vcm->cfg);
376
377       clib_spinlock_init (&vcm->session_fifo_lockp);
378       clib_fifo_validate (vcm->client_session_index_fifo,
379                           vcm->cfg.listen_queue_size);
380       clib_spinlock_init (&vcm->sessions_lockp);
381
382
383       vcm->main_cpu = os_get_thread_index ();
384
385       vcm->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
386
387       clib_time_init (&vcm->clib_time);
388       vppcom_init_error_string_table ();
389       svm_fifo_segment_main_init (vcl_cfg->segment_baseva,
390                                   20 /* timeout in secs */ );
391     }
392
393   if (vcm->my_client_index == ~0)
394     {
395       /* API hookup and connect to VPP */
396       vppcom_api_hookup ();
397       vcl_elog_init (vcm);
398       vcm->app_state = STATE_APP_START;
399       rv = vppcom_connect_to_vpp (app_name);
400       if (rv)
401         {
402           clib_warning ("VCL<%d>: ERROR: couldn't connect to VPP!",
403                         getpid ());
404           return rv;
405         }
406
407       /* State event handling thread */
408
409       rv = vce_start_event_thread (&(vcm->event_thread), 20);
410
411       VDBG (0, "VCL<%d>: sending session enable", getpid ());
412
413       rv = vppcom_app_session_enable ();
414       if (rv)
415         {
416           clib_warning ("VCL<%d>: ERROR: vppcom_app_session_enable() "
417                         "failed!", getpid ());
418           return rv;
419         }
420
421       VDBG (0, "VCL<%d>: sending app attach", getpid ());
422
423       rv = vppcom_app_attach ();
424       if (rv)
425         {
426           clib_warning ("VCL<%d>: ERROR: vppcom_app_attach() failed!",
427                         getpid ());
428           return rv;
429         }
430
431       VDBG (0, "VCL<%d>: app_name '%s', my_client_index %d (0x%x)",
432             getpid (), app_name, vcm->my_client_index, vcm->my_client_index);
433     }
434
435   return VPPCOM_OK;
436 }
437
438 void
439 vppcom_app_destroy (void)
440 {
441   int rv;
442   f64 orig_app_timeout;
443
444   if (vcm->my_client_index == ~0)
445     return;
446
447   VDBG (0, "VCL<%d>: detaching from VPP, my_client_index %d (0x%x)",
448         getpid (), vcm->my_client_index, vcm->my_client_index);
449   vcl_evt (VCL_EVT_DETACH, vcm);
450
451   vppcom_app_send_detach ();
452   orig_app_timeout = vcm->cfg.app_timeout;
453   vcm->cfg.app_timeout = 2.0;
454   rv = vppcom_wait_for_app_state_change (STATE_APP_ENABLED);
455   vcm->cfg.app_timeout = orig_app_timeout;
456   if (PREDICT_FALSE (rv))
457     VDBG (0, "VCL<%d>: application detach timed out! returning %d (%s)",
458           getpid (), rv, vppcom_retval_str (rv));
459
460   vcl_elog_stop (vcm);
461   vl_client_disconnect_from_vlib ();
462   vcm->my_client_index = ~0;
463   vcm->app_state = STATE_APP_START;
464 }
465
466 int
467 vppcom_session_create (u8 proto, u8 is_nonblocking)
468 {
469   vcl_session_t *session;
470   u32 session_index;
471
472   VCL_SESSION_LOCK ();
473   pool_get (vcm->sessions, session);
474   memset (session, 0, sizeof (*session));
475   session_index = session - vcm->sessions;
476
477   session->session_type = proto;
478   session->session_state = STATE_START;
479   session->vpp_handle = ~0;
480
481   if (is_nonblocking)
482     VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
483   else
484     VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_NONBLOCK);
485
486   vcl_evt (VCL_EVT_CREATE, session, session_type, session->session_state,
487            is_nonblocking, session_index);
488
489   VCL_SESSION_UNLOCK ();
490
491   VDBG (0, "VCL<%d>: sid %u", getpid (), session_index);
492
493   return (int) session_index;
494 }
495
496 int
497 vppcom_session_close (uint32_t session_index)
498 {
499   vcl_session_t *session = 0;
500   int rv;
501   u8 is_vep;
502   u8 is_vep_session;
503   u32 next_sid;
504   u32 vep_idx;
505   u64 vpp_handle;
506   uword *p;
507   session_state_t state;
508
509   VCL_SESSION_LOCK_AND_GET (session_index, &session);
510   is_vep = session->is_vep;
511   is_vep_session = session->is_vep_session;
512   next_sid = session->vep.next_sid;
513   vep_idx = session->vep.vep_idx;
514   state = session->session_state;
515   vpp_handle = session->vpp_handle;
516   VCL_SESSION_UNLOCK ();
517
518   if (VPPCOM_DEBUG > 0)
519     {
520       if (is_vep)
521         clib_warning ("VCL<%d>: vep_idx %u / sid %u: "
522                       "closing epoll session...",
523                       getpid (), session_index, session_index);
524       else
525         clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %d: "
526                       "closing session...",
527                       getpid (), vpp_handle, session_index);
528     }
529
530   if (is_vep)
531     {
532       while (next_sid != ~0)
533         {
534           rv = vppcom_epoll_ctl (session_index, EPOLL_CTL_DEL, next_sid, 0);
535           if (PREDICT_FALSE (rv < 0))
536             VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL "
537                   "vep_idx %u failed! rv %d (%s)",
538                   getpid (), vpp_handle, next_sid, vep_idx,
539                   rv, vppcom_retval_str (rv));
540
541           VCL_SESSION_LOCK_AND_GET (session_index, &session);
542           next_sid = session->vep.next_sid;
543           VCL_SESSION_UNLOCK ();
544         }
545     }
546   else
547     {
548       if (is_vep_session)
549         {
550           rv = vppcom_epoll_ctl (vep_idx, EPOLL_CTL_DEL, session_index, 0);
551           if (rv < 0)
552             VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL "
553                   "vep_idx %u failed! rv %d (%s)",
554                   getpid (), vpp_handle, session_index,
555                   vep_idx, rv, vppcom_retval_str (rv));
556         }
557
558       if (state & STATE_LISTEN)
559         {
560           rv = vppcom_session_unbind (session_index);
561           if (PREDICT_FALSE (rv < 0))
562             VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: listener unbind "
563                   "failed! rv %d (%s)",
564                   getpid (), vpp_handle, session_index,
565                   rv, vppcom_retval_str (rv));
566         }
567
568       else if (state & (CLIENT_STATE_OPEN | SERVER_STATE_OPEN))
569         {
570           rv = vppcom_session_disconnect (session_index);
571           if (PREDICT_FALSE (rv < 0))
572             clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
573                           "session disconnect failed! rv %d (%s)",
574                           getpid (), vpp_handle, session_index,
575                           rv, vppcom_retval_str (rv));
576         }
577     }
578
579   VCL_SESSION_LOCK_AND_GET (session_index, &session);
580   vpp_handle = session->vpp_handle;
581   if (vpp_handle != ~0)
582     {
583       p = hash_get (vcm->session_index_by_vpp_handles, vpp_handle);
584       if (p)
585         hash_unset (vcm->session_index_by_vpp_handles, vpp_handle);
586     }
587   pool_put_index (vcm->sessions, session_index);
588
589   VCL_SESSION_UNLOCK ();
590
591   if (VPPCOM_DEBUG > 0)
592     {
593       if (is_vep)
594         clib_warning ("VCL<%d>: vep_idx %u / sid %u: epoll session removed.",
595                       getpid (), session_index, session_index);
596       else
597         clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: session removed.",
598                       getpid (), vpp_handle, session_index);
599     }
600 done:
601
602   vcl_evt (VCL_EVT_CLOSE, session, rv);
603
604   return rv;
605 }
606
607 int
608 vppcom_session_bind (uint32_t session_index, vppcom_endpt_t * ep)
609 {
610   vcl_session_t *session = 0;
611   int rv;
612
613   if (!ep || !ep->ip)
614     return VPPCOM_EINVAL;
615
616   VCL_SESSION_LOCK_AND_GET (session_index, &session);
617
618   if (session->is_vep)
619     {
620       VCL_SESSION_UNLOCK ();
621       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
622                     "bind to an epoll session!", getpid (), session_index);
623       rv = VPPCOM_EBADFD;
624       goto done;
625     }
626
627   session->transport.is_ip4 = ep->is_ip4;
628   session->transport.lcl_ip = to_ip46 (ep->is_ip4 ? IP46_TYPE_IP4 :
629                                        IP46_TYPE_IP6, ep->ip);
630   session->transport.lcl_port = ep->port;
631
632   VDBG (0, "VCL<%d>: sid %u: binding to local %s address %U port %u, "
633         "proto %s", getpid (), session_index,
634         session->transport.is_ip4 ? "IPv4" : "IPv6",
635         format_ip46_address, &session->transport.lcl_ip,
636         session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
637         clib_net_to_host_u16 (session->transport.lcl_port),
638         session->session_type ? "UDP" : "TCP");
639   vcl_evt (VCL_EVT_BIND, session);
640   VCL_SESSION_UNLOCK ();
641 done:
642   return rv;
643 }
644
645 int
646 vppcom_session_listen (uint32_t listen_session_index, uint32_t q_len)
647 {
648   vcl_session_t *listen_session = 0;
649   u64 listen_vpp_handle;
650   int rv, retval;
651
652   if (q_len == 0 || q_len == ~0)
653     q_len = vcm->cfg.listen_queue_size;
654
655   VCL_SESSION_LOCK_AND_GET (listen_session_index, &listen_session);
656
657   if (listen_session->is_vep)
658     {
659       VCL_SESSION_UNLOCK ();
660       clib_warning ("VCL<%d>: ERROR: sid %u: cannot listen on an "
661                     "epoll session!", getpid (), listen_session_index);
662       rv = VPPCOM_EBADFD;
663       goto done;
664     }
665
666   listen_vpp_handle = listen_session->vpp_handle;
667   if (listen_session->session_state & STATE_LISTEN)
668     {
669       VCL_SESSION_UNLOCK ();
670       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: already in listen state!",
671             getpid (), listen_vpp_handle, listen_session_index);
672       rv = VPPCOM_OK;
673       goto done;
674     }
675
676   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: sending VPP bind+listen "
677         "request...", getpid (), listen_vpp_handle, listen_session_index);
678
679   vppcom_send_bind_sock (listen_session, listen_session_index);
680   VCL_SESSION_UNLOCK ();
681   retval =
682     vppcom_wait_for_session_state_change (listen_session_index, STATE_LISTEN,
683                                           vcm->cfg.session_timeout);
684
685   VCL_SESSION_LOCK_AND_GET (listen_session_index, &listen_session);
686   if (PREDICT_FALSE (retval))
687     {
688       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: bind+listen failed! "
689             "returning %d (%s)", getpid (), listen_session->vpp_handle,
690             listen_session_index, retval, vppcom_retval_str (retval));
691       VCL_SESSION_UNLOCK ();
692       rv = retval;
693       goto done;
694     }
695
696   VCL_ACCEPT_FIFO_LOCK ();
697   clib_fifo_validate (vcm->client_session_index_fifo, q_len);
698   VCL_ACCEPT_FIFO_UNLOCK ();
699
700   VCL_SESSION_UNLOCK ();
701
702 done:
703   return rv;
704 }
705
706 int
707 validate_args_session_accept_ (vcl_session_t * listen_session)
708 {
709   u32 listen_session_index = listen_session - vcm->sessions;
710
711   /* Input validation - expects spinlock on sessions_lockp */
712   if (listen_session->is_vep)
713     {
714       clib_warning ("VCL<%d>: ERROR: sid %u: cannot accept on an "
715                     "epoll session!", getpid (), listen_session_index);
716       return VPPCOM_EBADFD;
717     }
718
719   if (listen_session->session_state != STATE_LISTEN)
720     {
721       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
722                     "not in listen state! state 0x%x (%s)", getpid (),
723                     listen_session->vpp_handle, listen_session_index,
724                     listen_session->session_state,
725                     vppcom_session_state_str (listen_session->session_state));
726       return VPPCOM_EBADFD;
727     }
728   return VPPCOM_OK;
729 }
730
731 int
732 vppcom_session_accept (uint32_t listen_session_index, vppcom_endpt_t * ep,
733                        uint32_t flags)
734 {
735   vcl_session_t *listen_session = 0;
736   vcl_session_t *client_session = 0;
737   u32 client_session_index = ~0;
738   int rv;
739   u64 listen_vpp_handle;
740   vce_event_handler_reg_t *reg;
741   vce_event_t *ev;
742   vce_event_connect_request_t *result;
743   struct timespec ts;
744   struct timeval tv;
745   int millisecond_timeout = 1;
746   int hours_timeout = 20 * 60 * 60;
747
748   VCL_SESSION_LOCK_AND_GET (listen_session_index, &listen_session);
749   listen_vpp_handle = listen_session->vpp_handle;       // For debugging
750
751   rv = validate_args_session_accept_ (listen_session);
752   if (rv)
753     {
754       VCL_SESSION_UNLOCK ();
755       goto done;
756     }
757
758   /* Using an aggressive timer of 1ms and a generous timer of
759    * 20 hours, we can implement a blocking and non-blocking listener
760    * as both event and time driven */
761   gettimeofday (&tv, NULL);
762   ts.tv_nsec = (tv.tv_usec * 1000) + (1000 * millisecond_timeout);
763   ts.tv_sec = tv.tv_sec;
764
765   /* Predict that the Listener is blocking more often than not */
766   if (PREDICT_TRUE (!VCL_SESS_ATTR_TEST (listen_session->attr,
767                                          VCL_SESS_ATTR_NONBLOCK)))
768     ts.tv_sec += hours_timeout;
769
770   VCL_SESSION_UNLOCK ();
771
772   /* Register handler for connect_request event on listen_session_index */
773   vce_event_key_t evk;
774   evk.session_index = listen_session_index;
775   evk.eid = VCL_EVENT_CONNECT_REQ_ACCEPTED;
776   reg = vce_register_handler (&vcm->event_thread, &evk,
777                               vce_connect_request_handler_fn, 0);
778   VCL_EVENTS_LOCK ();
779   ev = vce_get_event_from_index (&vcm->event_thread, reg->ev_idx);
780   pthread_mutex_lock (&reg->handler_lock);
781   while (!ev)
782     {
783       VCL_EVENTS_UNLOCK ();
784       rv = pthread_cond_timedwait (&reg->handler_cond,
785                                    &reg->handler_lock, &ts);
786       if (rv == ETIMEDOUT)
787         {
788           rv = VPPCOM_EAGAIN;
789           goto cleanup;
790         }
791       VCL_EVENTS_LOCK ();
792       ev = vce_get_event_from_index (&vcm->event_thread, reg->ev_idx);
793     }
794   result = vce_get_event_data (ev, sizeof (*result));
795   client_session_index = result->accepted_session_index;
796   VCL_EVENTS_UNLOCK ();
797
798   /* Remove from the FIFO used to service epoll */
799   VCL_ACCEPT_FIFO_LOCK ();
800   if (clib_fifo_elts (vcm->client_session_index_fifo))
801     {
802       u32 tmp_client_session_index;
803       clib_fifo_sub1 (vcm->client_session_index_fifo,
804                       tmp_client_session_index);
805       /* It wasn't ours... put it back ... */
806       if (tmp_client_session_index != client_session_index)
807         clib_fifo_add1 (vcm->client_session_index_fifo,
808                         tmp_client_session_index);
809     }
810   VCL_ACCEPT_FIFO_UNLOCK ();
811
812   VCL_SESSION_LOCK ();
813
814   rv = vppcom_session_at_index (client_session_index, &client_session);
815   if (PREDICT_FALSE (rv))
816     {
817       rv = VPPCOM_ECONNABORTED;
818       clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: client sid %u "
819                     "lookup failed! returning %d (%s)", getpid (),
820                     listen_vpp_handle, listen_session_index,
821                     client_session_index, rv, vppcom_retval_str (rv));
822       goto cleanup;
823     }
824
825   if (flags & O_NONBLOCK)
826     VCL_SESS_ATTR_SET (client_session->attr, VCL_SESS_ATTR_NONBLOCK);
827   else
828     VCL_SESS_ATTR_CLR (client_session->attr, VCL_SESS_ATTR_NONBLOCK);
829
830   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: Got a client request! "
831         "vpp handle 0x%llx, sid %u, flags %d, is_nonblocking %u",
832         getpid (), listen_vpp_handle, listen_session_index,
833         client_session->vpp_handle, client_session_index,
834         flags, VCL_SESS_ATTR_TEST (client_session->attr,
835                                    VCL_SESS_ATTR_NONBLOCK));
836
837   if (ep)
838     {
839       ep->is_ip4 = client_session->transport.is_ip4;
840       ep->port = client_session->transport.rmt_port;
841       if (client_session->transport.is_ip4)
842         clib_memcpy (ep->ip, &client_session->transport.rmt_ip.ip4,
843                      sizeof (ip4_address_t));
844       else
845         clib_memcpy (ep->ip, &client_session->transport.rmt_ip.ip6,
846                      sizeof (ip6_address_t));
847     }
848
849   vppcom_send_accept_session_reply (client_session->vpp_handle,
850                                     client_session->client_context,
851                                     0 /* retval OK */ );
852
853   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: accepted vpp handle 0x%llx,"
854         " sid %u connection from peer %s address %U port %u to local %s address"
855         " %U port %u",
856         getpid (), listen_vpp_handle,
857         listen_session_index, client_session->vpp_handle,
858         client_session_index,
859         client_session->transport.is_ip4 ? "IPv4" : "IPv6",
860         format_ip46_address, &client_session->transport.rmt_ip,
861         client_session->transport.is_ip4 ?
862         IP46_TYPE_IP4 : IP46_TYPE_IP6,
863         clib_net_to_host_u16 (client_session->transport.rmt_port),
864         client_session->transport.is_ip4 ? "IPv4" : "IPv6",
865         format_ip46_address, &client_session->transport.lcl_ip,
866         client_session->transport.is_ip4 ?
867         IP46_TYPE_IP4 : IP46_TYPE_IP6,
868         clib_net_to_host_u16 (client_session->transport.lcl_port));
869   vcl_evt (VCL_EVT_ACCEPT, client_session, listen_session,
870            client_session_index);
871   VCL_SESSION_UNLOCK ();
872
873   rv = (int) client_session_index;
874   vce_clear_event (&vcm->event_thread, reg->ev_idx);
875   if (vcm->session_io_thread.io_sessions_lockp)
876     {
877       /* Throw this new accepted session index into the rx poll thread pool */
878       VCL_IO_SESSIONS_LOCK ();
879       u32 *active_session_index;
880       pool_get (vcm->session_io_thread.active_session_indexes,
881                 active_session_index);
882       *active_session_index = client_session_index;
883       VCL_IO_SESSIONS_UNLOCK ();
884     }
885 cleanup:
886   vce_unregister_handler (&vcm->event_thread, reg);
887   pthread_mutex_unlock (&reg->handler_lock);
888
889 done:
890   return rv;
891 }
892
893 int
894 vppcom_session_connect (uint32_t session_index, vppcom_endpt_t * server_ep)
895 {
896   vcl_session_t *session = 0;
897   u64 vpp_handle = 0;
898   int rv, retval = VPPCOM_OK;
899
900   VCL_SESSION_LOCK_AND_GET (session_index, &session);
901
902   if (PREDICT_FALSE (session->is_vep))
903     {
904       VCL_SESSION_UNLOCK ();
905       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
906                     "connect on an epoll session!", getpid (), session_index);
907       rv = VPPCOM_EBADFD;
908       goto done;
909     }
910
911   if (PREDICT_FALSE (session->session_state & CLIENT_STATE_OPEN))
912     {
913       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: session already "
914             "connected to %s %U port %d proto %s, state 0x%x (%s)",
915             getpid (), session->vpp_handle, session_index,
916             session->transport.is_ip4 ? "IPv4" : "IPv6",
917             format_ip46_address,
918             &session->transport.rmt_ip, session->transport.is_ip4 ?
919             IP46_TYPE_IP4 : IP46_TYPE_IP6,
920             clib_net_to_host_u16 (session->transport.rmt_port),
921             session->session_type ? "UDP" : "TCP", session->session_state,
922             vppcom_session_state_str (session->session_state));
923
924       VCL_SESSION_UNLOCK ();
925       goto done;
926     }
927
928   session->transport.is_ip4 = server_ep->is_ip4;
929   if (session->transport.is_ip4)
930     clib_memcpy (&session->transport.rmt_ip.ip4, server_ep->ip,
931                  sizeof (ip4_address_t));
932   else
933     clib_memcpy (&session->transport.rmt_ip.ip6, server_ep->ip,
934                  sizeof (ip6_address_t));
935   session->transport.rmt_port = server_ep->port;
936
937   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: connecting to server %s %U "
938         "port %d proto %s",
939         getpid (), session->vpp_handle, session_index,
940         session->transport.is_ip4 ? "IPv4" : "IPv6",
941         format_ip46_address,
942         &session->transport.rmt_ip, session->transport.is_ip4 ?
943         IP46_TYPE_IP4 : IP46_TYPE_IP6,
944         clib_net_to_host_u16 (session->transport.rmt_port),
945         session->session_type ? "UDP" : "TCP");
946
947   vppcom_send_connect_sock (session, session_index);
948   VCL_SESSION_UNLOCK ();
949
950   retval =
951     vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
952                                           vcm->cfg.session_timeout);
953
954   VCL_SESSION_LOCK_AND_GET (session_index, &session);
955   vpp_handle = session->vpp_handle;
956   VCL_SESSION_UNLOCK ();
957
958 done:
959   if (PREDICT_FALSE (retval))
960     {
961       rv = retval;
962       if (VPPCOM_DEBUG > 0)
963         {
964           if (session)
965             clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: connect "
966                           "failed! returning %d (%s)", getpid (), vpp_handle,
967                           session_index, rv, vppcom_retval_str (rv));
968           else
969             clib_warning ("VCL<%d>: no session for sid %u: connect failed! "
970                           "returning %d (%s)", getpid (),
971                           session_index, rv, vppcom_retval_str (rv));
972         }
973     }
974   else
975     VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: connected!",
976           getpid (), vpp_handle, session_index);
977
978   return rv;
979 }
980
981 static inline int
982 vppcom_session_read_internal (uint32_t session_index, void *buf, int n,
983                               u8 peek)
984 {
985   vcl_session_t *session = 0;
986   svm_fifo_t *rx_fifo;
987   int n_read = 0;
988   int rv;
989   int is_nonblocking;
990
991   u64 vpp_handle;
992   u32 poll_et;
993   session_state_t state;
994
995   ASSERT (buf);
996
997   VCL_SESSION_LOCK_AND_GET (session_index, &session);
998
999   is_nonblocking = VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK);
1000   rx_fifo = session->rx_fifo;
1001   state = session->session_state;
1002   vpp_handle = session->vpp_handle;
1003
1004   if (PREDICT_FALSE (session->is_vep))
1005     {
1006       VCL_SESSION_UNLOCK ();
1007       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
1008                     "read from an epoll session!", getpid (), session_index);
1009       rv = VPPCOM_EBADFD;
1010       goto done;
1011     }
1012
1013   if (PREDICT_FALSE (!(state & (SERVER_STATE_OPEN | CLIENT_STATE_OPEN))))
1014     {
1015       VCL_SESSION_UNLOCK ();
1016       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1017
1018       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: %s session is not open! "
1019             "state 0x%x (%s), returning %d (%s)",
1020             getpid (), vpp_handle, session_index, state,
1021             vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
1022       goto done;
1023     }
1024
1025   VCL_SESSION_UNLOCK ();
1026
1027   do
1028     {
1029       if (peek)
1030         n_read = svm_fifo_peek (rx_fifo, 0, n, buf);
1031       else
1032         n_read = svm_fifo_dequeue_nowait (rx_fifo, n, buf);
1033     }
1034   while (!is_nonblocking && (n_read <= 0));
1035
1036   if (n_read <= 0)
1037     {
1038       VCL_SESSION_LOCK_AND_GET (session_index, &session);
1039
1040       poll_et = (((EPOLLET | EPOLLIN) & session->vep.ev.events) ==
1041                  (EPOLLET | EPOLLIN));
1042       if (poll_et)
1043         session->vep.et_mask |= EPOLLIN;
1044
1045       if (state & STATE_CLOSE_ON_EMPTY)
1046         {
1047           rv = VPPCOM_ECONNRESET;
1048
1049           VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: Empty fifo with "
1050                 "session state 0x%x (%s)! Setting state to 0x%x (%s), "
1051                 "returning %d (%s)",
1052                 getpid (), session->vpp_handle, session_index,
1053                 state, vppcom_session_state_str (state),
1054                 STATE_DISCONNECT,
1055                 vppcom_session_state_str (STATE_DISCONNECT), rv,
1056                 vppcom_retval_str (rv));
1057
1058           session->session_state = STATE_DISCONNECT;
1059         }
1060       else
1061         rv = VPPCOM_EAGAIN;
1062
1063       VCL_SESSION_UNLOCK ();
1064     }
1065   else
1066     rv = n_read;
1067
1068   if (VPPCOM_DEBUG > 2)
1069     {
1070       if (rv > 0)
1071         clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: read %d bytes "
1072                       "from (%p)", getpid (), vpp_handle,
1073                       session_index, n_read, rx_fifo);
1074       else
1075         clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: nothing read! "
1076                       "returning %d (%s)", getpid (), vpp_handle,
1077                       session_index, rv, vppcom_retval_str (rv));
1078     }
1079 done:
1080   return rv;
1081 }
1082
1083 int
1084 vppcom_session_read (uint32_t session_index, void *buf, size_t n)
1085 {
1086   return (vppcom_session_read_internal (session_index, buf, n, 0));
1087 }
1088
1089 static int
1090 vppcom_session_peek (uint32_t session_index, void *buf, int n)
1091 {
1092   return (vppcom_session_read_internal (session_index, buf, n, 1));
1093 }
1094
1095 static inline int
1096 vppcom_session_read_ready (vcl_session_t * session, u32 session_index)
1097 {
1098   int ready = 0;
1099   u32 poll_et;
1100   int rv;
1101   session_state_t state = session->session_state;
1102   u64 vpp_handle = session->vpp_handle;
1103
1104   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1105   if (PREDICT_FALSE (session->is_vep))
1106     {
1107       clib_warning ("VCL<%d>: ERROR: sid %u: cannot read from an "
1108                     "epoll session!", getpid (), session_index);
1109       rv = VPPCOM_EBADFD;
1110       goto done;
1111     }
1112
1113   if (session->session_state & STATE_LISTEN)
1114     {
1115       VCL_ACCEPT_FIFO_LOCK ();
1116       ready = clib_fifo_elts (vcm->client_session_index_fifo);
1117       VCL_ACCEPT_FIFO_UNLOCK ();
1118     }
1119   else
1120     {
1121       if (!(state & (SERVER_STATE_OPEN | CLIENT_STATE_OPEN | STATE_LISTEN)))
1122         {
1123           rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET :
1124                 VPPCOM_ENOTCONN);
1125
1126           VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open!"
1127                 " state 0x%x (%s), returning %d (%s)",
1128                 getpid (), vpp_handle, session_index,
1129                 state, vppcom_session_state_str (state),
1130                 rv, vppcom_retval_str (rv));
1131           goto done;
1132         }
1133
1134       ready = svm_fifo_max_dequeue (session->rx_fifo);
1135     }
1136
1137   if (ready == 0)
1138     {
1139       poll_et =
1140         ((EPOLLET | EPOLLIN) & session->vep.ev.events) == (EPOLLET | EPOLLIN);
1141       if (poll_et)
1142         session->vep.et_mask |= EPOLLIN;
1143
1144       if (state & STATE_CLOSE_ON_EMPTY)
1145         {
1146           rv = VPPCOM_ECONNRESET;
1147
1148           VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: Empty fifo with "
1149                 "session state 0x%x (%s)! Setting state to 0x%x (%s), "
1150                 "returning %d (%s)",
1151                 getpid (), session_index, vpp_handle,
1152                 state, vppcom_session_state_str (state),
1153                 STATE_DISCONNECT,
1154                 vppcom_session_state_str (STATE_DISCONNECT), rv,
1155                 vppcom_retval_str (rv));
1156           session->session_state = STATE_DISCONNECT;
1157           goto done;
1158         }
1159     }
1160   rv = ready;
1161
1162   if (!svm_msg_q_is_empty (vcm->app_event_queue) &&
1163       !pthread_mutex_trylock (&vcm->app_event_queue->q->mutex))
1164     {
1165       u32 i, n_to_dequeue = vcm->app_event_queue->q->cursize;
1166       svm_msg_q_msg_t msg;
1167
1168       for (i = 0; i < n_to_dequeue; i++)
1169         {
1170           svm_queue_sub_raw (vcm->app_event_queue->q, (u8 *) & msg);
1171           svm_msg_q_free_msg (vcm->app_event_queue, &msg);
1172         }
1173
1174       pthread_mutex_unlock (&vcm->app_event_queue->q->mutex);
1175     }
1176 done:
1177   return rv;
1178 }
1179
1180 int
1181 vppcom_session_write (uint32_t session_index, void *buf, size_t n)
1182 {
1183   vcl_session_t *session = 0;
1184   svm_fifo_t *tx_fifo = 0;
1185   svm_msg_q_t *mq;
1186   session_state_t state;
1187   int rv, n_write, is_nonblocking;
1188   u32 poll_et;
1189   u64 vpp_handle;
1190
1191   ASSERT (buf);
1192
1193   VCL_SESSION_LOCK_AND_GET (session_index, &session);
1194
1195   tx_fifo = session->tx_fifo;
1196   is_nonblocking = VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK);
1197   vpp_handle = session->vpp_handle;
1198   state = session->session_state;
1199
1200   if (PREDICT_FALSE (session->is_vep))
1201     {
1202       VCL_SESSION_UNLOCK ();
1203       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1204                     "cannot write to an epoll session!",
1205                     getpid (), vpp_handle, session_index);
1206
1207       rv = VPPCOM_EBADFD;
1208       goto done;
1209     }
1210
1211   if (!(session->session_state & (SERVER_STATE_OPEN | CLIENT_STATE_OPEN)))
1212     {
1213       rv =
1214         ((session->session_state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET :
1215          VPPCOM_ENOTCONN);
1216
1217       VCL_SESSION_UNLOCK ();
1218       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open! "
1219             "state 0x%x (%s)",
1220             getpid (), vpp_handle, session_index,
1221             state, vppcom_session_state_str (state));
1222       goto done;
1223     }
1224
1225   VCL_SESSION_UNLOCK ();
1226
1227   do
1228     {
1229       n_write = svm_fifo_enqueue_nowait (tx_fifo, n, (void *) buf);
1230     }
1231   while (!is_nonblocking && (n_write <= 0));
1232
1233   /* If event wasn't set, add one
1234    *
1235    * To reduce context switching, can check if an
1236    * event is already there for this event_key, but for now
1237    * this will suffice. */
1238
1239   if ((n_write > 0) && svm_fifo_set_event (tx_fifo))
1240     {
1241       /* Send TX event to vpp */
1242       VCL_SESSION_LOCK_AND_GET (session_index, &session);
1243       mq = session->vpp_evt_q;
1244       ASSERT (mq);
1245       app_send_io_evt_to_vpp (mq, tx_fifo, FIFO_EVENT_APP_TX, SVM_Q_WAIT);
1246       VCL_SESSION_UNLOCK ();
1247       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: added FIFO_EVENT_APP_TX "
1248             "to vpp_event_q %p, n_write %d", getpid (),
1249             vpp_handle, session_index, mq, n_write);
1250     }
1251
1252   if (n_write <= 0)
1253     {
1254       VCL_SESSION_LOCK_AND_GET (session_index, &session);
1255
1256       poll_et = (((EPOLLET | EPOLLOUT) & session->vep.ev.events) ==
1257                  (EPOLLET | EPOLLOUT));
1258       if (poll_et)
1259         session->vep.et_mask |= EPOLLOUT;
1260
1261       if (session->session_state & STATE_CLOSE_ON_EMPTY)
1262         {
1263           rv = VPPCOM_ECONNRESET;
1264
1265           VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: Empty fifo with "
1266                 "session state 0x%x (%s)! Setting state to 0x%x (%s), "
1267                 "returning %d (%s)",
1268                 getpid (), session->vpp_handle, session_index,
1269                 session->session_state,
1270                 vppcom_session_state_str (session->session_state),
1271                 STATE_DISCONNECT,
1272                 vppcom_session_state_str (STATE_DISCONNECT), rv,
1273                 vppcom_retval_str (rv));
1274
1275           session->session_state = STATE_DISCONNECT;
1276         }
1277       else
1278         rv = VPPCOM_EAGAIN;
1279
1280       VCL_SESSION_UNLOCK ();
1281     }
1282   else
1283     rv = n_write;
1284
1285   if (VPPCOM_DEBUG > 2)
1286     {
1287       if (n_write <= 0)
1288         clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: "
1289                       "FIFO-FULL (%p)", getpid (), vpp_handle,
1290                       session_index, tx_fifo);
1291       else
1292         clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: "
1293                       "wrote %d bytes tx-fifo: (%p)", getpid (),
1294                       vpp_handle, session_index, n_write, tx_fifo);
1295     }
1296 done:
1297   return rv;
1298 }
1299
1300 static inline int
1301 vppcom_session_write_ready (vcl_session_t * session, u32 session_index)
1302 {
1303   int ready;
1304   u32 poll_et;
1305   int rv;
1306
1307   ASSERT (session);
1308
1309   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1310   if (PREDICT_FALSE (session->is_vep))
1311     {
1312       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1313                     "cannot write to an epoll session!",
1314                     getpid (), session->vpp_handle, session_index);
1315       rv = VPPCOM_EBADFD;
1316       goto done;
1317     }
1318
1319   if (PREDICT_FALSE (session->session_state & STATE_LISTEN))
1320     {
1321       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1322                     "cannot write to a listen session!",
1323                     getpid (), session->vpp_handle, session_index);
1324       rv = VPPCOM_EBADFD;
1325       goto done;
1326     }
1327
1328   if (!(session->session_state & (SERVER_STATE_OPEN | CLIENT_STATE_OPEN)))
1329     {
1330       session_state_t state = session->session_state;
1331
1332       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1333
1334       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1335                     "session is not open! state 0x%x (%s), "
1336                     "returning %d (%s)", getpid (), session->vpp_handle,
1337                     session_index,
1338                     state, vppcom_session_state_str (state),
1339                     rv, vppcom_retval_str (rv));
1340       goto done;
1341     }
1342
1343   ready = svm_fifo_max_enqueue (session->tx_fifo);
1344
1345   VDBG (3, "VCL<%d>: vpp handle 0x%llx, sid %u: peek %s (%p), ready = %d",
1346         getpid (), session->vpp_handle, session_index, session->tx_fifo,
1347         ready);
1348
1349   if (ready == 0)
1350     {
1351       poll_et = (((EPOLLET | EPOLLOUT) & session->vep.ev.events) ==
1352                  (EPOLLET | EPOLLOUT));
1353       if (poll_et)
1354         session->vep.et_mask |= EPOLLOUT;
1355
1356       if (session->session_state & STATE_CLOSE_ON_EMPTY)
1357         {
1358           rv = VPPCOM_ECONNRESET;
1359
1360           VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: Empty fifo with "
1361                 "session state 0x%x (%s)! Setting state to 0x%x (%s), "
1362                 "returning %d (%s)", getpid (),
1363                 session->vpp_handle, session_index,
1364                 session->session_state,
1365                 vppcom_session_state_str (session->session_state),
1366                 STATE_DISCONNECT,
1367                 vppcom_session_state_str (STATE_DISCONNECT), rv,
1368                 vppcom_retval_str (rv));
1369           session->session_state = STATE_DISCONNECT;
1370           goto done;
1371         }
1372     }
1373   rv = ready;
1374 done:
1375   return rv;
1376 }
1377
1378 int
1379 vppcom_select (unsigned long n_bits, unsigned long *read_map,
1380                unsigned long *write_map, unsigned long *except_map,
1381                double time_to_wait)
1382 {
1383   u32 session_index;
1384   vcl_session_t *session = 0;
1385   int rv, bits_set = 0;
1386   f64 timeout = clib_time_now (&vcm->clib_time) + time_to_wait;
1387   u32 minbits = clib_max (n_bits, BITS (uword));
1388
1389   ASSERT (sizeof (clib_bitmap_t) == sizeof (long int));
1390
1391   if (n_bits && read_map)
1392     {
1393       clib_bitmap_validate (vcm->rd_bitmap, minbits);
1394       clib_memcpy (vcm->rd_bitmap, read_map,
1395                    vec_len (vcm->rd_bitmap) * sizeof (clib_bitmap_t));
1396       memset (read_map, 0, vec_len (vcm->rd_bitmap) * sizeof (clib_bitmap_t));
1397     }
1398   if (n_bits && write_map)
1399     {
1400       clib_bitmap_validate (vcm->wr_bitmap, minbits);
1401       clib_memcpy (vcm->wr_bitmap, write_map,
1402                    vec_len (vcm->wr_bitmap) * sizeof (clib_bitmap_t));
1403       memset (write_map, 0,
1404               vec_len (vcm->wr_bitmap) * sizeof (clib_bitmap_t));
1405     }
1406   if (n_bits && except_map)
1407     {
1408       clib_bitmap_validate (vcm->ex_bitmap, minbits);
1409       clib_memcpy (vcm->ex_bitmap, except_map,
1410                    vec_len (vcm->ex_bitmap) * sizeof (clib_bitmap_t));
1411       memset (except_map, 0,
1412               vec_len (vcm->ex_bitmap) * sizeof (clib_bitmap_t));
1413     }
1414
1415   do
1416     {
1417       /* *INDENT-OFF* */
1418       if (n_bits)
1419         {
1420           if (read_map)
1421             {
1422               clib_bitmap_foreach (session_index, vcm->rd_bitmap,
1423                 ({
1424                   VCL_SESSION_LOCK();
1425                   rv = vppcom_session_at_index (session_index, &session);
1426                   if (rv < 0)
1427                     {
1428                       VCL_SESSION_UNLOCK();
1429                       VDBG (1, "VCL<%d>: session %d specified in read_map is"
1430                           " closed.", getpid (),
1431                                       session_index);
1432                       bits_set = VPPCOM_EBADFD;
1433                       goto select_done;
1434                     }
1435                   if (session->session_state & STATE_LISTEN)
1436                     {
1437                       vce_event_handler_reg_t *reg = 0;
1438                       vce_event_key_t evk;
1439
1440                       /* Check if handler already registered for this
1441                        * event.
1442                        * If not, register handler for connect_request event
1443                        * on listen_session_index
1444                        */
1445                       evk.session_index = session_index;
1446                       evk.eid = VCL_EVENT_CONNECT_REQ_ACCEPTED;
1447                       reg = vce_get_event_handler (&vcm->event_thread, &evk);
1448                       if (!reg)
1449                         reg = vce_register_handler (&vcm->event_thread, &evk,
1450                                     vce_poll_wait_connect_request_handler_fn,
1451                                                     0 /* No callback args */);
1452                       rv = vppcom_session_read_ready (session, session_index);
1453                       if (rv > 0)
1454                         {
1455                           vce_unregister_handler (&vcm->event_thread, reg);
1456                         }
1457                     }
1458                   else
1459                     rv = vppcom_session_read_ready (session, session_index);
1460                   VCL_SESSION_UNLOCK();
1461                   if (except_map && vcm->ex_bitmap &&
1462                       clib_bitmap_get (vcm->ex_bitmap, session_index) &&
1463                       (rv < 0))
1464                     {
1465                       clib_bitmap_set_no_check (except_map, session_index, 1);
1466                       bits_set++;
1467                     }
1468                   else if (rv > 0)
1469                     {
1470                       clib_bitmap_set_no_check (read_map, session_index, 1);
1471                       bits_set++;
1472                     }
1473                 }));
1474             }
1475
1476           if (write_map)
1477             {
1478               clib_bitmap_foreach (session_index, vcm->wr_bitmap,
1479                 ({
1480                   VCL_SESSION_LOCK();
1481                   rv = vppcom_session_at_index (session_index, &session);
1482                   if (rv < 0)
1483                     {
1484                       VCL_SESSION_UNLOCK();
1485                       VDBG (0, "VCL<%d>: session %d specified in "
1486                                       "write_map is closed.", getpid (),
1487                                       session_index);
1488                       bits_set = VPPCOM_EBADFD;
1489                       goto select_done;
1490                     }
1491
1492                   rv = vppcom_session_write_ready (session, session_index);
1493                   VCL_SESSION_UNLOCK();
1494                   if (write_map && (rv > 0))
1495                     {
1496                       clib_bitmap_set_no_check (write_map, session_index, 1);
1497                       bits_set++;
1498                     }
1499                 }));
1500             }
1501
1502           if (except_map)
1503             {
1504               clib_bitmap_foreach (session_index, vcm->ex_bitmap,
1505                 ({
1506                   VCL_SESSION_LOCK();
1507                   rv = vppcom_session_at_index (session_index, &session);
1508                   if (rv < 0)
1509                     {
1510                       VCL_SESSION_UNLOCK();
1511                       VDBG (1, "VCL<%d>: session %d specified in except_map "
1512                           "is closed.", getpid (),
1513                                       session_index);
1514                       bits_set = VPPCOM_EBADFD;
1515                       goto select_done;
1516                     }
1517
1518                   rv = vppcom_session_read_ready (session, session_index);
1519                   VCL_SESSION_UNLOCK();
1520                   if (rv < 0)
1521                     {
1522                       clib_bitmap_set_no_check (except_map, session_index, 1);
1523                       bits_set++;
1524                     }
1525                 }));
1526             }
1527         }
1528       /* *INDENT-ON* */
1529     }
1530   while ((time_to_wait == -1) || (clib_time_now (&vcm->clib_time) < timeout));
1531
1532 select_done:
1533   return (bits_set);
1534 }
1535
1536 static inline void
1537 vep_verify_epoll_chain (u32 vep_idx)
1538 {
1539   vcl_session_t *session;
1540   vppcom_epoll_t *vep;
1541   int rv;
1542   u32 sid = vep_idx;
1543
1544   if (VPPCOM_DEBUG <= 1)
1545     return;
1546
1547   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1548   rv = vppcom_session_at_index (vep_idx, &session);
1549   if (PREDICT_FALSE (rv))
1550     {
1551       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!",
1552                     getpid (), vep_idx);
1553       goto done;
1554     }
1555   if (PREDICT_FALSE (!session->is_vep))
1556     {
1557       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
1558                     getpid (), vep_idx);
1559       goto done;
1560     }
1561   vep = &session->vep;
1562   clib_warning ("VCL<%d>: vep_idx (%u): Dumping epoll chain\n"
1563                 "{\n"
1564                 "   is_vep         = %u\n"
1565                 "   is_vep_session = %u\n"
1566                 "   next_sid       = 0x%x (%u)\n"
1567                 "   wait_cont_idx  = 0x%x (%u)\n"
1568                 "}\n", getpid (), vep_idx,
1569                 session->is_vep, session->is_vep_session,
1570                 vep->next_sid, vep->next_sid,
1571                 session->wait_cont_idx, session->wait_cont_idx);
1572
1573   for (sid = vep->next_sid; sid != ~0; sid = vep->next_sid)
1574     {
1575       rv = vppcom_session_at_index (sid, &session);
1576       if (PREDICT_FALSE (rv))
1577         {
1578           clib_warning ("VCL<%d>: ERROR: Invalid sid (%u)!", getpid (), sid);
1579           goto done;
1580         }
1581       if (PREDICT_FALSE (session->is_vep))
1582         clib_warning ("VCL<%d>: ERROR: sid (%u) is a vep!",
1583                       getpid (), vep_idx);
1584       else if (PREDICT_FALSE (!session->is_vep_session))
1585         {
1586           clib_warning ("VCL<%d>: ERROR: session (%u) "
1587                         "is not a vep session!", getpid (), sid);
1588           goto done;
1589         }
1590       vep = &session->vep;
1591       if (PREDICT_FALSE (vep->vep_idx != vep_idx))
1592         clib_warning ("VCL<%d>: ERROR: session (%u) vep_idx (%u) != "
1593                       "vep_idx (%u)!", getpid (),
1594                       sid, session->vep.vep_idx, vep_idx);
1595       if (session->is_vep_session)
1596         {
1597           clib_warning ("vep_idx[%u]: sid 0x%x (%u)\n"
1598                         "{\n"
1599                         "   next_sid       = 0x%x (%u)\n"
1600                         "   prev_sid       = 0x%x (%u)\n"
1601                         "   vep_idx        = 0x%x (%u)\n"
1602                         "   ev.events      = 0x%x\n"
1603                         "   ev.data.u64    = 0x%llx\n"
1604                         "   et_mask        = 0x%x\n"
1605                         "}\n",
1606                         vep_idx, sid, sid,
1607                         vep->next_sid, vep->next_sid,
1608                         vep->prev_sid, vep->prev_sid,
1609                         vep->vep_idx, vep->vep_idx,
1610                         vep->ev.events, vep->ev.data.u64, vep->et_mask);
1611         }
1612     }
1613
1614 done:
1615   clib_warning ("VCL<%d>: vep_idx (%u): Dump complete!\n",
1616                 getpid (), vep_idx);
1617 }
1618
1619 int
1620 vppcom_epoll_create (void)
1621 {
1622   vcl_session_t *vep_session;
1623   u32 vep_idx;
1624
1625   VCL_SESSION_LOCK ();
1626   pool_get (vcm->sessions, vep_session);
1627   memset (vep_session, 0, sizeof (*vep_session));
1628   vep_idx = vep_session - vcm->sessions;
1629
1630   vep_session->is_vep = 1;
1631   vep_session->vep.vep_idx = ~0;
1632   vep_session->vep.next_sid = ~0;
1633   vep_session->vep.prev_sid = ~0;
1634   vep_session->wait_cont_idx = ~0;
1635   vep_session->vpp_handle = ~0;
1636   vep_session->poll_reg = 0;
1637
1638   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_idx);
1639   VCL_SESSION_UNLOCK ();
1640
1641   VDBG (0, "VCL<%d>: Created vep_idx %u / sid %u!",
1642         getpid (), vep_idx, vep_idx);
1643
1644   return (vep_idx);
1645 }
1646
1647 int
1648 vppcom_epoll_ctl (uint32_t vep_idx, int op, uint32_t session_index,
1649                   struct epoll_event *event)
1650 {
1651   vcl_session_t *vep_session;
1652   vcl_session_t *session;
1653   int rv;
1654
1655   if (vep_idx == session_index)
1656     {
1657       clib_warning ("VCL<%d>: ERROR: vep_idx == session_index (%u)!",
1658                     getpid (), vep_idx);
1659       return VPPCOM_EINVAL;
1660     }
1661
1662   VCL_SESSION_LOCK ();
1663   rv = vppcom_session_at_index (vep_idx, &vep_session);
1664   if (PREDICT_FALSE (rv))
1665     {
1666       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!", vep_idx);
1667       goto done;
1668     }
1669   if (PREDICT_FALSE (!vep_session->is_vep))
1670     {
1671       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
1672                     getpid (), vep_idx);
1673       rv = VPPCOM_EINVAL;
1674       goto done;
1675     }
1676
1677   ASSERT (vep_session->vep.vep_idx == ~0);
1678   ASSERT (vep_session->vep.prev_sid == ~0);
1679
1680   rv = vppcom_session_at_index (session_index, &session);
1681   if (PREDICT_FALSE (rv))
1682     {
1683       VDBG (0, "VCL<%d>: ERROR: Invalid session_index (%u)!",
1684             getpid (), session_index);
1685       goto done;
1686     }
1687   if (PREDICT_FALSE (session->is_vep))
1688     {
1689       clib_warning ("ERROR: session_index (%u) is a vep!", vep_idx);
1690       rv = VPPCOM_EINVAL;
1691       goto done;
1692     }
1693
1694   switch (op)
1695     {
1696     case EPOLL_CTL_ADD:
1697       if (PREDICT_FALSE (!event))
1698         {
1699           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: NULL pointer to "
1700                         "epoll_event structure!", getpid ());
1701           rv = VPPCOM_EINVAL;
1702           goto done;
1703         }
1704       if (vep_session->vep.next_sid != ~0)
1705         {
1706           vcl_session_t *next_session;
1707           rv = vppcom_session_at_index (vep_session->vep.next_sid,
1708                                         &next_session);
1709           if (PREDICT_FALSE (rv))
1710             {
1711               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: Invalid "
1712                             "vep.next_sid (%u) on vep_idx (%u)!",
1713                             getpid (), vep_session->vep.next_sid, vep_idx);
1714               goto done;
1715             }
1716           ASSERT (next_session->vep.prev_sid == vep_idx);
1717           next_session->vep.prev_sid = session_index;
1718         }
1719       session->vep.next_sid = vep_session->vep.next_sid;
1720       session->vep.prev_sid = vep_idx;
1721       session->vep.vep_idx = vep_idx;
1722       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
1723       session->vep.ev = *event;
1724       session->is_vep = 0;
1725       session->is_vep_session = 1;
1726       vep_session->vep.next_sid = session_index;
1727
1728       /* VCL Event Register handler */
1729       if (session->session_state & STATE_LISTEN)
1730         {
1731           /* Register handler for connect_request event on listen_session_index */
1732           vce_event_key_t evk;
1733           evk.session_index = session_index;
1734           evk.eid = VCL_EVENT_CONNECT_REQ_ACCEPTED;
1735           vep_session->poll_reg =
1736             vce_register_handler (&vcm->event_thread, &evk,
1737                                   vce_poll_wait_connect_request_handler_fn,
1738                                   0 /* No callback args */ );
1739         }
1740       VDBG (1, "VCL<%d>: EPOLL_CTL_ADD: vep_idx %u, "
1741             "sid %u, events 0x%x, data 0x%llx!",
1742             getpid (), vep_idx, session_index,
1743             event->events, event->data.u64);
1744       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
1745       break;
1746
1747     case EPOLL_CTL_MOD:
1748       if (PREDICT_FALSE (!event))
1749         {
1750           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_MOD: NULL pointer to "
1751                         "epoll_event structure!", getpid ());
1752           rv = VPPCOM_EINVAL;
1753           goto done;
1754         }
1755       else if (PREDICT_FALSE (!session->is_vep_session))
1756         {
1757           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
1758                         "not a vep session!", getpid (), session_index);
1759           rv = VPPCOM_EINVAL;
1760           goto done;
1761         }
1762       else if (PREDICT_FALSE (session->vep.vep_idx != vep_idx))
1763         {
1764           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
1765                         "vep_idx (%u) != vep_idx (%u)!",
1766                         getpid (), session_index,
1767                         session->vep.vep_idx, vep_idx);
1768           rv = VPPCOM_EINVAL;
1769           goto done;
1770         }
1771       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
1772       session->vep.ev = *event;
1773       VDBG (1, "VCL<%d>: EPOLL_CTL_MOD: vep_idx %u, sid %u, events 0x%x,"
1774             " data 0x%llx!", getpid (), vep_idx, session_index, event->events,
1775             event->data.u64);
1776       break;
1777
1778     case EPOLL_CTL_DEL:
1779       if (PREDICT_FALSE (!session->is_vep_session))
1780         {
1781           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
1782                         "not a vep session!", getpid (), session_index);
1783           rv = VPPCOM_EINVAL;
1784           goto done;
1785         }
1786       else if (PREDICT_FALSE (session->vep.vep_idx != vep_idx))
1787         {
1788           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
1789                         "vep_idx (%u) != vep_idx (%u)!",
1790                         getpid (), session_index,
1791                         session->vep.vep_idx, vep_idx);
1792           rv = VPPCOM_EINVAL;
1793           goto done;
1794         }
1795
1796       /* VCL Event Un-register handler */
1797       if ((session->session_state & STATE_LISTEN) && vep_session->poll_reg)
1798         {
1799           (void) vce_unregister_handler (&vcm->event_thread,
1800                                          vep_session->poll_reg);
1801         }
1802
1803       vep_session->wait_cont_idx =
1804         (vep_session->wait_cont_idx == session_index) ?
1805         session->vep.next_sid : vep_session->wait_cont_idx;
1806
1807       if (session->vep.prev_sid == vep_idx)
1808         vep_session->vep.next_sid = session->vep.next_sid;
1809       else
1810         {
1811           vcl_session_t *prev_session;
1812           rv = vppcom_session_at_index (session->vep.prev_sid, &prev_session);
1813           if (PREDICT_FALSE (rv))
1814             {
1815               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
1816                             "vep.prev_sid (%u) on sid (%u)!",
1817                             getpid (), session->vep.prev_sid, session_index);
1818               goto done;
1819             }
1820           ASSERT (prev_session->vep.next_sid == session_index);
1821           prev_session->vep.next_sid = session->vep.next_sid;
1822         }
1823       if (session->vep.next_sid != ~0)
1824         {
1825           vcl_session_t *next_session;
1826           rv = vppcom_session_at_index (session->vep.next_sid, &next_session);
1827           if (PREDICT_FALSE (rv))
1828             {
1829               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
1830                             "vep.next_sid (%u) on sid (%u)!",
1831                             getpid (), session->vep.next_sid, session_index);
1832               goto done;
1833             }
1834           ASSERT (next_session->vep.prev_sid == session_index);
1835           next_session->vep.prev_sid = session->vep.prev_sid;
1836         }
1837
1838       memset (&session->vep, 0, sizeof (session->vep));
1839       session->vep.next_sid = ~0;
1840       session->vep.prev_sid = ~0;
1841       session->vep.vep_idx = ~0;
1842       session->is_vep_session = 0;
1843       VDBG (1, "VCL<%d>: EPOLL_CTL_DEL: vep_idx %u, sid %u!",
1844             getpid (), vep_idx, session_index);
1845       vcl_evt (VCL_EVT_EPOLL_CTLDEL, session, vep_idx);
1846       break;
1847
1848     default:
1849       clib_warning ("VCL<%d>: ERROR: Invalid operation (%d)!", getpid (), op);
1850       rv = VPPCOM_EINVAL;
1851     }
1852
1853   vep_verify_epoll_chain (vep_idx);
1854
1855 done:
1856   VCL_SESSION_UNLOCK ();
1857   return rv;
1858 }
1859
1860 int
1861 vppcom_epoll_wait (uint32_t vep_idx, struct epoll_event *events,
1862                    int maxevents, double wait_for_time)
1863 {
1864   vcl_session_t *vep_session;
1865   int rv;
1866   f64 timeout = clib_time_now (&vcm->clib_time) + wait_for_time;
1867   u32 keep_trying = 1;
1868   int num_ev = 0;
1869   u32 vep_next_sid, wait_cont_idx;
1870   u8 is_vep;
1871
1872   if (PREDICT_FALSE (maxevents <= 0))
1873     {
1874       clib_warning ("VCL<%d>: ERROR: Invalid maxevents (%d)!",
1875                     getpid (), maxevents);
1876       return VPPCOM_EINVAL;
1877     }
1878   memset (events, 0, sizeof (*events) * maxevents);
1879
1880   VCL_SESSION_LOCK_AND_GET (vep_idx, &vep_session);
1881   vep_next_sid = vep_session->vep.next_sid;
1882   is_vep = vep_session->is_vep;
1883   wait_cont_idx = vep_session->wait_cont_idx;
1884   VCL_SESSION_UNLOCK ();
1885
1886   if (PREDICT_FALSE (!is_vep))
1887     {
1888       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
1889                     getpid (), vep_idx);
1890       rv = VPPCOM_EINVAL;
1891       goto done;
1892     }
1893   if (PREDICT_FALSE (vep_next_sid == ~0))
1894     {
1895       VDBG (1, "VCL<%d>: WARNING: vep_idx (%u) is empty!",
1896             getpid (), vep_idx);
1897       goto done;
1898     }
1899
1900   do
1901     {
1902       u32 sid;
1903       u32 next_sid = ~0;
1904       vcl_session_t *session;
1905
1906       for (sid = (wait_cont_idx == ~0) ? vep_next_sid : wait_cont_idx;
1907            sid != ~0; sid = next_sid)
1908         {
1909           u32 session_events, et_mask, clear_et_mask, session_vep_idx;
1910           u8 add_event, is_vep_session;
1911           int ready;
1912           u64 session_ev_data;
1913
1914           VCL_SESSION_LOCK_AND_GET (sid, &session);
1915           next_sid = session->vep.next_sid;
1916           session_events = session->vep.ev.events;
1917           et_mask = session->vep.et_mask;
1918           is_vep = session->is_vep;
1919           is_vep_session = session->is_vep_session;
1920           session_vep_idx = session->vep.vep_idx;
1921           session_ev_data = session->vep.ev.data.u64;
1922
1923           VCL_SESSION_UNLOCK ();
1924
1925           if (PREDICT_FALSE (is_vep))
1926             {
1927               VDBG (0, "VCL<%d>: ERROR: sid (%u) is a vep!",
1928                     getpid (), vep_idx);
1929               rv = VPPCOM_EINVAL;
1930               goto done;
1931             }
1932           if (PREDICT_FALSE (!is_vep_session))
1933             {
1934               VDBG (0, "VCL<%d>: ERROR: session (%u) is not "
1935                     "a vep session!", getpid (), sid);
1936               rv = VPPCOM_EINVAL;
1937               goto done;
1938             }
1939           if (PREDICT_FALSE (session_vep_idx != vep_idx))
1940             {
1941               clib_warning ("VCL<%d>: ERROR: session (%u) "
1942                             "vep_idx (%u) != vep_idx (%u)!",
1943                             getpid (), sid, session_vep_idx, vep_idx);
1944               rv = VPPCOM_EINVAL;
1945               goto done;
1946             }
1947
1948           add_event = clear_et_mask = 0;
1949
1950           if (EPOLLIN & session_events)
1951             {
1952               VCL_SESSION_LOCK_AND_GET (sid, &session);
1953               ready = vppcom_session_read_ready (session, sid);
1954               VCL_SESSION_UNLOCK ();
1955               if ((ready > 0) && (EPOLLIN & et_mask))
1956                 {
1957                   add_event = 1;
1958                   events[num_ev].events |= EPOLLIN;
1959                   if (((EPOLLET | EPOLLIN) & session_events) ==
1960                       (EPOLLET | EPOLLIN))
1961                     clear_et_mask |= EPOLLIN;
1962                 }
1963               else if (ready < 0)
1964                 {
1965                   add_event = 1;
1966                   switch (ready)
1967                     {
1968                     case VPPCOM_ECONNRESET:
1969                       events[num_ev].events |= EPOLLHUP | EPOLLRDHUP;
1970                       break;
1971
1972                     default:
1973                       events[num_ev].events |= EPOLLERR;
1974                       break;
1975                     }
1976                 }
1977             }
1978
1979           if (EPOLLOUT & session_events)
1980             {
1981               VCL_SESSION_LOCK_AND_GET (sid, &session);
1982               ready = vppcom_session_write_ready (session, sid);
1983               VCL_SESSION_UNLOCK ();
1984               if ((ready > 0) && (EPOLLOUT & et_mask))
1985                 {
1986                   add_event = 1;
1987                   events[num_ev].events |= EPOLLOUT;
1988                   if (((EPOLLET | EPOLLOUT) & session_events) ==
1989                       (EPOLLET | EPOLLOUT))
1990                     clear_et_mask |= EPOLLOUT;
1991                 }
1992               else if (ready < 0)
1993                 {
1994                   add_event = 1;
1995                   switch (ready)
1996                     {
1997                     case VPPCOM_ECONNRESET:
1998                       events[num_ev].events |= EPOLLHUP;
1999                       break;
2000
2001                     default:
2002                       events[num_ev].events |= EPOLLERR;
2003                       break;
2004                     }
2005                 }
2006             }
2007
2008           if (add_event)
2009             {
2010               events[num_ev].data.u64 = session_ev_data;
2011               if (EPOLLONESHOT & session_events)
2012                 {
2013                   VCL_SESSION_LOCK_AND_GET (sid, &session);
2014                   session->vep.ev.events = 0;
2015                   VCL_SESSION_UNLOCK ();
2016                 }
2017               num_ev++;
2018               if (num_ev == maxevents)
2019                 {
2020                   VCL_SESSION_LOCK_AND_GET (vep_idx, &vep_session);
2021                   vep_session->wait_cont_idx = next_sid;
2022                   VCL_SESSION_UNLOCK ();
2023                   goto done;
2024                 }
2025             }
2026           if (wait_cont_idx != ~0)
2027             {
2028               if (next_sid == ~0)
2029                 next_sid = vep_next_sid;
2030               else if (next_sid == wait_cont_idx)
2031                 next_sid = ~0;
2032             }
2033         }
2034       if (wait_for_time != -1)
2035         keep_trying = (clib_time_now (&vcm->clib_time) <= timeout) ? 1 : 0;
2036     }
2037   while ((num_ev == 0) && keep_trying);
2038
2039   if (wait_cont_idx != ~0)
2040     {
2041       VCL_SESSION_LOCK_AND_GET (vep_idx, &vep_session);
2042       vep_session->wait_cont_idx = ~0;
2043       VCL_SESSION_UNLOCK ();
2044     }
2045 done:
2046   return (rv != VPPCOM_OK) ? rv : num_ev;
2047 }
2048
2049 int
2050 vppcom_session_attr (uint32_t session_index, uint32_t op,
2051                      void *buffer, uint32_t * buflen)
2052 {
2053   vcl_session_t *session;
2054   int rv = VPPCOM_OK;
2055   u32 *flags = buffer;
2056   vppcom_endpt_t *ep = buffer;
2057
2058   VCL_SESSION_LOCK_AND_GET (session_index, &session);
2059
2060   ASSERT (session);
2061
2062   switch (op)
2063     {
2064     case VPPCOM_ATTR_GET_NREAD:
2065       rv = vppcom_session_read_ready (session, session_index);
2066       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_NREAD: sid %u, nread = %d",
2067             getpid (), rv);
2068       break;
2069
2070     case VPPCOM_ATTR_GET_NWRITE:
2071       rv = vppcom_session_write_ready (session, session_index);
2072       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_NWRITE: sid %u, nwrite = %d",
2073             getpid (), session_index, rv);
2074       break;
2075
2076     case VPPCOM_ATTR_GET_FLAGS:
2077       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
2078         {
2079           *flags = O_RDWR | (VCL_SESS_ATTR_TEST (session->attr,
2080                                                  VCL_SESS_ATTR_NONBLOCK));
2081           *buflen = sizeof (*flags);
2082           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_FLAGS: sid %u, flags = 0x%08x, "
2083                 "is_nonblocking = %u", getpid (),
2084                 session_index, *flags,
2085                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2086         }
2087       else
2088         rv = VPPCOM_EINVAL;
2089       break;
2090
2091     case VPPCOM_ATTR_SET_FLAGS:
2092       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
2093         {
2094           if (*flags & O_NONBLOCK)
2095             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
2096           else
2097             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_NONBLOCK);
2098
2099           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_FLAGS: sid %u, flags = 0x%08x,"
2100                 " is_nonblocking = %u",
2101                 getpid (), session_index, *flags,
2102                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2103         }
2104       else
2105         rv = VPPCOM_EINVAL;
2106       break;
2107
2108     case VPPCOM_ATTR_GET_PEER_ADDR:
2109       if (PREDICT_TRUE (buffer && buflen &&
2110                         (*buflen >= sizeof (*ep)) && ep->ip))
2111         {
2112           ep->is_ip4 = session->transport.is_ip4;
2113           ep->port = session->transport.rmt_port;
2114           if (session->transport.is_ip4)
2115             clib_memcpy (ep->ip, &session->transport.rmt_ip.ip4,
2116                          sizeof (ip4_address_t));
2117           else
2118             clib_memcpy (ep->ip, &session->transport.rmt_ip.ip6,
2119                          sizeof (ip6_address_t));
2120           *buflen = sizeof (*ep);
2121           VDBG (1, "VCL<%d>: VPPCOM_ATTR_GET_PEER_ADDR: sid %u, is_ip4 = %u, "
2122                 "addr = %U, port %u", getpid (),
2123                 session_index, ep->is_ip4, format_ip46_address,
2124                 &session->transport.rmt_ip,
2125                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2126                 clib_net_to_host_u16 (ep->port));
2127         }
2128       else
2129         rv = VPPCOM_EINVAL;
2130       break;
2131
2132     case VPPCOM_ATTR_GET_LCL_ADDR:
2133       if (PREDICT_TRUE (buffer && buflen &&
2134                         (*buflen >= sizeof (*ep)) && ep->ip))
2135         {
2136           ep->is_ip4 = session->transport.is_ip4;
2137           ep->port = session->transport.lcl_port;
2138           if (session->transport.is_ip4)
2139             clib_memcpy (ep->ip, &session->transport.lcl_ip.ip4,
2140                          sizeof (ip4_address_t));
2141           else
2142             clib_memcpy (ep->ip, &session->transport.lcl_ip.ip6,
2143                          sizeof (ip6_address_t));
2144           *buflen = sizeof (*ep);
2145           VDBG (1, "VCL<%d>: VPPCOM_ATTR_GET_LCL_ADDR: sid %u, is_ip4 = %u,"
2146                 " addr = %U port %d", getpid (),
2147                 session_index, ep->is_ip4, format_ip46_address,
2148                 &session->transport.lcl_ip,
2149                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2150                 clib_net_to_host_u16 (ep->port));
2151         }
2152       else
2153         rv = VPPCOM_EINVAL;
2154       break;
2155
2156     case VPPCOM_ATTR_GET_LIBC_EPFD:
2157       rv = session->libc_epfd;
2158       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d",
2159             getpid (), rv);
2160       break;
2161
2162     case VPPCOM_ATTR_SET_LIBC_EPFD:
2163       if (PREDICT_TRUE (buffer && buflen &&
2164                         (*buflen == sizeof (session->libc_epfd))))
2165         {
2166           session->libc_epfd = *(int *) buffer;
2167           *buflen = sizeof (session->libc_epfd);
2168
2169           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, "
2170                 "buflen %d", getpid (), session->libc_epfd, *buflen);
2171         }
2172       else
2173         rv = VPPCOM_EINVAL;
2174       break;
2175
2176     case VPPCOM_ATTR_GET_PROTOCOL:
2177       if (buffer && buflen && (*buflen >= sizeof (int)))
2178         {
2179           *(int *) buffer = session->session_type;
2180           *buflen = sizeof (int);
2181
2182           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
2183                 getpid (), *(int *) buffer, *(int *) buffer ? "UDP" : "TCP",
2184                 *buflen);
2185         }
2186       else
2187         rv = VPPCOM_EINVAL;
2188       break;
2189
2190     case VPPCOM_ATTR_GET_LISTEN:
2191       if (buffer && buflen && (*buflen >= sizeof (int)))
2192         {
2193           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2194                                                 VCL_SESS_ATTR_LISTEN);
2195           *buflen = sizeof (int);
2196
2197           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_LISTEN: %d, buflen %d",
2198                 getpid (), *(int *) buffer, *buflen);
2199         }
2200       else
2201         rv = VPPCOM_EINVAL;
2202       break;
2203
2204     case VPPCOM_ATTR_GET_ERROR:
2205       if (buffer && buflen && (*buflen >= sizeof (int)))
2206         {
2207           *(int *) buffer = 0;
2208           *buflen = sizeof (int);
2209
2210           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
2211                 getpid (), *(int *) buffer, *buflen);
2212         }
2213       else
2214         rv = VPPCOM_EINVAL;
2215       break;
2216
2217     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
2218       if (buffer && buflen && (*buflen >= sizeof (u32)))
2219         {
2220
2221           /* VPP-TBD */
2222           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
2223                                 session->tx_fifo ? session->tx_fifo->nitems :
2224                                 vcm->cfg.tx_fifo_size);
2225           *buflen = sizeof (u32);
2226
2227           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), "
2228                 "buflen %d, #VPP-TBD#", getpid (),
2229                 *(size_t *) buffer, *(size_t *) buffer, *buflen);
2230         }
2231       else
2232         rv = VPPCOM_EINVAL;
2233       break;
2234
2235     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
2236       if (buffer && buflen && (*buflen == sizeof (u32)))
2237         {
2238           /* VPP-TBD */
2239           session->sndbuf_size = *(u32 *) buffer;
2240           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), "
2241                 "buflen %d, #VPP-TBD#", getpid (),
2242                 session->sndbuf_size, session->sndbuf_size, *buflen);
2243         }
2244       else
2245         rv = VPPCOM_EINVAL;
2246       break;
2247
2248     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
2249       if (buffer && buflen && (*buflen >= sizeof (u32)))
2250         {
2251
2252           /* VPP-TBD */
2253           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
2254                                 session->rx_fifo ? session->rx_fifo->nitems :
2255                                 vcm->cfg.rx_fifo_size);
2256           *buflen = sizeof (u32);
2257
2258           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), "
2259                 "buflen %d, #VPP-TBD#", getpid (),
2260                 *(size_t *) buffer, *(size_t *) buffer, *buflen);
2261         }
2262       else
2263         rv = VPPCOM_EINVAL;
2264       break;
2265
2266     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
2267       if (buffer && buflen && (*buflen == sizeof (u32)))
2268         {
2269           /* VPP-TBD */
2270           session->rcvbuf_size = *(u32 *) buffer;
2271           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), "
2272                 "buflen %d, #VPP-TBD#", getpid (),
2273                 session->sndbuf_size, session->sndbuf_size, *buflen);
2274         }
2275       else
2276         rv = VPPCOM_EINVAL;
2277       break;
2278
2279     case VPPCOM_ATTR_GET_REUSEADDR:
2280       if (buffer && buflen && (*buflen >= sizeof (int)))
2281         {
2282           /* VPP-TBD */
2283           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2284                                                 VCL_SESS_ATTR_REUSEADDR);
2285           *buflen = sizeof (int);
2286
2287           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_REUSEADDR: %d, "
2288                 "buflen %d, #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2289         }
2290       else
2291         rv = VPPCOM_EINVAL;
2292       break;
2293
2294     case VPPCOM_ATTR_SET_REUSEADDR:
2295       if (buffer && buflen && (*buflen == sizeof (int)) &&
2296           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
2297         {
2298           /* VPP-TBD */
2299           if (*(int *) buffer)
2300             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEADDR);
2301           else
2302             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEADDR);
2303
2304           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d,"
2305                 " #VPP-TBD#", getpid (),
2306                 VCL_SESS_ATTR_TEST (session->attr,
2307                                     VCL_SESS_ATTR_REUSEADDR), *buflen);
2308         }
2309       else
2310         rv = VPPCOM_EINVAL;
2311       break;
2312
2313     case VPPCOM_ATTR_GET_REUSEPORT:
2314       if (buffer && buflen && (*buflen >= sizeof (int)))
2315         {
2316           /* VPP-TBD */
2317           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2318                                                 VCL_SESS_ATTR_REUSEPORT);
2319           *buflen = sizeof (int);
2320
2321           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d,"
2322                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2323         }
2324       else
2325         rv = VPPCOM_EINVAL;
2326       break;
2327
2328     case VPPCOM_ATTR_SET_REUSEPORT:
2329       if (buffer && buflen && (*buflen == sizeof (int)) &&
2330           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
2331         {
2332           /* VPP-TBD */
2333           if (*(int *) buffer)
2334             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEPORT);
2335           else
2336             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEPORT);
2337
2338           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d,"
2339                 " #VPP-TBD#", getpid (),
2340                 VCL_SESS_ATTR_TEST (session->attr,
2341                                     VCL_SESS_ATTR_REUSEPORT), *buflen);
2342         }
2343       else
2344         rv = VPPCOM_EINVAL;
2345       break;
2346
2347     case VPPCOM_ATTR_GET_BROADCAST:
2348       if (buffer && buflen && (*buflen >= sizeof (int)))
2349         {
2350           /* VPP-TBD */
2351           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2352                                                 VCL_SESS_ATTR_BROADCAST);
2353           *buflen = sizeof (int);
2354
2355           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d,"
2356                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2357         }
2358       else
2359         rv = VPPCOM_EINVAL;
2360       break;
2361
2362     case VPPCOM_ATTR_SET_BROADCAST:
2363       if (buffer && buflen && (*buflen == sizeof (int)))
2364         {
2365           /* VPP-TBD */
2366           if (*(int *) buffer)
2367             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_BROADCAST);
2368           else
2369             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_BROADCAST);
2370
2371           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, "
2372                 "#VPP-TBD#", getpid (),
2373                 VCL_SESS_ATTR_TEST (session->attr,
2374                                     VCL_SESS_ATTR_BROADCAST), *buflen);
2375         }
2376       else
2377         rv = VPPCOM_EINVAL;
2378       break;
2379
2380     case VPPCOM_ATTR_GET_V6ONLY:
2381       if (buffer && buflen && (*buflen >= sizeof (int)))
2382         {
2383           /* VPP-TBD */
2384           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2385                                                 VCL_SESS_ATTR_V6ONLY);
2386           *buflen = sizeof (int);
2387
2388           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, "
2389                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2390         }
2391       else
2392         rv = VPPCOM_EINVAL;
2393       break;
2394
2395     case VPPCOM_ATTR_SET_V6ONLY:
2396       if (buffer && buflen && (*buflen == sizeof (int)))
2397         {
2398           /* VPP-TBD */
2399           if (*(int *) buffer)
2400             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_V6ONLY);
2401           else
2402             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_V6ONLY);
2403
2404           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, "
2405                 "#VPP-TBD#", getpid (),
2406                 VCL_SESS_ATTR_TEST (session->attr,
2407                                     VCL_SESS_ATTR_V6ONLY), *buflen);
2408         }
2409       else
2410         rv = VPPCOM_EINVAL;
2411       break;
2412
2413     case VPPCOM_ATTR_GET_KEEPALIVE:
2414       if (buffer && buflen && (*buflen >= sizeof (int)))
2415         {
2416           /* VPP-TBD */
2417           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2418                                                 VCL_SESS_ATTR_KEEPALIVE);
2419           *buflen = sizeof (int);
2420
2421           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, "
2422                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2423         }
2424       else
2425         rv = VPPCOM_EINVAL;
2426       break;
2427
2428     case VPPCOM_ATTR_SET_KEEPALIVE:
2429       if (buffer && buflen && (*buflen == sizeof (int)))
2430         {
2431           /* VPP-TBD */
2432           if (*(int *) buffer)
2433             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_KEEPALIVE);
2434           else
2435             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_KEEPALIVE);
2436
2437           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, "
2438                 "#VPP-TBD#", getpid (),
2439                 VCL_SESS_ATTR_TEST (session->attr,
2440                                     VCL_SESS_ATTR_KEEPALIVE), *buflen);
2441         }
2442       else
2443         rv = VPPCOM_EINVAL;
2444       break;
2445
2446     case VPPCOM_ATTR_GET_TCP_NODELAY:
2447       if (buffer && buflen && (*buflen >= sizeof (int)))
2448         {
2449           /* VPP-TBD */
2450           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2451                                                 VCL_SESS_ATTR_TCP_NODELAY);
2452           *buflen = sizeof (int);
2453
2454           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, "
2455                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2456         }
2457       else
2458         rv = VPPCOM_EINVAL;
2459       break;
2460
2461     case VPPCOM_ATTR_SET_TCP_NODELAY:
2462       if (buffer && buflen && (*buflen == sizeof (int)))
2463         {
2464           /* VPP-TBD */
2465           if (*(int *) buffer)
2466             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
2467           else
2468             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
2469
2470           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, "
2471                 "#VPP-TBD#", getpid (),
2472                 VCL_SESS_ATTR_TEST (session->attr,
2473                                     VCL_SESS_ATTR_TCP_NODELAY), *buflen);
2474         }
2475       else
2476         rv = VPPCOM_EINVAL;
2477       break;
2478
2479     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
2480       if (buffer && buflen && (*buflen >= sizeof (int)))
2481         {
2482           /* VPP-TBD */
2483           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2484                                                 VCL_SESS_ATTR_TCP_KEEPIDLE);
2485           *buflen = sizeof (int);
2486
2487           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, "
2488                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2489         }
2490       else
2491         rv = VPPCOM_EINVAL;
2492       break;
2493
2494     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
2495       if (buffer && buflen && (*buflen == sizeof (int)))
2496         {
2497           /* VPP-TBD */
2498           if (*(int *) buffer)
2499             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
2500           else
2501             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
2502
2503           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, "
2504                 "#VPP-TBD#", getpid (),
2505                 VCL_SESS_ATTR_TEST (session->attr,
2506                                     VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
2507         }
2508       else
2509         rv = VPPCOM_EINVAL;
2510       break;
2511
2512     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
2513       if (buffer && buflen && (*buflen >= sizeof (int)))
2514         {
2515           /* VPP-TBD */
2516           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2517                                                 VCL_SESS_ATTR_TCP_KEEPINTVL);
2518           *buflen = sizeof (int);
2519
2520           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, "
2521                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2522         }
2523       else
2524         rv = VPPCOM_EINVAL;
2525       break;
2526
2527     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
2528       if (buffer && buflen && (*buflen == sizeof (int)))
2529         {
2530           /* VPP-TBD */
2531           if (*(int *) buffer)
2532             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
2533           else
2534             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
2535
2536           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, "
2537                 "#VPP-TBD#", getpid (),
2538                 VCL_SESS_ATTR_TEST (session->attr,
2539                                     VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
2540         }
2541       else
2542         rv = VPPCOM_EINVAL;
2543       break;
2544
2545     case VPPCOM_ATTR_GET_TCP_USER_MSS:
2546       if (buffer && buflen && (*buflen >= sizeof (u32)))
2547         {
2548           /* VPP-TBD */
2549           *(u32 *) buffer = session->user_mss;
2550           *buflen = sizeof (int);
2551
2552           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d,"
2553                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2554         }
2555       else
2556         rv = VPPCOM_EINVAL;
2557       break;
2558
2559     case VPPCOM_ATTR_SET_TCP_USER_MSS:
2560       if (buffer && buflen && (*buflen == sizeof (u32)))
2561         {
2562           /* VPP-TBD */
2563           session->user_mss = *(u32 *) buffer;
2564
2565           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d, "
2566                 "#VPP-TBD#", getpid (), session->user_mss, *buflen);
2567         }
2568       else
2569         rv = VPPCOM_EINVAL;
2570       break;
2571
2572     default:
2573       rv = VPPCOM_EINVAL;
2574       break;
2575     }
2576
2577 done:
2578   VCL_SESSION_UNLOCK ();
2579   return rv;
2580 }
2581
2582 int
2583 vppcom_session_recvfrom (uint32_t session_index, void *buffer,
2584                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
2585 {
2586   int rv = VPPCOM_OK;
2587   vcl_session_t *session = 0;
2588
2589   if (ep)
2590     {
2591       VCL_SESSION_LOCK ();
2592       rv = vppcom_session_at_index (session_index, &session);
2593       if (PREDICT_FALSE (rv))
2594         {
2595           VCL_SESSION_UNLOCK ();
2596           VDBG (0, "VCL<%d>: invalid session, sid (%u) has been closed!",
2597                 getpid (), session_index);
2598           rv = VPPCOM_EBADFD;
2599           VCL_SESSION_UNLOCK ();
2600           goto done;
2601         }
2602       ep->is_ip4 = session->transport.is_ip4;
2603       ep->port = session->transport.rmt_port;
2604       if (session->transport.is_ip4)
2605         clib_memcpy (ep->ip, &session->transport.rmt_ip.ip4,
2606                      sizeof (ip4_address_t));
2607       else
2608         clib_memcpy (ep->ip, &session->transport.rmt_ip.ip6,
2609                      sizeof (ip6_address_t));
2610       VCL_SESSION_UNLOCK ();
2611     }
2612
2613   if (flags == 0)
2614     rv = vppcom_session_read (session_index, buffer, buflen);
2615   else if (flags & MSG_PEEK)
2616     rv = vppcom_session_peek (session_index, buffer, buflen);
2617   else
2618     {
2619       clib_warning ("VCL<%d>: Unsupport flags for recvfrom %d",
2620                     getpid (), flags);
2621       rv = VPPCOM_EAFNOSUPPORT;
2622     }
2623
2624 done:
2625   return rv;
2626 }
2627
2628 int
2629 vppcom_session_sendto (uint32_t session_index, void *buffer,
2630                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
2631 {
2632   if (!buffer)
2633     return VPPCOM_EINVAL;
2634
2635   if (ep)
2636     {
2637       // TBD
2638       return VPPCOM_EINVAL;
2639     }
2640
2641   if (flags)
2642     {
2643       // TBD check the flags and do the right thing
2644       VDBG (2, "VCL<%d>: handling flags 0x%u (%d) not implemented yet.",
2645             getpid (), flags, flags);
2646     }
2647
2648   return (vppcom_session_write (session_index, buffer, buflen));
2649 }
2650
2651 int
2652 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
2653 {
2654   f64 timeout = clib_time_now (&vcm->clib_time) + wait_for_time;
2655   u32 i, keep_trying = 1;
2656   int rv, num_ev = 0;
2657
2658   VDBG (3, "VCL<%d>: vp %p, nsids %u, wait_for_time %f",
2659         getpid (), vp, n_sids, wait_for_time);
2660
2661   if (!vp)
2662     return VPPCOM_EFAULT;
2663
2664   do
2665     {
2666       vcl_session_t *session;
2667
2668       for (i = 0; i < n_sids; i++)
2669         {
2670           ASSERT (vp[i].revents);
2671
2672           VCL_SESSION_LOCK_AND_GET (vp[i].sid, &session);
2673           VCL_SESSION_UNLOCK ();
2674
2675           if (*vp[i].revents)
2676             *vp[i].revents = 0;
2677
2678           if (POLLIN & vp[i].events)
2679             {
2680               VCL_SESSION_LOCK_AND_GET (vp[i].sid, &session);
2681               rv = vppcom_session_read_ready (session, vp[i].sid);
2682               VCL_SESSION_UNLOCK ();
2683               if (rv > 0)
2684                 {
2685                   *vp[i].revents |= POLLIN;
2686                   num_ev++;
2687                 }
2688               else if (rv < 0)
2689                 {
2690                   switch (rv)
2691                     {
2692                     case VPPCOM_ECONNRESET:
2693                       *vp[i].revents = POLLHUP;
2694                       break;
2695
2696                     default:
2697                       *vp[i].revents = POLLERR;
2698                       break;
2699                     }
2700                   num_ev++;
2701                 }
2702             }
2703
2704           if (POLLOUT & vp[i].events)
2705             {
2706               VCL_SESSION_LOCK_AND_GET (vp[i].sid, &session);
2707               rv = vppcom_session_write_ready (session, vp[i].sid);
2708               VCL_SESSION_UNLOCK ();
2709               if (rv > 0)
2710                 {
2711                   *vp[i].revents |= POLLOUT;
2712                   num_ev++;
2713                 }
2714               else if (rv < 0)
2715                 {
2716                   switch (rv)
2717                     {
2718                     case VPPCOM_ECONNRESET:
2719                       *vp[i].revents = POLLHUP;
2720                       break;
2721
2722                     default:
2723                       *vp[i].revents = POLLERR;
2724                       break;
2725                     }
2726                   num_ev++;
2727                 }
2728             }
2729
2730           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
2731             {
2732             done:
2733               *vp[i].revents = POLLNVAL;
2734               num_ev++;
2735             }
2736         }
2737       if (wait_for_time != -1)
2738         keep_trying = (clib_time_now (&vcm->clib_time) <= timeout) ? 1 : 0;
2739     }
2740   while ((num_ev == 0) && keep_trying);
2741
2742   if (VPPCOM_DEBUG > 3)
2743     {
2744       clib_warning ("VCL<%d>: returning %d", getpid (), num_ev);
2745       for (i = 0; i < n_sids; i++)
2746         {
2747           clib_warning ("VCL<%d>: vp[%d].sid %d (0x%x), .events 0x%x, "
2748                         ".revents 0x%x", getpid (), i, vp[i].sid, vp[i].sid,
2749                         vp[i].events, *vp[i].revents);
2750         }
2751     }
2752   return num_ev;
2753 }
2754
2755 /*
2756  * fd.io coding-style-patch-verification: ON
2757  *
2758  * Local Variables:
2759  * eval: (c-set-style "gnu")
2760  * End:
2761  */