VCL: Set debug output level from env var
[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 <signal.h>
19 #include <svm/svm_fifo_segment.h>
20 #include <vlibmemory/api.h>
21 #include <vpp/api/vpe_msg_enum.h>
22 #include <vnet/session/application_interface.h>
23 #include <vcl/vppcom.h>
24 #include <vlib/unix/unix.h>
25 #include <vppinfra/vec_bootstrap.h>
26
27 #define vl_typedefs             /* define message structures */
28 #include <vpp/api/vpe_all_api_h.h>
29 #undef vl_typedefs
30
31 /* declare message handlers for each api */
32
33 #define vl_endianfun            /* define message structures */
34 #include <vpp/api/vpe_all_api_h.h>
35 #undef vl_endianfun
36
37 /* instantiate all the print functions we know about */
38 #define vl_print(handle, ...)
39 #define vl_printfun
40 #include <vpp/api/vpe_all_api_h.h>
41 #undef vl_printfun
42
43 #if (CLIB_DEBUG > 0)
44 /* Set VPPCOM_DEBUG_INIT 2 for connection debug,
45  *                       3 for read/write debug output
46  * or
47  *    export VCL_DEBUG=<#> to set dynamically.
48  */
49 #define VPPCOM_DEBUG_INIT 1
50 #else
51 #define VPPCOM_DEBUG_INIT 0
52 #endif
53
54 #define VPPCOM_DEBUG vcm->debug
55
56 /*
57  * VPPCOM Private definitions and functions.
58  */
59 typedef enum
60 {
61   STATE_APP_START,
62   STATE_APP_CONN_VPP,
63   STATE_APP_ENABLED,
64   STATE_APP_ATTACHED,
65 } app_state_t;
66
67 typedef enum
68 {
69   STATE_START,
70   STATE_CONNECT,
71   STATE_LISTEN,
72   STATE_ACCEPT,
73   STATE_DISCONNECT,
74   STATE_FAILED
75 } session_state_t;
76
77 typedef struct epoll_event vppcom_epoll_event_t;
78
79 typedef struct
80 {
81   u32 next_sid;
82   u32 prev_sid;
83   u32 vep_idx;
84   vppcom_epoll_event_t ev;
85 #define VEP_DEFAULT_ET_MASK  (EPOLLIN|EPOLLOUT)
86   u32 et_mask;
87 } vppcom_epoll_t;
88
89 typedef struct
90 {
91   u8 is_ip4;
92   ip46_address_t ip46;
93 } vppcom_ip46_t;
94
95 typedef struct
96 {
97   volatile session_state_t state;
98
99   svm_fifo_t *server_rx_fifo;
100   svm_fifo_t *server_tx_fifo;
101   u32 sm_seg_index;
102   u64 vpp_session_handle;
103   unix_shared_memory_queue_t *vpp_event_queue;
104
105   /* Socket configuration state */
106   /* TBD: covert 'is_*' vars to bit in u8 flags; */
107   u8 is_server;
108   u8 is_listen;
109   u8 is_cut_thru;
110   u8 is_nonblocking;
111   u8 is_vep;
112   u8 is_vep_session;
113   u32 wait_cont_idx;
114   vppcom_epoll_t vep;
115   u32 vrf;
116   vppcom_ip46_t lcl_addr;
117   vppcom_ip46_t peer_addr;
118   u16 lcl_port;                 // network order
119   u16 peer_port;                // network order
120   u8 proto;
121   u64 client_queue_address;
122   u64 options[16];
123 } session_t;
124
125 typedef struct vppcom_cfg_t_
126 {
127   u64 heapsize;
128   u64 segment_baseva;
129   u32 segment_size;
130   u32 add_segment_size;
131   u32 preallocated_fifo_pairs;
132   u32 rx_fifo_size;
133   u32 tx_fifo_size;
134   u32 event_queue_size;
135   u32 listen_queue_size;
136   u8 app_proxy_transport_tcp;
137   u8 app_proxy_transport_udp;
138   u8 app_scope_local;
139   u8 app_scope_global;
140   u8 *namespace_id;
141   u64 namespace_secret;
142   f64 app_timeout;
143   f64 session_timeout;
144   f64 accept_timeout;
145 } vppcom_cfg_t;
146
147 typedef struct vppcom_main_t_
148 {
149   u8 init;
150   u32 debug;
151   u32 *client_session_index_fifo;
152   volatile u32 bind_session_index;
153   int main_cpu;
154
155   /* vpe input queue */
156   unix_shared_memory_queue_t *vl_input_queue;
157
158   /* API client handle */
159   u32 my_client_index;
160
161   /* Session pool */
162   clib_spinlock_t sessions_lockp;
163   session_t *sessions;
164
165   /* Hash table for disconnect processing */
166   uword *session_index_by_vpp_handles;
167
168   /* Select bitmaps */
169   clib_bitmap_t *rd_bitmap;
170   clib_bitmap_t *wr_bitmap;
171   clib_bitmap_t *ex_bitmap;
172
173   /* Our event queue */
174   unix_shared_memory_queue_t *app_event_queue;
175
176   /* unique segment name counter */
177   u32 unique_segment_index;
178
179   /* For deadman timers */
180   clib_time_t clib_time;
181
182   /* State of the connection, shared between msg RX thread and main thread */
183   volatile app_state_t app_state;
184
185   vppcom_cfg_t cfg;
186
187   /* VNET_API_ERROR_FOO -> "Foo" hash table */
188   uword *error_string_by_error_number;
189 } vppcom_main_t;
190
191 /* NOTE: _vppcom_main is only used until the heap is allocated.
192  *       Do not access it directly -- use vcm which will point to
193  *       the heap allocated copy after init.
194  */
195 static vppcom_main_t _vppcom_main = {
196   .debug = VPPCOM_DEBUG_INIT,
197   .my_client_index = ~0
198 };
199
200 static vppcom_main_t *vcm = &_vppcom_main;
201
202 static const char *
203 vppcom_app_state_str (app_state_t state)
204 {
205   char *st;
206
207   switch (state)
208     {
209     case STATE_APP_START:
210       st = "STATE_APP_START";
211       break;
212
213     case STATE_APP_CONN_VPP:
214       st = "STATE_APP_CONN_VPP";
215       break;
216
217     case STATE_APP_ENABLED:
218       st = "STATE_APP_ENABLED";
219       break;
220
221     case STATE_APP_ATTACHED:
222       st = "STATE_APP_ATTACHED";
223       break;
224
225     default:
226       st = "UNKNOWN_APP_STATE";
227       break;
228     }
229
230   return st;
231 }
232
233 static const char *
234 vppcom_session_state_str (session_state_t state)
235 {
236   char *st;
237
238   switch (state)
239     {
240     case STATE_START:
241       st = "STATE_START";
242       break;
243
244     case STATE_CONNECT:
245       st = "STATE_CONNECT";
246       break;
247
248     case STATE_LISTEN:
249       st = "STATE_LISTEN";
250       break;
251
252     case STATE_ACCEPT:
253       st = "STATE_ACCEPT";
254       break;
255
256     case STATE_DISCONNECT:
257       st = "STATE_DISCONNECT";
258       break;
259
260     case STATE_FAILED:
261       st = "STATE_FAILED";
262       break;
263
264     default:
265       st = "UNKNOWN_STATE";
266       break;
267     }
268
269   return st;
270 }
271
272 /*
273  * VPPCOM Utility Functions
274  */
275 static inline int
276 vppcom_session_at_index (u32 session_index, session_t * volatile *sess)
277 {
278   /* Assumes that caller has acquired spinlock: vcm->sessions_lockp */
279   if (PREDICT_FALSE ((session_index == ~0) ||
280                      pool_is_free_index (vcm->sessions, session_index)))
281     {
282       clib_warning ("[%d] invalid session, sid (%u) has been closed!",
283                     getpid (), session_index);
284       return VPPCOM_EBADFD;
285     }
286   *sess = pool_elt_at_index (vcm->sessions, session_index);
287   return VPPCOM_OK;
288 }
289
290 static int
291 vppcom_connect_to_vpp (char *app_name)
292 {
293   api_main_t *am = &api_main;
294
295   if (VPPCOM_DEBUG > 0)
296     printf ("\nConnecting to VPP api...");
297   if (vl_client_connect_to_vlib ("/vpe-api", app_name, 32) < 0)
298     {
299       clib_warning ("[%d] connect to vpp (%s) failed!", getpid (), app_name);
300       return VPPCOM_ECONNREFUSED;
301     }
302
303   vcm->vl_input_queue = am->shmem_hdr->vl_input_queue;
304   vcm->my_client_index = am->my_client_index;
305   if (VPPCOM_DEBUG > 0)
306     printf (" connected!\n");
307
308   vcm->app_state = STATE_APP_CONN_VPP;
309   return VPPCOM_OK;
310 }
311
312 static u8 *
313 format_api_error (u8 * s, va_list * args)
314 {
315   i32 error = va_arg (*args, u32);
316   uword *p;
317
318   p = hash_get (vcm->error_string_by_error_number, -error);
319
320   if (p)
321     s = format (s, "%s (%d)", p[0], error);
322   else
323     s = format (s, "%d", error);
324   return s;
325 }
326
327 static void
328 vppcom_init_error_string_table (void)
329 {
330   vcm->error_string_by_error_number = hash_create (0, sizeof (uword));
331
332 #define _(n,v,s) hash_set (vcm->error_string_by_error_number, -v, s);
333   foreach_vnet_api_error;
334 #undef _
335
336   hash_set (vcm->error_string_by_error_number, 99, "Misc");
337 }
338
339 static inline int
340 vppcom_wait_for_app_state_change (app_state_t app_state)
341 {
342   f64 timeout = clib_time_now (&vcm->clib_time) + vcm->cfg.app_timeout;
343
344   while (clib_time_now (&vcm->clib_time) < timeout)
345     {
346       if (vcm->app_state == app_state)
347         return VPPCOM_OK;
348     }
349   if (VPPCOM_DEBUG > 0)
350     clib_warning ("[%d] timeout waiting for state %s (%d)", getpid (),
351                   vppcom_app_state_str (app_state), app_state);
352   return VPPCOM_ETIMEDOUT;
353 }
354
355 static inline int
356 vppcom_wait_for_session_state_change (u32 session_index,
357                                       session_state_t state,
358                                       f64 wait_for_time)
359 {
360   f64 timeout = clib_time_now (&vcm->clib_time) + wait_for_time;
361   session_t *volatile session;
362   int rv;
363
364   do
365     {
366       clib_spinlock_lock (&vcm->sessions_lockp);
367       rv = vppcom_session_at_index (session_index, &session);
368       if (PREDICT_FALSE (rv))
369         {
370           clib_spinlock_unlock (&vcm->sessions_lockp);
371           return rv;
372         }
373       if (session->state == state)
374         {
375           clib_spinlock_unlock (&vcm->sessions_lockp);
376           return VPPCOM_OK;
377         }
378       clib_spinlock_unlock (&vcm->sessions_lockp);
379     }
380   while (clib_time_now (&vcm->clib_time) < timeout);
381
382   if (VPPCOM_DEBUG > 0)
383     clib_warning ("[%d] timeout waiting for state %s (%d)", getpid (),
384                   vppcom_session_state_str (state), state);
385   return VPPCOM_ETIMEDOUT;
386 }
387
388 static inline int
389 vppcom_wait_for_client_session_index (f64 wait_for_time)
390 {
391   f64 timeout = clib_time_now (&vcm->clib_time) + wait_for_time;
392
393   do
394     {
395       if (clib_fifo_elts (vcm->client_session_index_fifo))
396         return VPPCOM_OK;
397     }
398   while (clib_time_now (&vcm->clib_time) < timeout);
399
400   if (wait_for_time == 0)
401     return VPPCOM_EAGAIN;
402
403   if (VPPCOM_DEBUG > 0)
404     clib_warning ("[%d] timeout waiting for client_session_index", getpid ());
405   return VPPCOM_ETIMEDOUT;
406 }
407
408 /*
409  * VPP-API message functions
410  */
411 static void
412 vppcom_send_session_enable_disable (u8 is_enable)
413 {
414   vl_api_session_enable_disable_t *bmp;
415   bmp = vl_msg_api_alloc (sizeof (*bmp));
416   memset (bmp, 0, sizeof (*bmp));
417
418   bmp->_vl_msg_id = ntohs (VL_API_SESSION_ENABLE_DISABLE);
419   bmp->client_index = vcm->my_client_index;
420   bmp->context = htonl (0xfeedface);
421   bmp->is_enable = is_enable;
422   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
423 }
424
425 static int
426 vppcom_app_session_enable (void)
427 {
428   int rv;
429
430   if (vcm->app_state != STATE_APP_ENABLED)
431     {
432       vppcom_send_session_enable_disable (1 /* is_enabled == TRUE */ );
433       rv = vppcom_wait_for_app_state_change (STATE_APP_ENABLED);
434       if (PREDICT_FALSE (rv))
435         {
436           if (VPPCOM_DEBUG > 0)
437             clib_warning ("[%d] Session enable timed out, rv = %s (%d)",
438                           getpid (), vppcom_retval_str (rv), rv);
439           return rv;
440         }
441     }
442   return VPPCOM_OK;
443 }
444
445 static void
446   vl_api_session_enable_disable_reply_t_handler
447   (vl_api_session_enable_disable_reply_t * mp)
448 {
449   if (mp->retval)
450     {
451       clib_warning ("[%d] session_enable_disable failed: %U", getpid (),
452                     format_api_error, ntohl (mp->retval));
453     }
454   else
455     vcm->app_state = STATE_APP_ENABLED;
456 }
457
458 static void
459 vppcom_app_send_attach (void)
460 {
461   vl_api_application_attach_t *bmp;
462   u8 nsid_len = vec_len (vcm->cfg.namespace_id);
463   u8 app_is_proxy = (vcm->cfg.app_proxy_transport_tcp ||
464                      vcm->cfg.app_proxy_transport_udp);
465
466   bmp = vl_msg_api_alloc (sizeof (*bmp));
467   memset (bmp, 0, sizeof (*bmp));
468
469   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_ATTACH);
470   bmp->client_index = vcm->my_client_index;
471   bmp->context = htonl (0xfeedface);
472   bmp->options[APP_OPTIONS_FLAGS] =
473     APP_OPTIONS_FLAGS_ACCEPT_REDIRECT | APP_OPTIONS_FLAGS_ADD_SEGMENT |
474     (vcm->cfg.app_scope_local ? APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE : 0) |
475     (vcm->cfg.app_scope_global ? APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE : 0) |
476     (app_is_proxy ? APP_OPTIONS_FLAGS_IS_PROXY : 0);
477   bmp->options[APP_OPTIONS_PROXY_TRANSPORT] =
478     (vcm->cfg.app_proxy_transport_tcp ? 1 << TRANSPORT_PROTO_TCP : 0) |
479     (vcm->cfg.app_proxy_transport_udp ? 1 << TRANSPORT_PROTO_UDP : 0);
480   bmp->options[SESSION_OPTIONS_SEGMENT_SIZE] = vcm->cfg.segment_size;
481   bmp->options[SESSION_OPTIONS_ADD_SEGMENT_SIZE] = vcm->cfg.add_segment_size;
482   bmp->options[SESSION_OPTIONS_RX_FIFO_SIZE] = vcm->cfg.rx_fifo_size;
483   bmp->options[SESSION_OPTIONS_TX_FIFO_SIZE] = vcm->cfg.tx_fifo_size;
484   if (nsid_len)
485     {
486       bmp->namespace_id_len = nsid_len;
487       clib_memcpy (bmp->namespace_id, vcm->cfg.namespace_id, nsid_len);
488       bmp->options[APP_OPTIONS_NAMESPACE_SECRET] = vcm->cfg.namespace_secret;
489     }
490   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
491 }
492
493 static int
494 vppcom_app_attach (void)
495 {
496   int rv;
497
498   vppcom_app_send_attach ();
499   rv = vppcom_wait_for_app_state_change (STATE_APP_ATTACHED);
500   if (PREDICT_FALSE (rv))
501     {
502       if (VPPCOM_DEBUG > 0)
503         clib_warning ("[%d] application attach timed out, rv = %s (%d)",
504                       getpid (), vppcom_retval_str (rv), rv);
505       return rv;
506     }
507   return VPPCOM_OK;
508 }
509
510 static void
511 vppcom_app_detach (void)
512 {
513   vl_api_application_detach_t *bmp;
514   bmp = vl_msg_api_alloc (sizeof (*bmp));
515   memset (bmp, 0, sizeof (*bmp));
516
517   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
518   bmp->client_index = vcm->my_client_index;
519   bmp->context = htonl (0xfeedface);
520   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
521 }
522
523 static void
524 vl_api_application_attach_reply_t_handler (vl_api_application_attach_reply_t *
525                                            mp)
526 {
527   static svm_fifo_segment_create_args_t _a;
528   svm_fifo_segment_create_args_t *a = &_a;
529   int rv;
530
531   memset (a, 0, sizeof (*a));
532   if (mp->retval)
533     {
534       clib_warning ("[%d] attach failed: %U", getpid (),
535                     format_api_error, ntohl (mp->retval));
536       return;
537     }
538
539   if (mp->segment_name_length == 0)
540     {
541       clib_warning ("[%d] segment_name_length zero", getpid ());
542       return;
543     }
544
545   a->segment_name = (char *) mp->segment_name;
546   a->segment_size = mp->segment_size;
547
548   ASSERT (mp->app_event_queue_address);
549
550   /* Attach to the segment vpp created */
551   rv = svm_fifo_segment_attach (a);
552   vec_reset_length (a->new_segment_indices);
553   if (PREDICT_FALSE (rv))
554     {
555       clib_warning ("[%d] svm_fifo_segment_attach ('%s') failed", getpid (),
556                     mp->segment_name);
557       return;
558     }
559
560   vcm->app_event_queue =
561     uword_to_pointer (mp->app_event_queue_address,
562                       unix_shared_memory_queue_t *);
563
564   vcm->app_state = STATE_APP_ATTACHED;
565 }
566
567 static void
568 vl_api_application_detach_reply_t_handler (vl_api_application_detach_reply_t *
569                                            mp)
570 {
571   if (mp->retval)
572     clib_warning ("[%d] detach failed: %U", getpid (), format_api_error,
573                   ntohl (mp->retval));
574
575   vcm->app_state = STATE_APP_ENABLED;
576 }
577
578 static void
579 vl_api_disconnect_session_reply_t_handler (vl_api_disconnect_session_reply_t *
580                                            mp)
581 {
582   uword *p;
583
584   p = hash_get (vcm->session_index_by_vpp_handles, mp->handle);
585   if (p)
586     {
587       session_t *session = 0;
588       int rv;
589       clib_spinlock_lock (&vcm->sessions_lockp);
590       rv = vppcom_session_at_index (p[0], &session);
591       if (PREDICT_FALSE (rv))
592         {
593           if (VPPCOM_DEBUG > 1)
594             clib_warning ("[%d] invalid session, sid (%u) has been closed!",
595                           getpid (), p[0]);
596         }
597       hash_unset (vcm->session_index_by_vpp_handles, mp->handle);
598       session->state = STATE_DISCONNECT;
599       clib_spinlock_unlock (&vcm->sessions_lockp);
600     }
601   else
602     {
603       if (VPPCOM_DEBUG > 1)
604         clib_warning ("[%d] couldn't find session key %llx", getpid (),
605                       mp->handle);
606     }
607
608   if (mp->retval)
609     clib_warning ("[%d] disconnect_session failed: %U", getpid (),
610                   format_api_error, ntohl (mp->retval));
611 }
612
613 static void
614 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
615 {
616   static svm_fifo_segment_create_args_t _a;
617   svm_fifo_segment_create_args_t *a = &_a;
618   int rv;
619
620   memset (a, 0, sizeof (*a));
621   a->segment_name = (char *) mp->segment_name;
622   a->segment_size = mp->segment_size;
623   /* Attach to the segment vpp created */
624   rv = svm_fifo_segment_attach (a);
625   vec_reset_length (a->new_segment_indices);
626   if (PREDICT_FALSE (rv))
627     {
628       clib_warning ("[%d] svm_fifo_segment_attach ('%s') failed",
629                     getpid (), mp->segment_name);
630       return;
631     }
632   if (VPPCOM_DEBUG > 1)
633     clib_warning ("[%d] mapped new segment '%s' size %d", getpid (),
634                   mp->segment_name, mp->segment_size);
635 }
636
637 static void
638 vl_api_disconnect_session_t_handler (vl_api_disconnect_session_t * mp)
639 {
640   session_t *session = 0;
641   vl_api_disconnect_session_reply_t *rmp;
642   uword *p;
643   int rv = 0;
644
645   p = hash_get (vcm->session_index_by_vpp_handles, mp->handle);
646   if (p)
647     {
648       int rval;
649       clib_spinlock_lock (&vcm->sessions_lockp);
650       rval = vppcom_session_at_index (p[0], &session);
651       if (PREDICT_FALSE (rval))
652         {
653           if (VPPCOM_DEBUG > 1)
654             clib_warning ("[%d] invalid session, sid (%u) has been closed!",
655                           getpid (), p[0]);
656         }
657       else
658         pool_put (vcm->sessions, session);
659       clib_spinlock_unlock (&vcm->sessions_lockp);
660       hash_unset (vcm->session_index_by_vpp_handles, mp->handle);
661     }
662   else
663     {
664       clib_warning ("[%d] couldn't find session key %llx", getpid (),
665                     mp->handle);
666       rv = -11;
667     }
668
669   rmp = vl_msg_api_alloc (sizeof (*rmp));
670   memset (rmp, 0, sizeof (*rmp));
671
672   rmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION_REPLY);
673   rmp->retval = htonl (rv);
674   rmp->handle = mp->handle;
675   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & rmp);
676 }
677
678 static void
679 vl_api_reset_session_t_handler (vl_api_reset_session_t * mp)
680 {
681   session_t *session = 0;
682   vl_api_reset_session_reply_t *rmp;
683   uword *p;
684   int rv = 0;
685
686   p = hash_get (vcm->session_index_by_vpp_handles, mp->handle);
687   if (p)
688     {
689       int rval;
690       clib_spinlock_lock (&vcm->sessions_lockp);
691       rval = vppcom_session_at_index (p[0], &session);
692       if (PREDICT_FALSE (rval))
693         {
694           if (VPPCOM_DEBUG > 1)
695             clib_warning ("[%d] invalid session, sid (%u) has been closed!",
696                           getpid (), p[0]);
697         }
698       else
699         pool_put (vcm->sessions, session);
700       clib_spinlock_unlock (&vcm->sessions_lockp);
701       hash_unset (vcm->session_index_by_vpp_handles, mp->handle);
702     }
703   else
704     {
705       clib_warning ("[%d] couldn't find session key %llx", getpid (),
706                     mp->handle);
707       rv = -11;
708     }
709
710   rmp = vl_msg_api_alloc (sizeof (*rmp));
711   memset (rmp, 0, sizeof (*rmp));
712   rmp->_vl_msg_id = ntohs (VL_API_RESET_SESSION_REPLY);
713   rmp->retval = htonl (rv);
714   rmp->handle = mp->handle;
715   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & rmp);
716 }
717
718 static void
719 vl_api_connect_session_reply_t_handler (vl_api_connect_session_reply_t * mp)
720 {
721   session_t *session;
722   u32 session_index;
723   svm_fifo_t *rx_fifo, *tx_fifo;
724   u8 is_cut_thru = 0;
725   int rv;
726
727   if (mp->retval)
728     {
729       clib_warning ("[%d] connect failed: %U", getpid (), format_api_error,
730                     ntohl (mp->retval));
731       return;
732     }
733
734   session_index = mp->context;
735   if (VPPCOM_DEBUG > 1)
736     clib_warning ("[%d] session_index = %d 0x%08x", getpid (),
737                   session_index, session_index);
738
739   clib_spinlock_lock (&vcm->sessions_lockp);
740   if (pool_is_free_index (vcm->sessions, session_index))
741     {
742       clib_spinlock_unlock (&vcm->sessions_lockp);
743       if (VPPCOM_DEBUG > 1)
744         clib_warning ("[%d] invalid session, sid %d is closed!",
745                       getpid (), session_index);
746       return;
747     }
748
749   /* We've been redirected */
750   if (mp->segment_name_length > 0)
751     {
752       static svm_fifo_segment_create_args_t _a;
753       svm_fifo_segment_create_args_t *a = &_a;
754
755       is_cut_thru = 1;
756       memset (a, 0, sizeof (*a));
757       a->segment_name = (char *) mp->segment_name;
758       if (VPPCOM_DEBUG > 1)
759         clib_warning ("[%d] cut-thru segment: %s", getpid (),
760                       a->segment_name);
761       rv = svm_fifo_segment_attach (a);
762       vec_reset_length (a->new_segment_indices);
763       if (PREDICT_FALSE (rv))
764         {
765           clib_spinlock_unlock (&vcm->sessions_lockp);
766           clib_warning ("[%d] sm_fifo_segment_attach ('%s') failed",
767                         getpid (), a->segment_name);
768           return;
769         }
770     }
771
772   /*
773    * Setup session
774    */
775   if (VPPCOM_DEBUG > 1)
776     clib_warning ("[%d] client sid %d", getpid (), session_index);
777
778   session = pool_elt_at_index (vcm->sessions, session_index);
779   session->is_cut_thru = is_cut_thru;
780   session->vpp_event_queue = uword_to_pointer (mp->vpp_event_queue_address,
781                                                unix_shared_memory_queue_t *);
782
783   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
784   rx_fifo->client_session_index = session_index;
785   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
786   tx_fifo->client_session_index = session_index;
787
788   session->server_rx_fifo = rx_fifo;
789   session->server_tx_fifo = tx_fifo;
790   session->vpp_session_handle = mp->handle;
791   session->state = STATE_CONNECT;
792
793   /* Add it to lookup table */
794   hash_set (vcm->session_index_by_vpp_handles, mp->handle, session_index);
795   clib_spinlock_unlock (&vcm->sessions_lockp);
796 }
797
798 static void
799 vppcom_send_connect_sock (session_t * session, u32 session_index)
800 {
801   vl_api_connect_sock_t *cmp;
802
803   /* Assumes caller as acquired the spinlock: vcm->sessions_lockp */
804   session->is_server = 0;
805   cmp = vl_msg_api_alloc (sizeof (*cmp));
806   memset (cmp, 0, sizeof (*cmp));
807   cmp->_vl_msg_id = ntohs (VL_API_CONNECT_SOCK);
808   cmp->client_index = vcm->my_client_index;
809   cmp->context = session_index;
810
811   if (VPPCOM_DEBUG > 1)
812     clib_warning ("[%d] session_index = %d 0x%08x",
813                   getpid (), session_index, session_index);
814
815   cmp->vrf = session->vrf;
816   cmp->is_ip4 = session->peer_addr.is_ip4;
817   clib_memcpy (cmp->ip, &session->peer_addr.ip46, sizeof (cmp->ip));
818   cmp->port = session->peer_port;
819   cmp->proto = session->proto;
820   clib_memcpy (cmp->options, session->options, sizeof (cmp->options));
821   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & cmp);
822 }
823
824 static inline void
825 vppcom_send_disconnect (session_t * session)
826 {
827   vl_api_disconnect_session_t *dmp;
828
829   /* Assumes caller as acquired the spinlock: vcm->sessions_lockp */
830   dmp = vl_msg_api_alloc (sizeof (*dmp));
831   memset (dmp, 0, sizeof (*dmp));
832   dmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION);
833   dmp->client_index = vcm->my_client_index;
834   dmp->handle = session->vpp_session_handle;
835   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & dmp);
836 }
837
838 static void
839 vl_api_bind_sock_reply_t_handler (vl_api_bind_sock_reply_t * mp)
840 {
841   session_t *session = 0;
842   int rv;
843
844   if (mp->retval)
845     clib_warning ("[%d] bind failed: %U", getpid (), format_api_error,
846                   ntohl (mp->retval));
847
848   ASSERT (vcm->bind_session_index != ~0);
849
850   clib_spinlock_lock (&vcm->sessions_lockp);
851   rv = vppcom_session_at_index (vcm->bind_session_index, &session);
852   if (rv == VPPCOM_OK)
853     {
854       session->vpp_session_handle = mp->handle;
855       hash_set (vcm->session_index_by_vpp_handles, mp->handle,
856                 vcm->bind_session_index);
857       session->state = mp->retval ? STATE_FAILED : STATE_LISTEN;
858       vcm->bind_session_index = ~0;
859     }
860   clib_spinlock_unlock (&vcm->sessions_lockp);
861 }
862
863 static void
864 vl_api_unbind_sock_reply_t_handler (vl_api_unbind_sock_reply_t * mp)
865 {
866   session_t *session = 0;
867   int rv;
868
869   clib_spinlock_lock (&vcm->sessions_lockp);
870   rv = vppcom_session_at_index (vcm->bind_session_index, &session);
871   if (rv == VPPCOM_OK)
872     {
873       if ((VPPCOM_DEBUG > 1) && (mp->retval))
874         clib_warning ("[%d] unbind failed: %U", getpid (), format_api_error,
875                       ntohl (mp->retval));
876
877       vcm->bind_session_index = ~0;
878       session->state = STATE_START;
879     }
880   clib_spinlock_unlock (&vcm->sessions_lockp);
881 }
882
883 u8 *
884 format_ip4_address (u8 * s, va_list * args)
885 {
886   u8 *a = va_arg (*args, u8 *);
887   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
888 }
889
890 u8 *
891 format_ip6_address (u8 * s, va_list * args)
892 {
893   ip6_address_t *a = va_arg (*args, ip6_address_t *);
894   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
895
896   i_max_n_zero = ARRAY_LEN (a->as_u16);
897   max_n_zeros = 0;
898   i_first_zero = i_max_n_zero;
899   n_zeros = 0;
900   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
901     {
902       u32 is_zero = a->as_u16[i] == 0;
903       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
904         {
905           i_first_zero = i;
906           n_zeros = 0;
907         }
908       n_zeros += is_zero;
909       if ((!is_zero && n_zeros > max_n_zeros)
910           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
911         {
912           i_max_n_zero = i_first_zero;
913           max_n_zeros = n_zeros;
914           i_first_zero = ARRAY_LEN (a->as_u16);
915           n_zeros = 0;
916         }
917     }
918
919   last_double_colon = 0;
920   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
921     {
922       if (i == i_max_n_zero && max_n_zeros > 1)
923         {
924           s = format (s, "::");
925           i += max_n_zeros - 1;
926           last_double_colon = 1;
927         }
928       else
929         {
930           s = format (s, "%s%x",
931                       (last_double_colon || i == 0) ? "" : ":",
932                       clib_net_to_host_u16 (a->as_u16[i]));
933           last_double_colon = 0;
934         }
935     }
936
937   return s;
938 }
939
940 /* Format an IP46 address. */
941 u8 *
942 format_ip46_address (u8 * s, va_list * args)
943 {
944   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
945   ip46_type_t type = va_arg (*args, ip46_type_t);
946   int is_ip4 = 1;
947
948   switch (type)
949     {
950     case IP46_TYPE_ANY:
951       is_ip4 = ip46_address_is_ip4 (ip46);
952       break;
953     case IP46_TYPE_IP4:
954       is_ip4 = 1;
955       break;
956     case IP46_TYPE_IP6:
957       is_ip4 = 0;
958       break;
959     }
960
961   return is_ip4 ?
962     format (s, "%U", format_ip4_address, &ip46->ip4) :
963     format (s, "%U", format_ip6_address, &ip46->ip6);
964 }
965
966 static void
967 vl_api_accept_session_t_handler (vl_api_accept_session_t * mp)
968 {
969   vl_api_accept_session_reply_t *rmp;
970   svm_fifo_t *rx_fifo, *tx_fifo;
971   session_t *session;
972   u32 session_index;
973   int rv = 0;
974
975   if (!clib_fifo_free_elts (vcm->client_session_index_fifo))
976     {
977       clib_warning ("[%d] client session queue is full!", getpid ());
978       rv = VNET_API_ERROR_QUEUE_FULL;
979       goto send_reply;
980     }
981
982   if (VPPCOM_DEBUG > 1)
983     {
984       u8 *ip_str = format (0, "%U", format_ip46_address, &mp->ip, mp->is_ip4);
985       clib_warning ("[%d] accepted session from: %s:%d", getpid (), ip_str,
986                     clib_net_to_host_u16 (mp->port));
987       vec_free (ip_str);
988     }
989
990   clib_spinlock_lock (&vcm->sessions_lockp);
991   /* Allocate local session and set it up */
992   pool_get (vcm->sessions, session);
993   memset (session, 0, sizeof (*session));
994   session_index = session - vcm->sessions;
995
996   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
997   rx_fifo->client_session_index = session_index;
998   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
999   tx_fifo->client_session_index = session_index;
1000
1001   session->server_rx_fifo = rx_fifo;
1002   session->server_tx_fifo = tx_fifo;
1003   session->vpp_event_queue = uword_to_pointer (mp->vpp_event_queue_address,
1004                                                unix_shared_memory_queue_t *);
1005   session->state = STATE_ACCEPT;
1006   session->is_cut_thru = 0;
1007   session->is_server = 1;
1008   session->peer_port = mp->port;
1009   session->peer_addr.is_ip4 = mp->is_ip4;
1010   clib_memcpy (&session->peer_addr.ip46, mp->ip,
1011                sizeof (session->peer_addr.ip46));
1012
1013   /* Add it to lookup table */
1014   hash_set (vcm->session_index_by_vpp_handles, mp->handle, session_index);
1015
1016   clib_fifo_add1 (vcm->client_session_index_fifo, session_index);
1017   clib_spinlock_unlock (&vcm->sessions_lockp);
1018
1019   /*
1020    * Send accept reply to vpp
1021    */
1022 send_reply:
1023   rmp = vl_msg_api_alloc (sizeof (*rmp));
1024   memset (rmp, 0, sizeof (*rmp));
1025   rmp->_vl_msg_id = ntohs (VL_API_ACCEPT_SESSION_REPLY);
1026   rmp->retval = htonl (rv);
1027   rmp->handle = mp->handle;
1028   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & rmp);
1029 }
1030
1031 /*
1032  * Acting as server for redirected connect requests
1033  */
1034 static void
1035 vl_api_connect_sock_t_handler (vl_api_connect_sock_t * mp)
1036 {
1037   static svm_fifo_segment_create_args_t _a;
1038   svm_fifo_segment_create_args_t *a = &_a;
1039   u32 session_index;
1040   svm_fifo_segment_private_t *seg;
1041   unix_shared_memory_queue_t *client_q;
1042   vl_api_connect_session_reply_t *rmp;
1043   session_t *session = 0;
1044   int rv = 0;
1045   svm_fifo_t *rx_fifo;
1046   svm_fifo_t *tx_fifo;
1047   unix_shared_memory_queue_t *event_q = 0;
1048
1049   clib_spinlock_lock (&vcm->sessions_lockp);
1050   if (!clib_fifo_free_elts (vcm->client_session_index_fifo))
1051     {
1052       if (VPPCOM_DEBUG > 1)
1053         clib_warning ("[%d] client session queue is full!", getpid ());
1054       rv = VNET_API_ERROR_QUEUE_FULL;
1055       clib_spinlock_unlock (&vcm->sessions_lockp);
1056       goto send_reply;
1057     }
1058
1059   /* Create the segment */
1060   memset (a, 0, sizeof (*a));
1061   a->segment_name = (char *) format ((u8 *) a->segment_name, "%d:segment%d%c",
1062                                      getpid (), vcm->unique_segment_index++,
1063                                      0);
1064   a->segment_size = vcm->cfg.segment_size;
1065   a->preallocated_fifo_pairs = vcm->cfg.preallocated_fifo_pairs;
1066   a->rx_fifo_size = vcm->cfg.rx_fifo_size;
1067   a->tx_fifo_size = vcm->cfg.tx_fifo_size;
1068
1069   rv = svm_fifo_segment_create (a);
1070   if (PREDICT_FALSE (rv))
1071     {
1072       if (VPPCOM_DEBUG > 1)
1073         clib_warning ("[%d] svm_fifo_segment_create ('%s') failed",
1074                       getpid (), a->segment_name);
1075       vec_reset_length (a->new_segment_indices);
1076       rv = VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
1077       goto send_reply;
1078     }
1079
1080   if (VPPCOM_DEBUG > 1)
1081     clib_warning ("[%d] created segment '%s'", getpid (), a->segment_name);
1082
1083   pool_get (vcm->sessions, session);
1084   memset (session, 0, sizeof (*session));
1085   session_index = session - vcm->sessions;
1086
1087   session->sm_seg_index = a->new_segment_indices[0];
1088   vec_reset_length (a->new_segment_indices);
1089
1090   seg = svm_fifo_segment_get_segment (session->sm_seg_index);
1091   rx_fifo = session->server_rx_fifo =
1092     svm_fifo_segment_alloc_fifo (seg, vcm->cfg.rx_fifo_size,
1093                                  FIFO_SEGMENT_RX_FREELIST);
1094   if (PREDICT_FALSE (!session->server_rx_fifo))
1095     {
1096       svm_fifo_segment_delete (seg);
1097       clib_warning ("[%d] rx fifo alloc failed, size %ld (0x%lx)",
1098                     getpid (), vcm->cfg.rx_fifo_size, vcm->cfg.rx_fifo_size);
1099       rv = VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
1100       clib_spinlock_unlock (&vcm->sessions_lockp);
1101       goto send_reply;
1102     }
1103
1104   tx_fifo = session->server_tx_fifo =
1105     svm_fifo_segment_alloc_fifo (seg, vcm->cfg.tx_fifo_size,
1106                                  FIFO_SEGMENT_TX_FREELIST);
1107   if (PREDICT_FALSE (!session->server_tx_fifo))
1108     {
1109       svm_fifo_segment_delete (seg);
1110       if (VPPCOM_DEBUG > 1)
1111         clib_warning ("[%d] tx fifo alloc failed, size %ld (0x%lx)",
1112                       getpid (), vcm->cfg.tx_fifo_size,
1113                       vcm->cfg.tx_fifo_size);
1114       rv = VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
1115       clib_spinlock_unlock (&vcm->sessions_lockp);
1116       goto send_reply;
1117     }
1118
1119   session->server_rx_fifo->master_session_index = session_index;
1120   session->server_tx_fifo->master_session_index = session_index;
1121   session->client_queue_address = mp->client_queue_address;
1122   session->is_cut_thru = 1;
1123   session->is_server = 1;
1124   session->peer_port = mp->port;
1125   session->peer_addr.is_ip4 = mp->is_ip4;
1126   clib_memcpy (&session->peer_addr.ip46, mp->ip,
1127                sizeof (session->peer_addr.ip46));
1128   {
1129     void *oldheap;
1130     ssvm_shared_header_t *sh = seg->ssvm.sh;
1131
1132     ssvm_lock_non_recursive (sh, 1);
1133     oldheap = ssvm_push_heap (sh);
1134     event_q = session->vpp_event_queue =
1135       unix_shared_memory_queue_init (vcm->cfg.event_queue_size,
1136                                      sizeof (session_fifo_event_t),
1137                                      getpid (), 0 /* signal not sent */ );
1138     ssvm_pop_heap (oldheap);
1139     ssvm_unlock_non_recursive (sh);
1140   }
1141
1142   session->state = STATE_ACCEPT;
1143   clib_fifo_add1 (vcm->client_session_index_fifo, session_index);
1144   if (VPPCOM_DEBUG > 1)
1145     clib_warning
1146       ("[%d] Connected cut-thru to client: sid %d, clib_fifo_elts %u!",
1147        getpid (), session_index,
1148        clib_fifo_elts (vcm->client_session_index_fifo));
1149   clib_spinlock_unlock (&vcm->sessions_lockp);
1150
1151 send_reply:
1152   rmp = vl_msg_api_alloc (sizeof (*rmp));
1153   memset (rmp, 0, sizeof (*rmp));
1154
1155   rmp->_vl_msg_id = ntohs (VL_API_CONNECT_SESSION_REPLY);
1156   rmp->context = mp->context;
1157   rmp->retval = htonl (rv);
1158   rmp->segment_name_length = vec_len (a->segment_name);
1159   clib_memcpy (rmp->segment_name, a->segment_name, vec_len (a->segment_name));
1160   vec_reset_length (a->segment_name);
1161
1162   if (event_q)
1163     {
1164       rmp->vpp_event_queue_address = pointer_to_uword (event_q);
1165       rmp->server_rx_fifo = pointer_to_uword (rx_fifo);
1166       rmp->server_tx_fifo = pointer_to_uword (tx_fifo);
1167     }
1168   client_q =
1169     uword_to_pointer (mp->client_queue_address, unix_shared_memory_queue_t *);
1170
1171   ASSERT (client_q);
1172   vl_msg_api_send_shmem (client_q, (u8 *) & rmp);
1173 }
1174
1175 static void
1176 vppcom_send_bind_sock (session_t * session)
1177 {
1178   vl_api_bind_sock_t *bmp;
1179
1180   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1181   session->is_server = 1;
1182   bmp = vl_msg_api_alloc (sizeof (*bmp));
1183   memset (bmp, 0, sizeof (*bmp));
1184
1185   bmp->_vl_msg_id = ntohs (VL_API_BIND_SOCK);
1186   bmp->client_index = vcm->my_client_index;
1187   bmp->context = htonl (0xfeedface);
1188   bmp->vrf = session->vrf;
1189   bmp->is_ip4 = session->lcl_addr.is_ip4;
1190   clib_memcpy (bmp->ip, &session->lcl_addr.ip46, sizeof (bmp->ip));
1191   bmp->port = session->lcl_port;
1192   bmp->proto = session->proto;
1193   clib_memcpy (bmp->options, session->options, sizeof (bmp->options));
1194   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & bmp);
1195 }
1196
1197 static void
1198 vppcom_send_unbind_sock (u32 session_index)
1199 {
1200   vl_api_unbind_sock_t *ump;
1201   session_t *session = 0;
1202   int rv;
1203
1204   clib_spinlock_lock (&vcm->sessions_lockp);
1205   rv = vppcom_session_at_index (session_index, &session);
1206   if (PREDICT_FALSE (rv))
1207     {
1208       clib_spinlock_unlock (&vcm->sessions_lockp);
1209       if (VPPCOM_DEBUG > 0)
1210         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
1211                       getpid (), session_index);
1212       return;
1213     }
1214
1215   ump = vl_msg_api_alloc (sizeof (*ump));
1216   memset (ump, 0, sizeof (*ump));
1217
1218   ump->_vl_msg_id = ntohs (VL_API_UNBIND_SOCK);
1219   ump->client_index = vcm->my_client_index;
1220   ump->handle = session->vpp_session_handle;
1221   clib_spinlock_unlock (&vcm->sessions_lockp);
1222   vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & ump);
1223 }
1224
1225 static int
1226 vppcom_session_unbind_cut_thru (session_t * session)
1227 {
1228   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
1229   svm_fifo_segment_private_t *seg;
1230   int rv = VPPCOM_OK;
1231
1232   seg = vec_elt_at_index (sm->segments, session->sm_seg_index);
1233   svm_fifo_segment_free_fifo (seg, session->server_rx_fifo,
1234                               FIFO_SEGMENT_RX_FREELIST);
1235   svm_fifo_segment_free_fifo (seg, session->server_tx_fifo,
1236                               FIFO_SEGMENT_TX_FREELIST);
1237   svm_fifo_segment_delete (seg);
1238
1239   return rv;
1240 }
1241
1242 static int
1243 vppcom_session_unbind (u32 session_index)
1244 {
1245   int rv;
1246
1247   clib_spinlock_lock (&vcm->sessions_lockp);
1248   if (PREDICT_FALSE (pool_is_free_index (vcm->sessions, session_index)))
1249     {
1250       clib_spinlock_unlock (&vcm->sessions_lockp);
1251       if (VPPCOM_DEBUG > 1)
1252         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
1253                       getpid (), session_index);
1254       return VPPCOM_EBADFD;
1255     }
1256   clib_spinlock_unlock (&vcm->sessions_lockp);
1257
1258   vcm->bind_session_index = session_index;
1259   vppcom_send_unbind_sock (session_index);
1260   rv = vppcom_wait_for_session_state_change (session_index, STATE_START,
1261                                              vcm->cfg.session_timeout);
1262   if (PREDICT_FALSE (rv))
1263     {
1264       vcm->bind_session_index = ~0;
1265       if (VPPCOM_DEBUG > 0)
1266         clib_warning ("[%d] server unbind timed out, rv = %s (%d)",
1267                       getpid (), vppcom_retval_str (rv), rv);
1268       return rv;
1269     }
1270   return VPPCOM_OK;
1271 }
1272
1273 static inline int
1274 vppcom_session_disconnect (u32 session_index)
1275 {
1276   int rv;
1277   session_t *session;
1278
1279   clib_spinlock_lock (&vcm->sessions_lockp);
1280   rv = vppcom_session_at_index (session_index, &session);
1281   if (PREDICT_FALSE (rv))
1282     {
1283       clib_spinlock_unlock (&vcm->sessions_lockp);
1284       if (VPPCOM_DEBUG > 1)
1285         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
1286                       getpid (), session_index);
1287       return rv;
1288     }
1289
1290   if (!session->is_cut_thru)
1291     {
1292       vppcom_send_disconnect (session);
1293       clib_spinlock_unlock (&vcm->sessions_lockp);
1294
1295       rv = vppcom_wait_for_session_state_change (session_index,
1296                                                  STATE_DISCONNECT, 1.0);
1297       if ((VPPCOM_DEBUG > 0) && (rv < 0))
1298         clib_warning ("[%d] disconnect (session %d) failed, rv = %s (%d)",
1299                       getpid (), session_index, vppcom_retval_str (rv), rv);
1300     }
1301   else
1302     clib_spinlock_unlock (&vcm->sessions_lockp);
1303
1304   return VPPCOM_OK;
1305 }
1306
1307 #define foreach_sock_msg                                        \
1308 _(SESSION_ENABLE_DISABLE_REPLY, session_enable_disable_reply)   \
1309 _(BIND_SOCK_REPLY, bind_sock_reply)                             \
1310 _(UNBIND_SOCK_REPLY, unbind_sock_reply)                         \
1311 _(ACCEPT_SESSION, accept_session)                               \
1312 _(CONNECT_SOCK, connect_sock)                                   \
1313 _(CONNECT_SESSION_REPLY, connect_session_reply)                 \
1314 _(DISCONNECT_SESSION, disconnect_session)                       \
1315 _(DISCONNECT_SESSION_REPLY, disconnect_session_reply)           \
1316 _(RESET_SESSION, reset_session)                                 \
1317 _(APPLICATION_ATTACH_REPLY, application_attach_reply)           \
1318 _(APPLICATION_DETACH_REPLY, application_detach_reply)           \
1319 _(MAP_ANOTHER_SEGMENT, map_another_segment)
1320
1321 static void
1322 vppcom_api_hookup (void)
1323 {
1324 #define _(N,n)                                                  \
1325     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1326                            vl_api_##n##_t_handler,              \
1327                            vl_noop_handler,                     \
1328                            vl_api_##n##_t_endian,               \
1329                            vl_api_##n##_t_print,                \
1330                            sizeof(vl_api_##n##_t), 1);
1331   foreach_sock_msg;
1332 #undef _
1333 }
1334
1335 static void
1336 vppcom_cfg_init (vppcom_cfg_t * vcl_cfg)
1337 {
1338   ASSERT (vcl_cfg);
1339
1340   vcl_cfg->heapsize = (256ULL << 20);
1341   vcl_cfg->segment_baseva = 0x200000000ULL;
1342   vcl_cfg->segment_size = (256 << 20);
1343   vcl_cfg->add_segment_size = (128 << 20);
1344   vcl_cfg->preallocated_fifo_pairs = 8;
1345   vcl_cfg->rx_fifo_size = (1 << 20);
1346   vcl_cfg->tx_fifo_size = (1 << 20);
1347   vcl_cfg->event_queue_size = 2048;
1348   vcl_cfg->listen_queue_size = CLIB_CACHE_LINE_BYTES / sizeof (u32);
1349   vcl_cfg->app_timeout = 10 * 60.0;
1350   vcl_cfg->session_timeout = 10 * 60.0;
1351   vcl_cfg->accept_timeout = 60.0;
1352 }
1353
1354 static void
1355 vppcom_cfg_heapsize (char *conf_fname)
1356 {
1357   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
1358   FILE *fp;
1359   char inbuf[4096];
1360   int argc = 1;
1361   char **argv = NULL;
1362   char *arg = NULL;
1363   char *p;
1364   int i;
1365   u8 *sizep;
1366   u32 size;
1367   void *vcl_mem;
1368   void *heap;
1369
1370   fp = fopen (conf_fname, "r");
1371   if (fp == NULL)
1372     {
1373       if (VPPCOM_DEBUG > 0)
1374         fprintf (stderr, "open configuration file '%s' failed\n", conf_fname);
1375       goto defaulted;
1376     }
1377   argv = calloc (1, sizeof (char *));
1378   if (argv == NULL)
1379     goto defaulted;
1380
1381   while (1)
1382     {
1383       if (fgets (inbuf, 4096, fp) == 0)
1384         break;
1385       p = strtok (inbuf, " \t\n");
1386       while (p != NULL)
1387         {
1388           if (*p == '#')
1389             break;
1390           argc++;
1391           char **tmp = realloc (argv, argc * sizeof (char *));
1392           if (tmp == NULL)
1393             goto defaulted;
1394           argv = tmp;
1395           arg = strndup (p, 1024);
1396           if (arg == NULL)
1397             goto defaulted;
1398           argv[argc - 1] = arg;
1399           p = strtok (NULL, " \t\n");
1400         }
1401     }
1402
1403   fclose (fp);
1404   fp = NULL;
1405
1406   char **tmp = realloc (argv, (argc + 1) * sizeof (char *));
1407   if (tmp == NULL)
1408     goto defaulted;
1409   argv = tmp;
1410   argv[argc] = NULL;
1411
1412   /*
1413    * Look for and parse the "heapsize" config parameter.
1414    * Manual since none of the clib infra has been bootstrapped yet.
1415    *
1416    * Format: heapsize <nn>[mM][gG]
1417    */
1418
1419   for (i = 1; i < (argc - 1); i++)
1420     {
1421       if (!strncmp (argv[i], "heapsize", 8))
1422         {
1423           sizep = (u8 *) argv[i + 1];
1424           size = 0;
1425           while (*sizep >= '0' && *sizep <= '9')
1426             {
1427               size *= 10;
1428               size += *sizep++ - '0';
1429             }
1430           if (size == 0)
1431             {
1432               if (VPPCOM_DEBUG > 0)
1433                 clib_warning ("[%d] parse error '%s %s', "
1434                               "using default heapsize %lld (0x%llx)",
1435                               getpid (), argv[i], argv[i + 1],
1436                               vcl_cfg->heapsize, vcl_cfg->heapsize);
1437               goto defaulted;
1438             }
1439
1440           if (*sizep == 'g' || *sizep == 'G')
1441             vcl_cfg->heapsize = size << 30;
1442           else if (*sizep == 'm' || *sizep == 'M')
1443             vcl_cfg->heapsize = size << 20;
1444           else
1445             {
1446               if (VPPCOM_DEBUG > 0)
1447                 clib_warning ("[%d] parse error '%s %s', "
1448                               "using default heapsize %lld (0x%llx)",
1449                               getpid (), argv[i], argv[i + 1],
1450                               vcl_cfg->heapsize, vcl_cfg->heapsize);
1451               goto defaulted;
1452             }
1453         }
1454     }
1455
1456 defaulted:
1457   if (fp != NULL)
1458     fclose (fp);
1459   if (argv != NULL)
1460     free (argv);
1461
1462   vcl_mem = mmap (0, vcl_cfg->heapsize, PROT_READ | PROT_WRITE,
1463                   MAP_SHARED | MAP_ANONYMOUS, -1, 0);
1464   if (vcl_mem <= 0)
1465     {
1466       clib_unix_error ("[%d] ERROR: mmap(0, %lld == 0x%llx, "
1467                        "PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS, "
1468                        "-1, 0) failed!",
1469                        getpid (), vcl_cfg->heapsize, vcl_cfg->heapsize);
1470       return;
1471     }
1472   heap = clib_mem_init (vcl_mem, vcl_cfg->heapsize);
1473   if (!heap)
1474     {
1475       clib_warning ("[%d] ERROR: clib_mem_init() failed!", getpid ());
1476       return;
1477     }
1478   vcl_mem = clib_mem_alloc (sizeof (_vppcom_main));
1479   if (!vcl_mem)
1480     {
1481       clib_warning ("[%d] ERROR: clib_mem_alloc() failed!", getpid ());
1482       return;
1483     }
1484
1485   clib_memcpy (vcl_mem, &_vppcom_main, sizeof (_vppcom_main));
1486   vcm = vcl_mem;
1487
1488   if (VPPCOM_DEBUG > 0)
1489     clib_warning ("[%d] allocated VCL heap = %p, size %lld (0x%llx)",
1490                   getpid (), heap, vcl_cfg->heapsize, vcl_cfg->heapsize);
1491 }
1492
1493 static void
1494 vppcom_cfg_read (char *conf_fname)
1495 {
1496   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
1497   int fd;
1498   unformat_input_t _input, *input = &_input;
1499   unformat_input_t _line_input, *line_input = &_line_input;
1500   u8 vc_cfg_input = 0;
1501   u8 *chroot_path;
1502   struct stat s;
1503   u32 uid, gid;
1504
1505   fd = open (conf_fname, O_RDONLY);
1506   if (fd < 0)
1507     {
1508       if (VPPCOM_DEBUG > 0)
1509         clib_warning ("[%d] open configuration file '%s' failed!",
1510                       getpid (), conf_fname);
1511       goto file_done;
1512     }
1513
1514   if (fstat (fd, &s) < 0)
1515     {
1516       if (VPPCOM_DEBUG > 0)
1517         clib_warning ("[%d] failed to stat `%s'", getpid (), conf_fname);
1518       goto file_done;
1519     }
1520
1521   if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
1522     {
1523       if (VPPCOM_DEBUG > 0)
1524         clib_warning ("[%d] not a regular file `%s'", getpid (), conf_fname);
1525       goto file_done;
1526     }
1527
1528   unformat_init_clib_file (input, fd);
1529
1530   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1531     {
1532       (void) unformat_user (input, unformat_line_input, line_input);
1533       unformat_skip_white_space (line_input);
1534
1535       if (unformat (line_input, "vcl {"))
1536         {
1537           vc_cfg_input = 1;
1538           continue;
1539         }
1540
1541       if (vc_cfg_input)
1542         {
1543           if (unformat (line_input, "heapsize %s", &chroot_path))
1544             {
1545               vec_terminate_c_string (chroot_path);
1546               if (VPPCOM_DEBUG > 0)
1547                 clib_warning ("[%d] configured heapsize %s, "
1548                               "actual heapsize %lld (0x%llx)",
1549                               getpid (), chroot_path, vcl_cfg->heapsize,
1550                               vcl_cfg->heapsize);
1551               vec_free (chroot_path);
1552             }
1553           else if (unformat (line_input, "api-prefix %s", &chroot_path))
1554             {
1555               vec_terminate_c_string (chroot_path);
1556               vl_set_memory_root_path ((char *) chroot_path);
1557               if (VPPCOM_DEBUG > 0)
1558                 clib_warning ("[%d] configured api-prefix %s",
1559                               getpid (), chroot_path);
1560               chroot_path = 0;  /* Don't vec_free() it! */
1561             }
1562           else if (unformat (line_input, "uid %d", &uid))
1563             {
1564               vl_set_memory_uid (uid);
1565               if (VPPCOM_DEBUG > 0)
1566                 clib_warning ("[%d] configured uid %d", getpid (), uid);
1567             }
1568           else if (unformat (line_input, "gid %d", &gid))
1569             {
1570               vl_set_memory_gid (gid);
1571               if (VPPCOM_DEBUG > 0)
1572                 clib_warning ("[%d] configured gid %d", getpid (), gid);
1573             }
1574           else if (unformat (line_input, "segment-baseva 0x%lx",
1575                              &vcl_cfg->segment_baseva))
1576             {
1577               if (VPPCOM_DEBUG > 0)
1578                 clib_warning ("[%d] configured segment_baseva 0x%lx",
1579                               getpid (), vcl_cfg->segment_baseva);
1580             }
1581           else if (unformat (line_input, "segment-size 0x%lx",
1582                              &vcl_cfg->segment_size))
1583             {
1584               if (VPPCOM_DEBUG > 0)
1585                 clib_warning ("[%d] configured segment_size 0x%lx (%ld)",
1586                               getpid (), vcl_cfg->segment_size,
1587                               vcl_cfg->segment_size);
1588             }
1589           else if (unformat (line_input, "segment-size %ld",
1590                              &vcl_cfg->segment_size))
1591             {
1592               if (VPPCOM_DEBUG > 0)
1593                 clib_warning ("[%d] configured segment_size %ld (0x%lx)",
1594                               getpid (), vcl_cfg->segment_size,
1595                               vcl_cfg->segment_size);
1596             }
1597           else if (unformat (line_input, "add-segment-size 0x%lx",
1598                              &vcl_cfg->add_segment_size))
1599             {
1600               if (VPPCOM_DEBUG > 0)
1601                 clib_warning
1602                   ("[%d] configured add_segment_size 0x%lx (%ld)",
1603                    getpid (), vcl_cfg->add_segment_size,
1604                    vcl_cfg->add_segment_size);
1605             }
1606           else if (unformat (line_input, "add-segment-size %ld",
1607                              &vcl_cfg->add_segment_size))
1608             {
1609               if (VPPCOM_DEBUG > 0)
1610                 clib_warning
1611                   ("[%d] configured add_segment_size %ld (0x%lx)",
1612                    getpid (), vcl_cfg->add_segment_size,
1613                    vcl_cfg->add_segment_size);
1614             }
1615           else if (unformat (line_input, "preallocated-fifo-pairs %d",
1616                              &vcl_cfg->preallocated_fifo_pairs))
1617             {
1618               if (VPPCOM_DEBUG > 0)
1619                 clib_warning ("[%d] configured preallocated_fifo_pairs "
1620                               "%d (0x%x)", getpid (),
1621                               vcl_cfg->preallocated_fifo_pairs,
1622                               vcl_cfg->preallocated_fifo_pairs);
1623             }
1624           else if (unformat (line_input, "rx-fifo-size 0x%lx",
1625                              &vcl_cfg->rx_fifo_size))
1626             {
1627               if (VPPCOM_DEBUG > 0)
1628                 clib_warning ("[%d] configured rx_fifo_size 0x%lx (%ld)",
1629                               getpid (), vcl_cfg->rx_fifo_size,
1630                               vcl_cfg->rx_fifo_size);
1631             }
1632           else if (unformat (line_input, "rx-fifo-size %ld",
1633                              &vcl_cfg->rx_fifo_size))
1634             {
1635               if (VPPCOM_DEBUG > 0)
1636                 clib_warning ("[%d] configured rx_fifo_size %ld (0x%lx)",
1637                               getpid (), vcl_cfg->rx_fifo_size,
1638                               vcl_cfg->rx_fifo_size);
1639             }
1640           else if (unformat (line_input, "tx-fifo-size 0x%lx",
1641                              &vcl_cfg->tx_fifo_size))
1642             {
1643               if (VPPCOM_DEBUG > 0)
1644                 clib_warning ("[%d] configured tx_fifo_size 0x%lx (%ld)",
1645                               getpid (), vcl_cfg->tx_fifo_size,
1646                               vcl_cfg->tx_fifo_size);
1647             }
1648           else if (unformat (line_input, "tx-fifo-size %ld",
1649                              &vcl_cfg->tx_fifo_size))
1650             {
1651               if (VPPCOM_DEBUG > 0)
1652                 clib_warning ("[%d] configured tx_fifo_size %ld (0x%lx)",
1653                               getpid (), vcl_cfg->tx_fifo_size,
1654                               vcl_cfg->tx_fifo_size);
1655             }
1656           else if (unformat (line_input, "event-queue-size 0x%lx",
1657                              &vcl_cfg->event_queue_size))
1658             {
1659               if (VPPCOM_DEBUG > 0)
1660                 clib_warning ("[%d] configured event_queue_size 0x%lx (%ld)",
1661                               getpid (), vcl_cfg->event_queue_size,
1662                               vcl_cfg->event_queue_size);
1663             }
1664           else if (unformat (line_input, "event-queue-size %ld",
1665                              &vcl_cfg->event_queue_size))
1666             {
1667               if (VPPCOM_DEBUG > 0)
1668                 clib_warning ("[%d] configured event_queue_size %ld (0x%lx)",
1669                               getpid (), vcl_cfg->event_queue_size,
1670                               vcl_cfg->event_queue_size);
1671             }
1672           else if (unformat (line_input, "listen-queue-size 0x%lx",
1673                              &vcl_cfg->listen_queue_size))
1674             {
1675               if (VPPCOM_DEBUG > 0)
1676                 clib_warning ("[%d] configured listen_queue_size 0x%lx (%ld)",
1677                               getpid (), vcl_cfg->listen_queue_size,
1678                               vcl_cfg->listen_queue_size);
1679             }
1680           else if (unformat (line_input, "listen-queue-size %ld",
1681                              &vcl_cfg->listen_queue_size))
1682             {
1683               if (VPPCOM_DEBUG > 0)
1684                 clib_warning ("[%d] configured listen_queue_size %ld (0x%lx)",
1685                               getpid (), vcl_cfg->listen_queue_size,
1686                               vcl_cfg->listen_queue_size);
1687             }
1688           else if (unformat (line_input, "app-timeout %f",
1689                              &vcl_cfg->app_timeout))
1690             {
1691               if (VPPCOM_DEBUG > 0)
1692                 clib_warning ("[%d] configured app_timeout %f",
1693                               getpid (), vcl_cfg->app_timeout);
1694             }
1695           else if (unformat (line_input, "session-timeout %f",
1696                              &vcl_cfg->session_timeout))
1697             {
1698               if (VPPCOM_DEBUG > 0)
1699                 clib_warning ("[%d] configured session_timeout %f",
1700                               getpid (), vcl_cfg->session_timeout);
1701             }
1702           else if (unformat (line_input, "accept-timeout %f",
1703                              &vcl_cfg->accept_timeout))
1704             {
1705               if (VPPCOM_DEBUG > 0)
1706                 clib_warning ("[%d] configured accept_timeout %f",
1707                               getpid (), vcl_cfg->accept_timeout);
1708             }
1709           else if (unformat (line_input, "app-proxy-transport-tcp"))
1710             {
1711               vcl_cfg->app_proxy_transport_tcp = 1;
1712               if (VPPCOM_DEBUG > 0)
1713                 clib_warning ("[%d] configured app_proxy_transport_tcp (%d)",
1714                               getpid (), vcl_cfg->app_proxy_transport_tcp);
1715             }
1716           else if (unformat (line_input, "app-proxy-transport-udp"))
1717             {
1718               vcl_cfg->app_proxy_transport_udp = 1;
1719               if (VPPCOM_DEBUG > 0)
1720                 clib_warning ("[%d] configured app_proxy_transport_udp (%d)",
1721                               getpid (), vcl_cfg->app_proxy_transport_udp);
1722             }
1723           else if (unformat (line_input, "app-scope-local"))
1724             {
1725               vcl_cfg->app_scope_local = 1;
1726               if (VPPCOM_DEBUG > 0)
1727                 clib_warning ("[%d] configured app_scope_local (%d)",
1728                               getpid (), vcl_cfg->app_scope_local);
1729             }
1730           else if (unformat (line_input, "app-scope-global"))
1731             {
1732               vcl_cfg->app_scope_global = 1;
1733               if (VPPCOM_DEBUG > 0)
1734                 clib_warning ("[%d] configured app_scope_global (%d)",
1735                               getpid (), vcl_cfg->app_scope_global);
1736             }
1737           else if (unformat (line_input, "namespace-secret %lu",
1738                              &vcl_cfg->namespace_secret))
1739             {
1740               if (VPPCOM_DEBUG > 0)
1741                 clib_warning
1742                   ("[%d] configured namespace_secret %lu (0x%lx)",
1743                    getpid (), vcl_cfg->namespace_secret,
1744                    vcl_cfg->namespace_secret);
1745             }
1746           else if (unformat (line_input, "namespace-id %v",
1747                              &vcl_cfg->namespace_id))
1748             {
1749               vl_api_application_attach_t *mp;
1750               u32 max_nsid_vec_len = sizeof (mp->namespace_id) - 1;
1751               u32 nsid_vec_len = vec_len (vcl_cfg->namespace_id);
1752               if (nsid_vec_len > max_nsid_vec_len)
1753                 {
1754                   _vec_len (vcl_cfg->namespace_id) = max_nsid_vec_len;
1755                   if (VPPCOM_DEBUG > 0)
1756                     clib_warning ("[%d] configured namespace_id is too long,"
1757                                   " truncated to %d characters!", getpid (),
1758                                   max_nsid_vec_len);
1759                 }
1760
1761               if (VPPCOM_DEBUG > 0)
1762                 clib_warning ("[%d] configured namespace_id %v",
1763                               getpid (), vcl_cfg->namespace_id);
1764             }
1765           else if (unformat (line_input, "}"))
1766             {
1767               vc_cfg_input = 0;
1768               if (VPPCOM_DEBUG > 0)
1769                 clib_warning ("[%d] completed parsing vppcom config!",
1770                               getpid ());
1771               goto input_done;
1772             }
1773           else
1774             {
1775               if (line_input->buffer[line_input->index] != '#')
1776                 {
1777                   clib_warning ("[%d] Unknown vppcom config option: '%s'",
1778                                 getpid (), (char *)
1779                                 &line_input->buffer[line_input->index]);
1780                 }
1781             }
1782         }
1783     }
1784
1785 input_done:
1786   unformat_free (input);
1787
1788 file_done:
1789   if (fd >= 0)
1790     close (fd);
1791 }
1792
1793 /*
1794  * VPPCOM Public API functions
1795  */
1796 int
1797 vppcom_app_create (char *app_name)
1798 {
1799   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
1800   u8 *heap;
1801   mheap_t *h;
1802   int rv;
1803
1804   if (!vcm->init)
1805     {
1806       char *conf_fname;
1807       char *env_var_str;
1808
1809       vcm->init = 1;
1810       vppcom_cfg_init (vcl_cfg);
1811       env_var_str = getenv (VPPCOM_ENV_DEBUG);
1812       if (env_var_str)
1813         {
1814           u32 tmp;
1815           if (sscanf (env_var_str, "%u", &tmp) != 1)
1816             clib_warning ("[%d] Invalid debug level specified in "
1817                           "the environment variable "
1818                           VPPCOM_ENV_DEBUG
1819                           " (%s)!\n", getpid (), env_var_str);
1820           else
1821             {
1822               vcm->debug = tmp;
1823               clib_warning ("[%d] configured debug level (%u) from "
1824                             VPPCOM_ENV_DEBUG "!", getpid (), vcm->debug);
1825             }
1826         }
1827       conf_fname = getenv (VPPCOM_ENV_CONF);
1828       if (!conf_fname)
1829         {
1830           conf_fname = VPPCOM_CONF_DEFAULT;
1831           if (VPPCOM_DEBUG > 0)
1832             clib_warning ("[%d] getenv '%s' failed!", getpid (),
1833                           VPPCOM_ENV_CONF);
1834         }
1835       vppcom_cfg_heapsize (conf_fname);
1836       clib_fifo_validate (vcm->client_session_index_fifo,
1837                           vcm->cfg.listen_queue_size);
1838       vppcom_cfg_read (conf_fname);
1839       env_var_str = getenv (VPPCOM_ENV_APP_NAMESPACE_ID);
1840       if (env_var_str)
1841         {
1842           u32 ns_id_vec_len = strlen (env_var_str);
1843
1844           vec_reset_length (vcm->cfg.namespace_id);
1845           vec_validate (vcm->cfg.namespace_id, ns_id_vec_len - 1);
1846           clib_memcpy (vcm->cfg.namespace_id, env_var_str, ns_id_vec_len);
1847
1848           if (VPPCOM_DEBUG > 0)
1849             clib_warning ("[%d] configured namespace_id (%v) from "
1850                           VPPCOM_ENV_APP_NAMESPACE_ID "!", getpid (),
1851                           vcm->cfg.namespace_id);
1852         }
1853       env_var_str = getenv (VPPCOM_ENV_APP_NAMESPACE_SECRET);
1854       if (env_var_str)
1855         {
1856           u64 tmp;
1857           if (sscanf (env_var_str, "%lu", &tmp) != 1)
1858             clib_warning ("[%d] Invalid namespace secret specified in "
1859                           "the environment variable "
1860                           VPPCOM_ENV_APP_NAMESPACE_SECRET
1861                           " (%s)!\n", getpid (), env_var_str);
1862           else
1863             {
1864               vcm->cfg.namespace_secret = tmp;
1865               if (VPPCOM_DEBUG > 0)
1866                 clib_warning ("[%d] configured namespace secret (%lu) from "
1867                               VPPCOM_ENV_APP_NAMESPACE_ID "!", getpid (),
1868                               vcm->cfg.namespace_secret);
1869             }
1870         }
1871       if (getenv (VPPCOM_ENV_APP_PROXY_TRANSPORT_TCP))
1872         {
1873           vcm->cfg.app_proxy_transport_tcp = 1;
1874           if (VPPCOM_DEBUG > 0)
1875             clib_warning ("[%d] configured app_proxy_transport_tcp (%u) from "
1876                           VPPCOM_ENV_APP_PROXY_TRANSPORT_TCP "!", getpid (),
1877                           vcm->cfg.app_proxy_transport_tcp);
1878         }
1879       if (getenv (VPPCOM_ENV_APP_PROXY_TRANSPORT_UDP))
1880         {
1881           vcm->cfg.app_proxy_transport_udp = 1;
1882           if (VPPCOM_DEBUG > 0)
1883             clib_warning ("[%d] configured app_proxy_transport_udp (%u) from "
1884                           VPPCOM_ENV_APP_PROXY_TRANSPORT_UDP "!", getpid (),
1885                           vcm->cfg.app_proxy_transport_udp);
1886         }
1887       if (getenv (VPPCOM_ENV_APP_SCOPE_LOCAL))
1888         {
1889           vcm->cfg.app_scope_local = 1;
1890           if (VPPCOM_DEBUG > 0)
1891             clib_warning ("[%d] configured app_scope_local (%u) from "
1892                           VPPCOM_ENV_APP_SCOPE_LOCAL "!", getpid (),
1893                           vcm->cfg.app_scope_local);
1894         }
1895       if (getenv (VPPCOM_ENV_APP_SCOPE_GLOBAL))
1896         {
1897           vcm->cfg.app_scope_global = 1;
1898           if (VPPCOM_DEBUG > 0)
1899             clib_warning ("[%d] configured app_scope_global (%u) from "
1900                           VPPCOM_ENV_APP_SCOPE_GLOBAL "!", getpid (),
1901                           vcm->cfg.app_scope_global);
1902         }
1903
1904       vcm->bind_session_index = ~0;
1905       vcm->main_cpu = os_get_thread_index ();
1906       heap = clib_mem_get_per_cpu_heap ();
1907       h = mheap_header (heap);
1908
1909       /* make the main heap thread-safe */
1910       h->flags |= MHEAP_FLAG_THREAD_SAFE;
1911
1912       vcm->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
1913
1914       clib_time_init (&vcm->clib_time);
1915       vppcom_init_error_string_table ();
1916       svm_fifo_segment_init (vcl_cfg->segment_baseva,
1917                              20 /* timeout in secs */ );
1918       clib_spinlock_init (&vcm->sessions_lockp);
1919       vppcom_api_hookup ();
1920     }
1921
1922   if (vcm->my_client_index == ~0)
1923     {
1924       vcm->app_state = STATE_APP_START;
1925       rv = vppcom_connect_to_vpp (app_name);
1926       if (rv)
1927         {
1928           clib_warning ("[%d] couldn't connect to VPP.", getpid ());
1929           return rv;
1930         }
1931
1932       if (VPPCOM_DEBUG > 0)
1933         clib_warning ("[%d] sending session enable", getpid ());
1934
1935       rv = vppcom_app_session_enable ();
1936       if (rv)
1937         {
1938           clib_warning ("[%d] vppcom_app_session_enable() failed!",
1939                         getpid ());
1940           return rv;
1941         }
1942
1943       if (VPPCOM_DEBUG > 0)
1944         clib_warning ("[%d] sending app attach", getpid ());
1945
1946       rv = vppcom_app_attach ();
1947       if (rv)
1948         {
1949           clib_warning ("[%d] vppcom_app_attach() failed!", getpid ());
1950           return rv;
1951         }
1952
1953       if (VPPCOM_DEBUG > 0)
1954         clib_warning ("[%d] app_name '%s', my_client_index %d (0x%x)",
1955                       getpid (), app_name, vcm->my_client_index,
1956                       vcm->my_client_index);
1957     }
1958
1959   return VPPCOM_OK;
1960 }
1961
1962 void
1963 vppcom_app_destroy (void)
1964 {
1965   int rv;
1966
1967   if (vcm->my_client_index == ~0)
1968     return;
1969
1970   if (VPPCOM_DEBUG > 0)
1971     clib_warning ("[%d] detaching from VPP, my_client_index %d (0x%x)",
1972                   getpid (), vcm->my_client_index, vcm->my_client_index);
1973
1974   vppcom_app_detach ();
1975   rv = vppcom_wait_for_app_state_change (STATE_APP_ENABLED);
1976   if (PREDICT_FALSE (rv))
1977     {
1978       if (VPPCOM_DEBUG > 0)
1979         clib_warning ("[%d] application detach timed out, rv = %s (%d)",
1980                       getpid (), vppcom_retval_str (rv), rv);
1981     }
1982   vl_client_disconnect_from_vlib ();
1983   vcm->my_client_index = ~0;
1984   vcm->app_state = STATE_APP_START;
1985 }
1986
1987 int
1988 vppcom_session_create (u32 vrf, u8 proto, u8 is_nonblocking)
1989 {
1990   session_t *session;
1991   u32 session_index;
1992
1993   clib_spinlock_lock (&vcm->sessions_lockp);
1994   pool_get (vcm->sessions, session);
1995   memset (session, 0, sizeof (*session));
1996   session_index = session - vcm->sessions;
1997
1998   session->vrf = vrf;
1999   session->proto = proto;
2000   session->state = STATE_START;
2001   session->is_nonblocking = is_nonblocking ? 1 : 0;
2002   clib_spinlock_unlock (&vcm->sessions_lockp);
2003
2004   if (VPPCOM_DEBUG > 0)
2005     clib_warning ("[%d] sid %d", getpid (), session_index);
2006
2007   return (int) session_index;
2008 }
2009
2010 int
2011 vppcom_session_close (uint32_t session_index)
2012 {
2013   session_t *session = 0;
2014   int rv;
2015   u8 is_server;
2016   u8 is_listen;
2017   u8 is_cut_thru;
2018   u8 is_vep;
2019   u8 is_vep_session;
2020   u32 next_sid;
2021   u32 vep_idx;
2022   session_state_t state;
2023
2024   clib_spinlock_lock (&vcm->sessions_lockp);
2025   rv = vppcom_session_at_index (session_index, &session);
2026   if (PREDICT_FALSE (rv))
2027     {
2028       if (VPPCOM_DEBUG > 0)
2029         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2030                       getpid (), session_index);
2031       clib_spinlock_unlock (&vcm->sessions_lockp);
2032       goto done;
2033     }
2034   is_server = session->is_server;
2035   is_listen = session->is_listen;
2036   is_cut_thru = session->is_cut_thru;
2037   is_vep = session->is_vep;
2038   is_vep_session = session->is_vep_session;
2039   next_sid = session->vep.next_sid;
2040   vep_idx = session->vep.vep_idx;
2041   state = session->state;
2042   clib_spinlock_unlock (&vcm->sessions_lockp);
2043
2044   if (VPPCOM_DEBUG > 0)
2045     clib_warning ("[%d] sid %d", getpid (), session_index);
2046
2047   if (is_vep)
2048     {
2049       while (next_sid != ~0)
2050         {
2051           rv = vppcom_epoll_ctl (session_index, EPOLL_CTL_DEL, next_sid, 0);
2052           if ((VPPCOM_DEBUG > 0) && (rv < 0))
2053             clib_warning ("[%d] EPOLL_CTL_DEL vep_idx %u, sid %u failed, "
2054                           "rv = %s (%d)", getpid (), vep_idx, next_sid,
2055                           vppcom_retval_str (rv), rv);
2056
2057           clib_spinlock_lock (&vcm->sessions_lockp);
2058           rv = vppcom_session_at_index (session_index, &session);
2059           if (PREDICT_FALSE (rv))
2060             {
2061               if (VPPCOM_DEBUG > 0)
2062                 clib_warning
2063                   ("[%d] invalid session, sid (%u) has been closed!",
2064                    getpid (), session_index);
2065               clib_spinlock_unlock (&vcm->sessions_lockp);
2066               goto done;
2067             }
2068           next_sid = session->vep.next_sid;
2069           clib_spinlock_unlock (&vcm->sessions_lockp);
2070         }
2071     }
2072   else
2073     {
2074       if (is_vep_session)
2075         {
2076           rv = vppcom_epoll_ctl (vep_idx, EPOLL_CTL_DEL, session_index, 0);
2077           if ((VPPCOM_DEBUG > 0) && (rv < 0))
2078             clib_warning ("[%d] EPOLL_CTL_DEL vep_idx %u, sid %u failed, "
2079                           "rv = %s (%d)", getpid (), vep_idx, session_index,
2080                           vppcom_retval_str (rv), rv);
2081         }
2082
2083       if (is_cut_thru && is_server && (state == STATE_ACCEPT))
2084         {
2085           rv = vppcom_session_unbind_cut_thru (session);
2086           if ((VPPCOM_DEBUG > 0) && (rv < 0))
2087             clib_warning ("[%d] unbind cut-thru (session %d) failed, "
2088                           "rv = %s (%d)",
2089                           getpid (), session_index,
2090                           vppcom_retval_str (rv), rv);
2091         }
2092       else if (is_server && is_listen)
2093         {
2094           rv = vppcom_session_unbind (session_index);
2095           if ((VPPCOM_DEBUG > 0) && (rv < 0))
2096             clib_warning ("[%d] unbind (session %d) failed, rv = %s (%d)",
2097                           getpid (), session_index,
2098                           vppcom_retval_str (rv), rv);
2099         }
2100       else if (state == STATE_CONNECT)
2101         if (vppcom_session_disconnect (session_index))
2102           goto done;
2103     }
2104   clib_spinlock_lock (&vcm->sessions_lockp);
2105   pool_put_index (vcm->sessions, session_index);
2106   clib_spinlock_unlock (&vcm->sessions_lockp);
2107 done:
2108   return rv;
2109 }
2110
2111 int
2112 vppcom_session_bind (uint32_t session_index, vppcom_endpt_t * ep)
2113 {
2114   session_t *session = 0;
2115   int rv;
2116
2117   if (!ep || !ep->ip)
2118     return VPPCOM_EINVAL;
2119
2120   clib_spinlock_lock (&vcm->sessions_lockp);
2121   rv = vppcom_session_at_index (session_index, &session);
2122   if (PREDICT_FALSE (rv))
2123     {
2124       clib_spinlock_unlock (&vcm->sessions_lockp);
2125       if (VPPCOM_DEBUG > 0)
2126         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2127                       getpid (), session_index);
2128       return rv;
2129     }
2130
2131   if (session->is_vep)
2132     {
2133       clib_spinlock_unlock (&vcm->sessions_lockp);
2134       if (VPPCOM_DEBUG > 0)
2135         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2136                       getpid (), session_index);
2137       return VPPCOM_EBADFD;
2138     }
2139
2140   session->vrf = ep->vrf;
2141   session->lcl_addr.is_ip4 = ep->is_ip4;
2142   session->lcl_addr.ip46 = to_ip46 (!ep->is_ip4, ep->ip);
2143   session->lcl_port = ep->port;
2144
2145   if (VPPCOM_DEBUG > 0)
2146     clib_warning ("[%d] sid %d, bound to lcl address %U lcl port %u",
2147                   getpid (), session_index, format_ip46_address,
2148                   &session->lcl_addr.ip46, session->lcl_addr.is_ip4,
2149                   clib_net_to_host_u16 (session->lcl_port));
2150
2151   clib_spinlock_unlock (&vcm->sessions_lockp);
2152   return VPPCOM_OK;
2153 }
2154
2155 int
2156 vppcom_session_listen (uint32_t listen_session_index, uint32_t q_len)
2157 {
2158   session_t *listen_session = 0;
2159   int rv;
2160
2161   clib_spinlock_lock (&vcm->sessions_lockp);
2162   rv = vppcom_session_at_index (listen_session_index, &listen_session);
2163   if (PREDICT_FALSE (rv))
2164     {
2165       clib_spinlock_unlock (&vcm->sessions_lockp);
2166       if (VPPCOM_DEBUG > 0)
2167         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2168                       getpid (), listen_session_index);
2169       return rv;
2170     }
2171
2172   if (listen_session->is_vep)
2173     {
2174       clib_spinlock_unlock (&vcm->sessions_lockp);
2175       if (VPPCOM_DEBUG > 0)
2176         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2177                       getpid (), listen_session_index);
2178       return VPPCOM_EBADFD;
2179     }
2180
2181   if (listen_session->is_listen)
2182     {
2183       clib_spinlock_unlock (&vcm->sessions_lockp);
2184       if (VPPCOM_DEBUG > 0)
2185         clib_warning ("[%d] sid (%u) is already in listen state!",
2186                       getpid (), listen_session_index);
2187       return VPPCOM_OK;
2188     }
2189
2190   if (VPPCOM_DEBUG > 0)
2191     clib_warning ("[%d] sid %d", getpid (), listen_session_index);
2192
2193   ASSERT (vcm->bind_session_index == ~0);
2194   vcm->bind_session_index = listen_session_index;
2195   vppcom_send_bind_sock (listen_session);
2196   clib_spinlock_unlock (&vcm->sessions_lockp);
2197   rv =
2198     vppcom_wait_for_session_state_change (listen_session_index, STATE_LISTEN,
2199                                           vcm->cfg.session_timeout);
2200   if (PREDICT_FALSE (rv))
2201     {
2202       vcm->bind_session_index = ~0;
2203       if (VPPCOM_DEBUG > 0)
2204         clib_warning ("[%d] server listen timed out, rv = %d (%d)",
2205                       getpid (), vppcom_retval_str (rv), rv);
2206       return rv;
2207     }
2208
2209   clib_spinlock_lock (&vcm->sessions_lockp);
2210   rv = vppcom_session_at_index (listen_session_index, &listen_session);
2211   if (PREDICT_FALSE (rv))
2212     {
2213       clib_spinlock_unlock (&vcm->sessions_lockp);
2214       if (VPPCOM_DEBUG > 0)
2215         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2216                       getpid (), listen_session_index);
2217       return rv;
2218     }
2219   listen_session->is_listen = 1;
2220   clib_fifo_validate (vcm->client_session_index_fifo, q_len);
2221   clib_spinlock_unlock (&vcm->sessions_lockp);
2222
2223   return VPPCOM_OK;
2224 }
2225
2226 int
2227 vppcom_session_accept (uint32_t listen_session_index, vppcom_endpt_t * ep,
2228                        double wait_for_time)
2229 {
2230   session_t *listen_session = 0;
2231   session_t *client_session = 0;
2232   u32 client_session_index;
2233   int rv;
2234   f64 wait_for;
2235
2236   clib_spinlock_lock (&vcm->sessions_lockp);
2237   rv = vppcom_session_at_index (listen_session_index, &listen_session);
2238   if (PREDICT_FALSE (rv))
2239     {
2240       clib_spinlock_unlock (&vcm->sessions_lockp);
2241       if (VPPCOM_DEBUG > 0)
2242         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2243                       getpid (), listen_session_index);
2244       return rv;
2245     }
2246
2247   if (listen_session->is_vep)
2248     {
2249       clib_spinlock_unlock (&vcm->sessions_lockp);
2250       if (VPPCOM_DEBUG > 0)
2251         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2252                       getpid (), listen_session_index);
2253       return VPPCOM_EBADFD;
2254     }
2255
2256   if (listen_session->state != STATE_LISTEN)
2257     {
2258       clib_spinlock_unlock (&vcm->sessions_lockp);
2259       if (VPPCOM_DEBUG > 0)
2260         clib_warning ("[%d] session not in listen state, state = %s",
2261                       getpid (),
2262                       vppcom_session_state_str (listen_session->state));
2263       return VPPCOM_EBADFD;
2264     }
2265   wait_for = listen_session->is_nonblocking ? 0 :
2266     (wait_for_time < 0) ? vcm->cfg.accept_timeout : wait_for_time;
2267
2268   if (VPPCOM_DEBUG > 0)
2269     clib_warning ("[%d] sid %d: %s (%d)", getpid (),
2270                   listen_session_index,
2271                   vppcom_session_state_str (listen_session->state),
2272                   listen_session->state);
2273   clib_spinlock_unlock (&vcm->sessions_lockp);
2274
2275   while (1)
2276     {
2277       rv = vppcom_wait_for_client_session_index (wait_for);
2278       if (rv)
2279         {
2280           if ((VPPCOM_DEBUG > 0))
2281             clib_warning ("[%d] sid %d, accept timed out, rv = %s (%d)",
2282                           getpid (), listen_session_index,
2283                           vppcom_retval_str (rv), rv);
2284           if ((wait_for == 0) || (wait_for_time > 0))
2285             return rv;
2286         }
2287       else
2288         break;
2289     }
2290
2291   clib_spinlock_lock (&vcm->sessions_lockp);
2292   clib_fifo_sub1 (vcm->client_session_index_fifo, client_session_index);
2293   rv = vppcom_session_at_index (client_session_index, &client_session);
2294   ASSERT (rv == VPPCOM_OK);
2295   ASSERT (client_session->peer_addr.is_ip4 ==
2296           listen_session->lcl_addr.is_ip4);
2297
2298   if (VPPCOM_DEBUG > 0)
2299     clib_warning ("[%d] Got a request: client sid %d", getpid (),
2300                   client_session_index);
2301
2302   // Copy the lcl information from the listening session to the client session
2303   //  client_session->lcl_port = listen_session->lcl_port;
2304   //  client_session->lcl_addr = listen_session->lcl_addr;
2305
2306   ep->vrf = client_session->vrf;
2307   ep->is_cut_thru = client_session->is_cut_thru;
2308   ep->is_ip4 = client_session->peer_addr.is_ip4;
2309   ep->port = client_session->peer_port;
2310   if (client_session->peer_addr.is_ip4)
2311     clib_memcpy (ep->ip, &client_session->peer_addr.ip46.ip4,
2312                  sizeof (ip4_address_t));
2313   else
2314     clib_memcpy (ep->ip, &client_session->peer_addr.ip46.ip6,
2315                  sizeof (ip6_address_t));
2316   if (VPPCOM_DEBUG > 0)
2317     clib_warning ("[%d] sid %d, accepted peer address %U peer port %u",
2318                   getpid (), client_session_index, format_ip46_address,
2319                   &client_session->peer_addr.ip46,
2320                   client_session->peer_addr.is_ip4,
2321                   clib_net_to_host_u16 (client_session->peer_port));
2322   clib_spinlock_unlock (&vcm->sessions_lockp);
2323   return (int) client_session_index;
2324 }
2325
2326 int
2327 vppcom_session_connect (uint32_t session_index, vppcom_endpt_t * server_ep)
2328 {
2329   session_t *session = 0;
2330   int rv;
2331
2332   clib_spinlock_lock (&vcm->sessions_lockp);
2333   rv = vppcom_session_at_index (session_index, &session);
2334   if (PREDICT_FALSE (rv))
2335     {
2336       clib_spinlock_unlock (&vcm->sessions_lockp);
2337       if (VPPCOM_DEBUG > 0)
2338         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2339                       getpid (), session_index);
2340       return rv;
2341     }
2342
2343   if (session->is_vep)
2344     {
2345       clib_spinlock_unlock (&vcm->sessions_lockp);
2346       if (VPPCOM_DEBUG > 0)
2347         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2348                       getpid (), session_index);
2349       return VPPCOM_EBADFD;
2350     }
2351
2352   if (session->state == STATE_CONNECT)
2353     {
2354       clib_spinlock_unlock (&vcm->sessions_lockp);
2355       if (VPPCOM_DEBUG > 0)
2356         clib_warning ("[%d] session, sid (%u) already connected!",
2357                       getpid (), session_index);
2358       return VPPCOM_OK;
2359     }
2360
2361   session->vrf = server_ep->vrf;
2362   session->peer_addr.is_ip4 = server_ep->is_ip4;
2363   session->peer_addr.ip46 = to_ip46 (!server_ep->is_ip4, server_ep->ip);
2364   session->peer_port = server_ep->port;
2365
2366   if (VPPCOM_DEBUG > 0)
2367     {
2368       u8 *ip_str = format (0, "%U", format_ip46_address,
2369                            &session->peer_addr.ip46,
2370                            session->peer_addr.is_ip4);
2371       clib_warning ("[%d] connect sid %d to %s server port %d proto %s",
2372                     getpid (), session_index, ip_str,
2373                     clib_net_to_host_u16 (session->peer_port),
2374                     session->proto ? "UDP" : "TCP");
2375       vec_free (ip_str);
2376     }
2377
2378   vppcom_send_connect_sock (session, session_index);
2379   clib_spinlock_unlock (&vcm->sessions_lockp);
2380   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
2381                                              vcm->cfg.session_timeout);
2382   if (PREDICT_FALSE (rv))
2383     {
2384       if (VPPCOM_DEBUG > 0)
2385         clib_warning ("[%d] connect timed out, rv = %s (%d)",
2386                       getpid (), vppcom_retval_str (rv), rv);
2387       return rv;
2388     }
2389   if (VPPCOM_DEBUG > 0)
2390     clib_warning ("[%d] sid %d connected!", getpid (), session_index);
2391
2392   return VPPCOM_OK;
2393 }
2394
2395 static inline int
2396 vppcom_session_read_internal (uint32_t session_index, void *buf, int n,
2397                               u8 peek)
2398 {
2399   session_t *session = 0;
2400   svm_fifo_t *rx_fifo;
2401   int n_read = 0;
2402   int rv;
2403   char *fifo_str;
2404   u32 poll_et;
2405
2406   ASSERT (buf);
2407
2408   clib_spinlock_lock (&vcm->sessions_lockp);
2409   rv = vppcom_session_at_index (session_index, &session);
2410   if (PREDICT_FALSE (rv))
2411     {
2412       clib_spinlock_unlock (&vcm->sessions_lockp);
2413       if (VPPCOM_DEBUG > 0)
2414         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2415                       getpid (), session_index);
2416       return rv;
2417     }
2418
2419   if (session->is_vep)
2420     {
2421       clib_spinlock_unlock (&vcm->sessions_lockp);
2422       if (VPPCOM_DEBUG > 0)
2423         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2424                       getpid (), session_index);
2425       return VPPCOM_EBADFD;
2426     }
2427
2428   if (session->state == STATE_DISCONNECT)
2429     {
2430       clib_spinlock_unlock (&vcm->sessions_lockp);
2431       if (VPPCOM_DEBUG > 0)
2432         clib_warning ("[%d] sid (%u) has been closed by remote peer!",
2433                       getpid (), session_index);
2434       return VPPCOM_ECONNRESET;
2435     }
2436
2437   rx_fifo = ((!session->is_cut_thru || session->is_server) ?
2438              session->server_rx_fifo : session->server_tx_fifo);
2439   fifo_str = ((!session->is_cut_thru || session->is_server) ?
2440               "server_rx_fifo" : "server_tx_fifo");
2441   poll_et = EPOLLET & session->vep.ev.events;
2442   clib_spinlock_unlock (&vcm->sessions_lockp);
2443
2444   do
2445     {
2446       if (peek)
2447         n_read = svm_fifo_peek (rx_fifo, 0, n, buf);
2448       else
2449         n_read = svm_fifo_dequeue_nowait (rx_fifo, n, buf);
2450     }
2451   while (!session->is_nonblocking && (n_read <= 0));
2452
2453   if (poll_et && (n_read <= 0))
2454     {
2455       clib_spinlock_lock (&vcm->sessions_lockp);
2456       session->vep.et_mask |= EPOLLIN;
2457       clib_spinlock_unlock (&vcm->sessions_lockp);
2458     }
2459
2460   if ((VPPCOM_DEBUG > 2) && (n_read > 0))
2461     clib_warning ("[%d] sid %d, read %d bytes from %s (%p)", getpid (),
2462                   session_index, n_read, fifo_str, rx_fifo);
2463
2464   return (n_read <= 0) ? VPPCOM_EAGAIN : n_read;
2465 }
2466
2467 int
2468 vppcom_session_read (uint32_t session_index, void *buf, int n)
2469 {
2470   return (vppcom_session_read_internal (session_index, buf, n, 0));
2471 }
2472
2473 static int
2474 vppcom_session_peek (uint32_t session_index, void *buf, int n)
2475 {
2476   return (vppcom_session_read_internal (session_index, buf, n, 1));
2477 }
2478
2479 static inline int
2480 vppcom_session_read_ready (session_t * session, u32 session_index)
2481 {
2482   svm_fifo_t *rx_fifo = 0;
2483   int ready = 0;
2484
2485   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
2486   if (session->is_vep)
2487     {
2488       clib_spinlock_unlock (&vcm->sessions_lockp);
2489       if (VPPCOM_DEBUG > 0)
2490         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2491                       getpid (), session_index);
2492       return VPPCOM_EBADFD;
2493     }
2494
2495   if (session->state == STATE_DISCONNECT)
2496     {
2497       if (VPPCOM_DEBUG > 0)
2498         clib_warning ("[%d] sid (%u) has been closed by remote peer!",
2499                       getpid (), session_index);
2500       return VPPCOM_ECONNRESET;
2501     }
2502
2503   if (session->is_listen)
2504     ready = clib_fifo_elts (vcm->client_session_index_fifo);
2505   else
2506     {
2507       rx_fifo = ((!session->is_cut_thru || session->is_server) ?
2508                  session->server_rx_fifo : session->server_tx_fifo);
2509
2510       ready = svm_fifo_max_dequeue (rx_fifo);
2511     }
2512
2513   if (VPPCOM_DEBUG > 3)
2514     clib_warning ("[%d] sid %d, is_listen %u, peek %s (%p), ready = %d",
2515                   getpid (), session_index, session->is_listen,
2516                   session->is_server ? "server_rx_fifo" : "server_tx_fifo",
2517                   rx_fifo, ready);
2518   if ((session->vep.ev.events & EPOLLET) && (ready == 0))
2519     session->vep.et_mask |= EPOLLIN;
2520
2521   return ready;
2522 }
2523
2524 int
2525 vppcom_session_write (uint32_t session_index, void *buf, int n)
2526 {
2527   session_t *session = 0;
2528   svm_fifo_t *tx_fifo;
2529   unix_shared_memory_queue_t *q;
2530   session_fifo_event_t evt;
2531   int rv, n_write;
2532   char *fifo_str;
2533   u32 poll_et;
2534
2535   ASSERT (buf);
2536
2537   clib_spinlock_lock (&vcm->sessions_lockp);
2538   rv = vppcom_session_at_index (session_index, &session);
2539   if (PREDICT_FALSE (rv))
2540     {
2541       clib_spinlock_unlock (&vcm->sessions_lockp);
2542       if (VPPCOM_DEBUG > 0)
2543         clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2544                       getpid (), session_index);
2545       return rv;
2546     }
2547
2548   if (session->is_vep)
2549     {
2550       clib_spinlock_unlock (&vcm->sessions_lockp);
2551       if (VPPCOM_DEBUG > 0)
2552         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2553                       getpid (), session_index);
2554       return VPPCOM_EBADFD;
2555     }
2556
2557   if (session->state == STATE_DISCONNECT)
2558     {
2559       clib_spinlock_unlock (&vcm->sessions_lockp);
2560       if (VPPCOM_DEBUG > 0)
2561         clib_warning ("[%d] sid (%u) has been closed by remote peer!",
2562                       getpid (), session_index);
2563       return VPPCOM_ECONNRESET;
2564     }
2565
2566   tx_fifo = ((!session->is_cut_thru || session->is_server) ?
2567              session->server_tx_fifo : session->server_rx_fifo);
2568   fifo_str = ((!session->is_cut_thru || session->is_server) ?
2569               "server_tx_fifo" : "server_rx_fifo");
2570   q = session->vpp_event_queue;
2571   poll_et = EPOLLET & session->vep.ev.events;
2572   clib_spinlock_unlock (&vcm->sessions_lockp);
2573
2574   do
2575     {
2576       n_write = svm_fifo_enqueue_nowait (tx_fifo, n, buf);
2577     }
2578   while (!session->is_nonblocking && (n_write <= 0));
2579
2580   /* If event wasn't set, add one */
2581   if (!session->is_cut_thru && (n_write > 0) && svm_fifo_set_event (tx_fifo))
2582     {
2583       int rval;
2584
2585       /* Fabricate TX event, send to vpp */
2586       evt.fifo = tx_fifo;
2587       evt.event_type = FIFO_EVENT_APP_TX;
2588
2589       rval = vppcom_session_at_index (session_index, &session);
2590       if (PREDICT_FALSE (rval))
2591         {
2592           if (VPPCOM_DEBUG > 1)
2593             clib_warning ("[%d] invalid session, sid (%u) has been closed!",
2594                           getpid (), session_index);
2595           return rval;
2596         }
2597       ASSERT (q);
2598       unix_shared_memory_queue_add (q, (u8 *) & evt,
2599                                     0 /* do wait for mutex */ );
2600     }
2601
2602   if (poll_et && (n_write <= 0))
2603     {
2604       clib_spinlock_lock (&vcm->sessions_lockp);
2605       session->vep.et_mask |= EPOLLOUT;
2606       clib_spinlock_unlock (&vcm->sessions_lockp);
2607     }
2608
2609   if (VPPCOM_DEBUG > 2)
2610     {
2611       if (n_write == -2)
2612         clib_warning ("[%d] sid %d, FIFO-FULL %s (%p)", getpid (),
2613                       session_index, fifo_str, tx_fifo);
2614       else
2615         clib_warning ("[%d] sid %d, wrote %d bytes to %s (%p)", getpid (),
2616                       session_index, n_write, fifo_str, tx_fifo);
2617     }
2618   return (n_write < 0) ? VPPCOM_EAGAIN : n_write;
2619 }
2620
2621 static inline int
2622 vppcom_session_write_ready (session_t * session, u32 session_index)
2623 {
2624   svm_fifo_t *tx_fifo;
2625   char *fifo_str;
2626   int ready;
2627
2628   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
2629   if (session->is_vep)
2630     {
2631       clib_spinlock_unlock (&vcm->sessions_lockp);
2632       if (VPPCOM_DEBUG > 0)
2633         clib_warning ("[%d] invalid session, sid (%u) is an epoll session!",
2634                       getpid (), session_index);
2635       return VPPCOM_EBADFD;
2636     }
2637
2638   if (session->state == STATE_DISCONNECT)
2639     {
2640       if (VPPCOM_DEBUG > 0)
2641         clib_warning ("[%d] sid (%u) has been closed by remote peer!",
2642                       getpid (), session_index);
2643       return VPPCOM_ECONNRESET;
2644     }
2645
2646   tx_fifo = ((!session->is_cut_thru || session->is_server) ?
2647              session->server_tx_fifo : session->server_rx_fifo);
2648   fifo_str = ((!session->is_cut_thru || session->is_server) ?
2649               "server_tx_fifo" : "server_rx_fifo");
2650
2651   ready = svm_fifo_max_enqueue (tx_fifo);
2652
2653   if (VPPCOM_DEBUG > 3)
2654     clib_warning ("[%d] sid %d, peek %s (%p), ready = %d", getpid (),
2655                   session_index, fifo_str, tx_fifo, ready);
2656   if ((session->vep.ev.events & EPOLLET) && (ready == 0))
2657     session->vep.et_mask |= EPOLLOUT;
2658
2659   return ready;
2660 }
2661
2662 int
2663 vppcom_select (unsigned long n_bits, unsigned long *read_map,
2664                unsigned long *write_map, unsigned long *except_map,
2665                double time_to_wait)
2666 {
2667   u32 session_index;
2668   session_t *session = 0;
2669   int rv, bits_set = 0;
2670   f64 timeout = clib_time_now (&vcm->clib_time) + time_to_wait;
2671   u32 minbits = clib_max (n_bits, BITS (uword));
2672
2673   ASSERT (sizeof (clib_bitmap_t) == sizeof (long int));
2674
2675   if (n_bits && read_map)
2676     {
2677       clib_bitmap_validate (vcm->rd_bitmap, minbits);
2678       clib_memcpy (vcm->rd_bitmap, read_map, vec_len (vcm->rd_bitmap));
2679       memset (read_map, 0, vec_len (vcm->rd_bitmap));
2680     }
2681   if (n_bits && write_map)
2682     {
2683       clib_bitmap_validate (vcm->wr_bitmap, minbits);
2684       clib_memcpy (vcm->wr_bitmap, write_map, vec_len (vcm->wr_bitmap));
2685       memset (write_map, 0, vec_len (vcm->wr_bitmap));
2686     }
2687   if (n_bits && except_map)
2688     {
2689       clib_bitmap_validate (vcm->ex_bitmap, minbits);
2690       clib_memcpy (vcm->ex_bitmap, except_map, vec_len (vcm->ex_bitmap));
2691       memset (except_map, 0, vec_len (vcm->ex_bitmap));
2692     }
2693
2694   do
2695     {
2696       /* *INDENT-OFF* */
2697       if (n_bits)
2698         {
2699           if (read_map)
2700             {
2701               clib_bitmap_foreach (session_index, vcm->rd_bitmap,
2702                 ({
2703                   clib_spinlock_lock (&vcm->sessions_lockp);
2704                   rv = vppcom_session_at_index (session_index, &session);
2705                   if (rv < 0)
2706                     {
2707                       clib_spinlock_unlock (&vcm->sessions_lockp);
2708                       if (VPPCOM_DEBUG > 1)
2709                         clib_warning ("[%d] session %d specified in "
2710                                       "read_map is closed.", getpid (),
2711                                       session_index);
2712                       bits_set = VPPCOM_EBADFD;
2713                       goto select_done;
2714                     }
2715
2716                   rv = vppcom_session_read_ready (session, session_index);
2717                   clib_spinlock_unlock (&vcm->sessions_lockp);
2718                   if (except_map && vcm->ex_bitmap &&
2719                       clib_bitmap_get (vcm->ex_bitmap, session_index) &&
2720                       (rv < 0))
2721                     {
2722                       // TBD: clib_warning
2723                       clib_bitmap_set_no_check (except_map, session_index, 1);
2724                       bits_set++;
2725                     }
2726                   else if (rv > 0)
2727                     {
2728                       // TBD: clib_warning
2729                       clib_bitmap_set_no_check (read_map, session_index, 1);
2730                       bits_set++;
2731                     }
2732                 }));
2733             }
2734
2735           if (write_map)
2736             {
2737               clib_bitmap_foreach (session_index, vcm->wr_bitmap,
2738                 ({
2739                   clib_spinlock_lock (&vcm->sessions_lockp);
2740                   rv = vppcom_session_at_index (session_index, &session);
2741                   if (rv < 0)
2742                     {
2743                       clib_spinlock_unlock (&vcm->sessions_lockp);
2744                       if (VPPCOM_DEBUG > 0)
2745                         clib_warning ("[%d] session %d specified in "
2746                                       "write_map is closed.", getpid (),
2747                                       session_index);
2748                       bits_set = VPPCOM_EBADFD;
2749                       goto select_done;
2750                     }
2751
2752                   rv = vppcom_session_write_ready (session, session_index);
2753                   clib_spinlock_unlock (&vcm->sessions_lockp);
2754                   if (write_map && (rv > 0))
2755                     {
2756                       // TBD: clib_warning
2757                       clib_bitmap_set_no_check (write_map, session_index, 1);
2758                       bits_set++;
2759                     }
2760                 }));
2761             }
2762
2763           if (except_map)
2764             {
2765               clib_bitmap_foreach (session_index, vcm->ex_bitmap,
2766                 ({
2767                   clib_spinlock_lock (&vcm->sessions_lockp);
2768                   rv = vppcom_session_at_index (session_index, &session);
2769                   if (rv < 0)
2770                     {
2771                       clib_spinlock_unlock (&vcm->sessions_lockp);
2772                       if (VPPCOM_DEBUG > 1)
2773                         clib_warning ("[%d] session %d specified in "
2774                                       "except_map is closed.", getpid (),
2775                                       session_index);
2776                       bits_set = VPPCOM_EBADFD;
2777                       goto select_done;
2778                     }
2779
2780                   rv = vppcom_session_read_ready (session, session_index);
2781                   clib_spinlock_unlock (&vcm->sessions_lockp);
2782                   if (rv < 0)
2783                     {
2784                       // TBD: clib_warning
2785                       clib_bitmap_set_no_check (except_map, session_index, 1);
2786                       bits_set++;
2787                     }
2788                 }));
2789             }
2790         }
2791       /* *INDENT-ON* */
2792     }
2793   while (clib_time_now (&vcm->clib_time) < timeout);
2794
2795 select_done:
2796   return (bits_set);
2797 }
2798
2799 static inline void
2800 vep_verify_epoll_chain (u32 vep_idx)
2801 {
2802   session_t *session;
2803   vppcom_epoll_t *vep;
2804   int rv;
2805   u32 sid = vep_idx;
2806
2807   if (VPPCOM_DEBUG <= 1)
2808     return;
2809
2810   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
2811   rv = vppcom_session_at_index (vep_idx, &session);
2812   if (PREDICT_FALSE (rv))
2813     {
2814       clib_warning ("[%d] ERROR: Invalid vep_idx (%u)!", getpid (), vep_idx);
2815       goto done;
2816     }
2817   if (PREDICT_FALSE (!session->is_vep))
2818     {
2819       clib_warning ("[%d] ERROR: vep_idx (%u) is not a vep!", getpid (),
2820                     vep_idx);
2821       goto done;
2822     }
2823   clib_warning ("[%d] vep_idx (%u): Dumping epoll chain\n"
2824                 "{\n"
2825                 "   is_vep         = %u\n"
2826                 "   is_vep_session = %u\n"
2827                 "   wait_cont_idx  = 0x%x (%u)\n"
2828                 "}\n", getpid (),
2829                 vep_idx, session->is_vep, session->is_vep_session,
2830                 session->wait_cont_idx, session->wait_cont_idx);
2831   do
2832     {
2833       vep = &session->vep;
2834       sid = vep->next_sid;
2835       if (sid != ~0)
2836         {
2837           rv = vppcom_session_at_index (sid, &session);
2838           if (PREDICT_FALSE (rv))
2839             {
2840               clib_warning ("[%d] ERROR: Invalid sid (%u)!", getpid (), sid);
2841               goto done;
2842             }
2843           if (PREDICT_FALSE (session->is_vep))
2844             clib_warning ("[%d] ERROR: sid (%u) is a vep!",
2845                           getpid (), vep_idx);
2846           else if (PREDICT_FALSE (!session->is_vep_session))
2847             {
2848               clib_warning ("[%d] ERROR: session (%u) is not a vep session!",
2849                             getpid (), sid);
2850               goto done;
2851             }
2852           if (PREDICT_FALSE (session->vep.vep_idx != vep_idx))
2853             clib_warning ("[%d] ERROR: session (%u) vep_idx (%u) != "
2854                           "vep_idx (%u)!", getpid (),
2855                           sid, session->vep.vep_idx, vep_idx);
2856           if (session->is_vep_session)
2857             {
2858               clib_warning ("vep_idx[%u]: sid 0x%x (%u)\n"
2859                             "{\n"
2860                             "   next_sid       = 0x%x (%u)\n"
2861                             "   prev_sid       = 0x%x (%u)\n"
2862                             "   vep_idx        = 0x%x (%u)\n"
2863                             "   ev.events      = 0x%x\n"
2864                             "   ev.data.u64    = 0x%llx\n"
2865                             "   et_mask        = 0x%x\n"
2866                             "}\n",
2867                             vep_idx, sid, sid,
2868                             vep->next_sid, vep->next_sid,
2869                             vep->prev_sid, vep->prev_sid,
2870                             vep->vep_idx, vep->vep_idx,
2871                             vep->ev.events, vep->ev.data.u64, vep->et_mask);
2872             }
2873         }
2874     }
2875   while (sid != ~0);
2876
2877 done:
2878   clib_warning ("[%d] vep_idx (%u): Dump complete!\n", getpid (), vep_idx);
2879 }
2880
2881 int
2882 vppcom_epoll_create (void)
2883 {
2884   session_t *vep_session;
2885   u32 vep_idx;
2886
2887   clib_spinlock_lock (&vcm->sessions_lockp);
2888   pool_get (vcm->sessions, vep_session);
2889   memset (vep_session, 0, sizeof (*vep_session));
2890   vep_idx = vep_session - vcm->sessions;
2891
2892   vep_session->is_vep = 1;
2893   vep_session->vep.vep_idx = ~0;
2894   vep_session->vep.next_sid = ~0;
2895   vep_session->vep.prev_sid = ~0;
2896   vep_session->wait_cont_idx = ~0;
2897   clib_spinlock_unlock (&vcm->sessions_lockp);
2898
2899   if (VPPCOM_DEBUG > 0)
2900     clib_warning ("[%d] Created vep_idx %u!", getpid (), vep_idx);
2901
2902   return (vep_idx);
2903 }
2904
2905 int
2906 vppcom_epoll_ctl (uint32_t vep_idx, int op, uint32_t session_index,
2907                   struct epoll_event *event)
2908 {
2909   session_t *vep_session;
2910   session_t *session;
2911   int rv;
2912
2913   if (vep_idx == session_index)
2914     {
2915       if (VPPCOM_DEBUG > 0)
2916         clib_warning ("[%d] ERROR: vep_idx == session_index (%u)!",
2917                       getpid (), vep_idx);
2918       return VPPCOM_EINVAL;
2919     }
2920
2921   clib_spinlock_lock (&vcm->sessions_lockp);
2922   rv = vppcom_session_at_index (vep_idx, &vep_session);
2923   if (PREDICT_FALSE (rv))
2924     {
2925       if (VPPCOM_DEBUG > 0)
2926         clib_warning ("[%d] ERROR: Invalid vep_idx (%u)!", vep_idx);
2927       goto done;
2928     }
2929   if (PREDICT_FALSE (!vep_session->is_vep))
2930     {
2931       if (VPPCOM_DEBUG > 0)
2932         clib_warning ("[%d] ERROR: vep_idx (%u) is not a vep!",
2933                       getpid (), vep_idx);
2934       rv = VPPCOM_EINVAL;
2935       goto done;
2936     }
2937
2938   ASSERT (vep_session->vep.vep_idx == ~0);
2939   ASSERT (vep_session->vep.prev_sid == ~0);
2940
2941   rv = vppcom_session_at_index (session_index, &session);
2942   if (PREDICT_FALSE (rv))
2943     {
2944       if (VPPCOM_DEBUG > 0)
2945         clib_warning ("[%d] ERROR: Invalid session_index (%u)!",
2946                       getpid (), session_index);
2947       goto done;
2948     }
2949   if (PREDICT_FALSE (session->is_vep))
2950     {
2951       if (VPPCOM_DEBUG > 0)
2952         clib_warning ("ERROR: session_index (%u) is a vep!", vep_idx);
2953       rv = VPPCOM_EINVAL;
2954       goto done;
2955     }
2956
2957   switch (op)
2958     {
2959     case EPOLL_CTL_ADD:
2960       if (PREDICT_FALSE (!event))
2961         {
2962           clib_warning ("[%d] ERROR: EPOLL_CTL_ADD: NULL pointer to "
2963                         "epoll_event structure!", getpid ());
2964           rv = VPPCOM_EINVAL;
2965           goto done;
2966         }
2967       if (vep_session->vep.next_sid != ~0)
2968         {
2969           session_t *next_session;
2970           rv = vppcom_session_at_index (vep_session->vep.next_sid,
2971                                         &next_session);
2972           if (PREDICT_FALSE (rv))
2973             {
2974               if (VPPCOM_DEBUG > 0)
2975                 clib_warning ("[%d] ERROR: EPOLL_CTL_ADD: Invalid "
2976                               "vep.next_sid (%u) on vep_idx (%u)!",
2977                               getpid (), vep_session->vep.next_sid, vep_idx);
2978               goto done;
2979             }
2980           ASSERT (next_session->vep.prev_sid == vep_idx);
2981           next_session->vep.prev_sid = session_index;
2982         }
2983       session->vep.next_sid = vep_session->vep.next_sid;
2984       session->vep.prev_sid = vep_idx;
2985       session->vep.vep_idx = vep_idx;
2986       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2987       session->vep.ev = *event;
2988       session->is_vep_session = 1;
2989       vep_session->vep.next_sid = session_index;
2990       if (VPPCOM_DEBUG > 1)
2991         clib_warning ("[%d] EPOLL_CTL_ADD: vep_idx %u, sid %u, events 0x%x,"
2992                       " data 0x%llx!", getpid (), vep_idx, session_index,
2993                       event->events, event->data.u64);
2994       break;
2995
2996     case EPOLL_CTL_MOD:
2997       if (PREDICT_FALSE (!event))
2998         {
2999           clib_warning ("[%d] ERROR: EPOLL_CTL_MOD: NULL pointer to "
3000                         "epoll_event structure!", getpid ());
3001           rv = VPPCOM_EINVAL;
3002           goto done;
3003         }
3004       if (PREDICT_FALSE (!session->is_vep_session &&
3005                          (session->vep.vep_idx != vep_idx)))
3006         {
3007           if (VPPCOM_DEBUG > 0)
3008             {
3009               if (!session->is_vep_session)
3010                 clib_warning ("[%d] ERROR: EPOLL_CTL_MOD: session (%u) "
3011                               "is not a vep session!",
3012                               getpid (), session_index);
3013               else
3014                 clib_warning ("[%d] ERROR: EPOLL_CTL_MOD: session (%u) "
3015                               "vep_idx (%u) != vep_idx (%u)!",
3016                               getpid (), session_index,
3017                               session->vep.vep_idx, vep_idx);
3018             }
3019           rv = VPPCOM_EINVAL;
3020           goto done;
3021         }
3022       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
3023       session->vep.ev = *event;
3024       if (VPPCOM_DEBUG > 1)
3025         clib_warning ("[%d] EPOLL_CTL_MOD: vep_idx %u, sid %u, events 0x%x,"
3026                       " data 0x%llx!", getpid (), vep_idx, session_index,
3027                       event->events, event->data.u64);
3028       break;
3029
3030     case EPOLL_CTL_DEL:
3031       if (PREDICT_FALSE (!session->is_vep_session &&
3032                          (session->vep.vep_idx != vep_idx)))
3033         {
3034           if (VPPCOM_DEBUG > 0)
3035             {
3036               if (!session->is_vep_session)
3037                 clib_warning ("[%d] ERROR: EPOLL_CTL_DEL: session (%u) "
3038                               "is not a vep session!",
3039                               getpid (), session_index);
3040               else
3041                 clib_warning ("[%d] ERROR: EPOLL_CTL_DEL: session (%u) "
3042                               "vep_idx (%u) != vep_idx (%u)!",
3043                               getpid (), session_index,
3044                               session->vep.vep_idx, vep_idx);
3045             }
3046           rv = VPPCOM_EINVAL;
3047           goto done;
3048         }
3049
3050       vep_session->wait_cont_idx =
3051         (vep_session->wait_cont_idx == session_index) ?
3052         session->vep.next_sid : vep_session->wait_cont_idx;
3053
3054       if (session->vep.prev_sid == vep_idx)
3055         vep_session->vep.next_sid = session->vep.next_sid;
3056       else
3057         {
3058           session_t *prev_session;
3059           rv = vppcom_session_at_index (session->vep.prev_sid, &prev_session);
3060           if (PREDICT_FALSE (rv))
3061             {
3062               if (VPPCOM_DEBUG > 0)
3063                 clib_warning ("[%d] ERROR: EPOLL_CTL_DEL: Invalid "
3064                               "vep.prev_sid (%u) on sid (%u)!",
3065                               getpid (), session->vep.prev_sid,
3066                               session_index);
3067               goto done;
3068             }
3069           ASSERT (prev_session->vep.next_sid == session_index);
3070           prev_session->vep.next_sid = session->vep.next_sid;
3071         }
3072       if (session->vep.next_sid != ~0)
3073         {
3074           session_t *next_session;
3075           rv = vppcom_session_at_index (session->vep.next_sid, &next_session);
3076           if (PREDICT_FALSE (rv))
3077             {
3078               if (VPPCOM_DEBUG > 0)
3079                 clib_warning ("[%d] ERROR: EPOLL_CTL_DEL: Invalid "
3080                               "vep.next_sid (%u) on sid (%u)!",
3081                               getpid (), session->vep.next_sid,
3082                               session_index);
3083               goto done;
3084             }
3085           ASSERT (next_session->vep.prev_sid == session_index);
3086           next_session->vep.prev_sid = session->vep.prev_sid;
3087         }
3088
3089       memset (&session->vep, 0, sizeof (session->vep));
3090       session->vep.next_sid = ~0;
3091       session->vep.prev_sid = ~0;
3092       session->vep.vep_idx = ~0;
3093       session->is_vep_session = 0;
3094       if (VPPCOM_DEBUG > 1)
3095         clib_warning ("[%d] EPOLL_CTL_DEL: vep_idx %u, sid %u!",
3096                       getpid (), vep_idx, session_index);
3097       break;
3098
3099     default:
3100       clib_warning ("[%d] ERROR: Invalid operation (%d)!", getpid (), op);
3101       rv = VPPCOM_EINVAL;
3102     }
3103
3104   vep_verify_epoll_chain (vep_idx);
3105
3106 done:
3107   clib_spinlock_unlock (&vcm->sessions_lockp);
3108   return rv;
3109 }
3110
3111 #define VCL_LOCK_AND_GET_SESSION(I, S)                  \
3112 do {                                                    \
3113   clib_spinlock_lock (&vcm->sessions_lockp);            \
3114   rv = vppcom_session_at_index (I, S);                  \
3115   if (PREDICT_FALSE (rv))                               \
3116     {                                                   \
3117       clib_spinlock_unlock (&vcm->sessions_lockp);      \
3118                                                         \
3119       if (VPPCOM_DEBUG > 0)                             \
3120         clib_warning ("[%s] ERROR: Invalid ##I (%u)!",  \
3121                       getpid (), I);                  \
3122                                                         \
3123       goto done;                                        \
3124     }                                                   \
3125 } while (0)
3126
3127 int
3128 vppcom_epoll_wait (uint32_t vep_idx, struct epoll_event *events,
3129                    int maxevents, double wait_for_time)
3130 {
3131   session_t *vep_session;
3132   int rv;
3133   f64 timeout = clib_time_now (&vcm->clib_time) + wait_for_time;
3134   u32 keep_trying = 1;
3135   int num_ev = 0;
3136   u32 vep_next_sid, wait_cont_idx;
3137   u8 is_vep;
3138
3139   if (PREDICT_FALSE (maxevents <= 0))
3140     {
3141       if (VPPCOM_DEBUG > 0)
3142         clib_warning ("[%d] ERROR: Invalid maxevents (%d)!",
3143                       getpid (), maxevents);
3144       return VPPCOM_EINVAL;
3145     }
3146   memset (events, 0, sizeof (*events) * maxevents);
3147
3148   VCL_LOCK_AND_GET_SESSION (vep_idx, &vep_session);
3149   vep_next_sid = vep_session->vep.next_sid;
3150   is_vep = vep_session->is_vep;
3151   wait_cont_idx = vep_session->wait_cont_idx;
3152   clib_spinlock_unlock (&vcm->sessions_lockp);
3153
3154   if (PREDICT_FALSE (!is_vep))
3155     {
3156       if (VPPCOM_DEBUG > 0)
3157         clib_warning ("[%d] ERROR: vep_idx (%u) is not a vep!",
3158                       getpid (), vep_idx);
3159       rv = VPPCOM_EINVAL;
3160       goto done;
3161     }
3162   if (PREDICT_FALSE (vep_next_sid == ~0))
3163     {
3164       if (VPPCOM_DEBUG > 0)
3165         clib_warning ("[%d] WARNING: vep_idx (%u) is empty!",
3166                       getpid (), vep_idx);
3167       goto done;
3168     }
3169
3170   do
3171     {
3172       u32 sid;
3173       u32 next_sid = ~0;
3174       session_t *session;
3175
3176       for (sid = (wait_cont_idx == ~0) ? vep_next_sid : wait_cont_idx;
3177            sid != ~0; sid = next_sid)
3178         {
3179           u32 session_events, et_mask, clear_et_mask, session_vep_idx;
3180           u8 add_event, is_vep_session;
3181           int ready;
3182           u64 session_ev_data;
3183
3184           VCL_LOCK_AND_GET_SESSION (sid, &session);
3185           next_sid = session->vep.next_sid;
3186           session_events = session->vep.ev.events;
3187           et_mask = session->vep.et_mask;
3188           is_vep = session->is_vep;
3189           is_vep_session = session->is_vep_session;
3190           session_vep_idx = session->vep.vep_idx;
3191           session_ev_data = session->vep.ev.data.u64;
3192           clib_spinlock_unlock (&vcm->sessions_lockp);
3193
3194           if (PREDICT_FALSE (is_vep))
3195             {
3196               if (VPPCOM_DEBUG > 0)
3197                 clib_warning ("[%d] ERROR: sid (%u) is a vep!",
3198                               getpid (), vep_idx);
3199               rv = VPPCOM_EINVAL;
3200               goto done;
3201             }
3202           if (PREDICT_FALSE (!is_vep_session))
3203             {
3204               if (VPPCOM_DEBUG > 0)
3205                 clib_warning ("[%d] ERROR: session (%u) is not "
3206                               "a vep session!", getpid (), sid);
3207               rv = VPPCOM_EINVAL;
3208               goto done;
3209             }
3210           if (PREDICT_FALSE (session_vep_idx != vep_idx))
3211             {
3212               clib_warning ("[%d] ERROR: session (%u) "
3213                             "vep_idx (%u) != vep_idx (%u)!",
3214                             getpid (), sid, session->vep.vep_idx, vep_idx);
3215               rv = VPPCOM_EINVAL;
3216               goto done;
3217             }
3218
3219           add_event = clear_et_mask = 0;
3220
3221           if ((EPOLLIN & session_events) && (EPOLLIN & et_mask))
3222             {
3223               VCL_LOCK_AND_GET_SESSION (sid, &session);
3224               ready = vppcom_session_read_ready (session, sid);
3225               clib_spinlock_unlock (&vcm->sessions_lockp);
3226               if (ready > 0)
3227                 {
3228                   add_event = 1;
3229                   events[num_ev].events |= EPOLLIN;
3230                   if (EPOLLET & session_events)
3231                     clear_et_mask |= EPOLLIN;
3232                 }
3233               else if (ready < 0)
3234                 {
3235                   add_event = 1;
3236                   switch (ready)
3237                     {
3238                     case VPPCOM_ECONNRESET:
3239                       events[num_ev].events |= EPOLLHUP | EPOLLRDHUP;
3240                       break;
3241
3242                     default:
3243                       events[num_ev].events |= EPOLLERR;
3244                       break;
3245                     }
3246                 }
3247             }
3248
3249           if ((EPOLLOUT & session_events) && (EPOLLOUT & et_mask))
3250             {
3251               VCL_LOCK_AND_GET_SESSION (sid, &session);
3252               ready = vppcom_session_write_ready (session, sid);
3253               clib_spinlock_unlock (&vcm->sessions_lockp);
3254               if (ready > 0)
3255                 {
3256                   add_event = 1;
3257                   events[num_ev].events |= EPOLLOUT;
3258                   if (EPOLLET & session_events)
3259                     clear_et_mask |= EPOLLOUT;
3260                 }
3261               else if (ready < 0)
3262                 {
3263                   add_event = 1;
3264                   switch (ready)
3265                     {
3266                     case VPPCOM_ECONNRESET:
3267                       events[num_ev].events |= EPOLLHUP;
3268                       break;
3269
3270                     default:
3271                       events[num_ev].events |= EPOLLERR;
3272                       break;
3273                     }
3274                 }
3275             }
3276
3277           if (add_event)
3278             {
3279               events[num_ev].data.u64 = session_ev_data;
3280               if (EPOLLONESHOT & session_events)
3281                 {
3282                   VCL_LOCK_AND_GET_SESSION (sid, &session);
3283                   session->vep.ev.events = 0;
3284                   clib_spinlock_unlock (&vcm->sessions_lockp);
3285                 }
3286               num_ev++;
3287               if (num_ev == maxevents)
3288                 {
3289                   VCL_LOCK_AND_GET_SESSION (vep_idx, &vep_session);
3290                   vep_session->wait_cont_idx = next_sid;
3291                   clib_spinlock_unlock (&vcm->sessions_lockp);
3292                   goto done;
3293                 }
3294             }
3295           if (wait_cont_idx != ~0)
3296             {
3297               if (next_sid == ~0)
3298                 next_sid = vep_next_sid;
3299               else if (next_sid == wait_cont_idx)
3300                 next_sid = ~0;
3301             }
3302         }
3303       if (wait_for_time != -1)
3304         keep_trying = (clib_time_now (&vcm->clib_time) <= timeout) ? 1 : 0;
3305     }
3306   while ((num_ev == 0) && keep_trying);
3307
3308   if (wait_cont_idx != ~0)
3309     {
3310       VCL_LOCK_AND_GET_SESSION (vep_idx, &vep_session);
3311       vep_session->wait_cont_idx = ~0;
3312       clib_spinlock_unlock (&vcm->sessions_lockp);
3313     }
3314 done:
3315   return (rv != VPPCOM_OK) ? rv : num_ev;
3316 }
3317
3318 int
3319 vppcom_session_attr (uint32_t session_index, uint32_t op,
3320                      void *buffer, uint32_t * buflen)
3321 {
3322   session_t *session;
3323   int rv = VPPCOM_OK;
3324   u32 *flags = buffer;
3325   vppcom_endpt_t *ep = buffer;
3326
3327   VCL_LOCK_AND_GET_SESSION (session_index, &session);
3328   switch (op)
3329     {
3330     case VPPCOM_ATTR_GET_NREAD:
3331       rv = vppcom_session_read_ready (session, session_index);
3332       if (VPPCOM_DEBUG > 1)
3333         clib_warning ("[%d] VPPCOM_ATTR_GET_NREAD: nread = %d",
3334                       getpid (), rv);
3335
3336       break;
3337
3338     case VPPCOM_ATTR_PEEK_NREAD:
3339       /* TBD */
3340       break;
3341
3342     case VPPCOM_ATTR_GET_FLAGS:
3343       if (buffer && buflen && (*buflen >= sizeof (*flags)))
3344         {
3345           *flags = O_RDWR | ((session->is_nonblocking) ? O_NONBLOCK : 0);
3346           *buflen = sizeof (*flags);
3347           if (VPPCOM_DEBUG > 1)
3348             clib_warning ("[%d] VPPCOM_ATTR_GET_FLAGS: flags = 0x%08x, "
3349                           "is_nonblocking = %u", getpid (), *flags,
3350                           session->is_nonblocking);
3351         }
3352       else
3353         rv = VPPCOM_EINVAL;
3354       break;
3355
3356     case VPPCOM_ATTR_SET_FLAGS:
3357       if (buffer && buflen && (*buflen >= sizeof (*flags)))
3358         {
3359           session->is_nonblocking = (*flags & O_NONBLOCK) ? 1 : 0;
3360           if (VPPCOM_DEBUG > 1)
3361             clib_warning ("[%d] VPPCOM_ATTR_SET_FLAGS: flags = 0x%08x, "
3362                           "is_nonblocking = %u", getpid (), *flags,
3363                           session->is_nonblocking);
3364         }
3365       else
3366         rv = VPPCOM_EINVAL;
3367       break;
3368
3369     case VPPCOM_ATTR_GET_PEER_ADDR:
3370       if (buffer && buflen && (*buflen >= sizeof (*ep)))
3371         {
3372           ep->vrf = session->vrf;
3373           ep->is_ip4 = session->peer_addr.is_ip4;
3374           ep->port = session->peer_port;
3375           if (session->peer_addr.is_ip4)
3376             clib_memcpy (ep->ip, &session->peer_addr.ip46.ip4,
3377                          sizeof (ip4_address_t));
3378           else
3379             clib_memcpy (ep->ip, &session->peer_addr.ip46.ip6,
3380                          sizeof (ip6_address_t));
3381           *buflen = sizeof (*ep);
3382           if (VPPCOM_DEBUG > 1)
3383             clib_warning ("[%d] VPPCOM_ATTR_GET_PEER_ADDR: sid %u is_ip4 = "
3384                           "%u, addr = %U, port %u", getpid (),
3385                           session_index, ep->is_ip4, format_ip46_address,
3386                           &session->peer_addr.ip46, ep->is_ip4,
3387                           clib_net_to_host_u16 (ep->port));
3388         }
3389       else
3390         rv = VPPCOM_EINVAL;
3391       break;
3392
3393     case VPPCOM_ATTR_GET_LCL_ADDR:
3394       if (buffer && buflen && (*buflen >= sizeof (*ep)))
3395         {
3396           ep->vrf = session->vrf;
3397           ep->is_ip4 = session->lcl_addr.is_ip4;
3398           ep->port = session->lcl_port;
3399           if (session->lcl_addr.is_ip4)
3400             clib_memcpy (ep->ip, &session->lcl_addr.ip46.ip4,
3401                          sizeof (ip4_address_t));
3402           else
3403             clib_memcpy (ep->ip, &session->lcl_addr.ip46.ip6,
3404                          sizeof (ip6_address_t));
3405           *buflen = sizeof (*ep);
3406           if (VPPCOM_DEBUG > 1)
3407             clib_warning ("[%d] VPPCOM_ATTR_GET_LCL_ADDR: sid %u is_ip4 = "
3408                           "%u, addr = %U port %d", getpid (),
3409                           session_index, ep->is_ip4, format_ip46_address,
3410                           &session->lcl_addr.ip46, ep->is_ip4,
3411                           clib_net_to_host_u16 (ep->port));
3412         }
3413       else
3414         rv = VPPCOM_EINVAL;
3415       break;
3416
3417     case VPPCOM_ATTR_SET_REUSEADDR:
3418       break;
3419
3420     case VPPCOM_ATTR_SET_BROADCAST:
3421       break;
3422
3423     case VPPCOM_ATTR_SET_V6ONLY:
3424       break;
3425
3426     case VPPCOM_ATTR_SET_KEEPALIVE:
3427       break;
3428
3429     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3430       break;
3431
3432     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3433       break;
3434
3435     default:
3436       rv = VPPCOM_EINVAL;
3437       break;
3438     }
3439
3440 done:
3441   clib_spinlock_unlock (&vcm->sessions_lockp);
3442   return rv;
3443 }
3444
3445 int
3446 vppcom_session_recvfrom (uint32_t session_index, void *buffer,
3447                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3448 {
3449   int rv = VPPCOM_OK;
3450   session_t *session = 0;
3451
3452   if (ep)
3453     {
3454       clib_spinlock_lock (&vcm->sessions_lockp);
3455       rv = vppcom_session_at_index (session_index, &session);
3456       if (PREDICT_FALSE (rv))
3457         {
3458           clib_spinlock_unlock (&vcm->sessions_lockp);
3459           if (VPPCOM_DEBUG > 0)
3460             clib_warning ("[%d] invalid session, sid (%u) has been closed!",
3461                           getpid (), session_index);
3462           rv = VPPCOM_EBADFD;
3463           clib_spinlock_unlock (&vcm->sessions_lockp);
3464           goto done;
3465         }
3466       ep->vrf = session->vrf;
3467       ep->is_ip4 = session->peer_addr.is_ip4;
3468       ep->port = session->peer_port;
3469       if (session->peer_addr.is_ip4)
3470         clib_memcpy (ep->ip, &session->peer_addr.ip46.ip4,
3471                      sizeof (ip4_address_t));
3472       else
3473         clib_memcpy (ep->ip, &session->peer_addr.ip46.ip6,
3474                      sizeof (ip6_address_t));
3475       clib_spinlock_unlock (&vcm->sessions_lockp);
3476     }
3477
3478   if (flags == 0)
3479     rv = vppcom_session_read (session_index, buffer, buflen);
3480   else if (flags & MSG_PEEK)
3481     rv = vppcom_session_peek (session_index, buffer, buflen);
3482   else
3483     {
3484       clib_warning ("[%d] Unsupport flags for recvfrom %d", getpid (), flags);
3485       rv = VPPCOM_EAFNOSUPPORT;
3486     }
3487
3488 done:
3489   return rv;
3490 }
3491
3492 int
3493 vppcom_session_sendto (uint32_t session_index, void *buffer,
3494                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
3495 {
3496   if (!buffer)
3497     return VPPCOM_EINVAL;
3498
3499   if (ep)
3500     {
3501       // TBD
3502       return VPPCOM_EINVAL;
3503     }
3504
3505   if (flags)
3506     {
3507       // TBD check the flags and do the right thing
3508       if (VPPCOM_DEBUG > 2)
3509         clib_warning ("[%d] handling flags 0x%u (%d) not implemented yet.",
3510                       getpid (), flags, flags);
3511     }
3512
3513   return (vppcom_session_write (session_index, buffer, buflen));
3514 }
3515
3516 /*
3517  * fd.io coding-style-patch-verification: ON
3518  *
3519  * Local Variables:
3520  * eval: (c-set-style "gnu")
3521  * End:
3522  */