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