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