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