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