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