vcl: fix disconnect from binary api
[vpp.git] / src / vcl / vcl_bapi.c
1 /*
2  * Copyright (c) 2018-2019 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 <vcl/vcl_private.h>
17 #include <vlibmemory/api.h>
18 #include <vpp/api/vpe_msg_enum.h>
19
20 #define vl_typedefs             /* define message structures */
21 #include <vpp/api/vpe_all_api_h.h>
22 #undef vl_typedefs
23
24 /* declare message handlers for each api */
25
26 #define vl_endianfun            /* define message structures */
27 #include <vpp/api/vpe_all_api_h.h>
28 #undef vl_endianfun
29
30 /* instantiate all the print functions we know about */
31 #define vl_print(handle, ...)
32 #define vl_printfun
33 #include <vpp/api/vpe_all_api_h.h>
34 #undef vl_printfun
35
36 u8 *
37 format_api_error (u8 * s, va_list * args)
38 {
39   i32 error = va_arg (*args, u32);
40   uword *p;
41
42   p = hash_get (vcm->error_string_by_error_number, -error);
43
44   if (p)
45     s = format (s, "%s (%d)", p[0], error);
46   else
47     s = format (s, "%d", error);
48   return s;
49 }
50
51 static void
52   vl_api_session_enable_disable_reply_t_handler
53   (vl_api_session_enable_disable_reply_t * mp)
54 {
55   if (mp->retval)
56     {
57       clib_warning ("VCL<%d>: session_enable_disable failed: %U", getpid (),
58                     format_api_error, ntohl (mp->retval));
59     }
60   else
61     vcm->app_state = STATE_APP_ENABLED;
62 }
63
64 static int
65 vcl_segment_attach (u64 segment_handle, char *name, ssvm_segment_type_t type,
66                     int fd)
67 {
68   fifo_segment_create_args_t _a, *a = &_a;
69   int rv;
70
71   memset (a, 0, sizeof (*a));
72   a->segment_name = (char *) name;
73   a->segment_type = type;
74
75   if (type == SSVM_SEGMENT_MEMFD)
76     a->memfd_fd = fd;
77
78   if ((rv = fifo_segment_attach (&vcm->segment_main, a)))
79     {
80       clib_warning ("svm_fifo_segment_attach ('%s') failed", name);
81       return rv;
82     }
83   vcl_segment_table_add (segment_handle, a->new_segment_indices[0]);
84   vec_reset_length (a->new_segment_indices);
85   return 0;
86 }
87
88 static void
89 vcl_segment_detach (u64 segment_handle)
90 {
91   fifo_segment_main_t *sm = &vcm->segment_main;
92   fifo_segment_t *segment;
93   u32 segment_index;
94
95   segment_index = vcl_segment_table_lookup (segment_handle);
96   if (segment_index == (u32) ~ 0)
97     return;
98   segment = fifo_segment_get_segment (sm, segment_index);
99   fifo_segment_delete (sm, segment);
100   vcl_segment_table_del (segment_handle);
101   VDBG (0, "detached segment %u handle %u", segment_index, segment_handle);
102 }
103
104 static u64
105 vcl_vpp_worker_segment_handle (u32 wrk_index)
106 {
107   return (VCL_INVALID_SEGMENT_HANDLE - wrk_index - 1);
108 }
109
110 static void
111 vl_api_app_attach_reply_t_handler (vl_api_app_attach_reply_t * mp)
112 {
113   vcl_worker_t *wrk = vcl_worker_get (0);
114   svm_msg_q_t *ctrl_mq;
115   u64 segment_handle;
116   int *fds = 0, i;
117   u32 n_fds = 0;
118
119   if (mp->retval)
120     {
121       VERR ("attach failed: %U", format_api_error, ntohl (mp->retval));
122       goto failed;
123     }
124
125   wrk->app_event_queue = uword_to_pointer (mp->app_mq, svm_msg_q_t *);
126   ctrl_mq = uword_to_pointer (mp->vpp_ctrl_mq, svm_msg_q_t *);
127   vec_validate (wrk->vpp_event_queues, mp->vpp_ctrl_mq_thread);
128   wrk->vpp_event_queues[mp->vpp_ctrl_mq_thread] = ctrl_mq;
129   vcm->ctrl_mq = wrk->ctrl_mq = ctrl_mq;
130   segment_handle = clib_net_to_host_u64 (mp->segment_handle);
131   if (segment_handle == VCL_INVALID_SEGMENT_HANDLE)
132     {
133       VERR ("invalid segment handle");
134       goto failed;
135     }
136
137   if (mp->n_fds)
138     {
139       vec_validate (fds, mp->n_fds);
140       if (vl_socket_client_recv_fd_msg2 (&wrk->bapi_sock_ctx, fds, mp->n_fds,
141                                          5))
142         goto failed;
143
144       if (mp->fd_flags & SESSION_FD_F_VPP_MQ_SEGMENT)
145         if (vcl_segment_attach (vcl_vpp_worker_segment_handle (0),
146                                 "vpp-mq-seg", SSVM_SEGMENT_MEMFD,
147                                 fds[n_fds++]))
148           goto failed;
149
150       if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
151         if (vcl_segment_attach (segment_handle, (char *) mp->segment_name,
152                                 SSVM_SEGMENT_MEMFD, fds[n_fds++]))
153           goto failed;
154
155       if (mp->fd_flags & SESSION_FD_F_MQ_EVENTFD)
156         {
157           svm_msg_q_set_consumer_eventfd (wrk->app_event_queue, fds[n_fds]);
158           vcl_mq_epoll_add_evfd (wrk, wrk->app_event_queue);
159           n_fds++;
160         }
161
162       vec_free (fds);
163     }
164   else
165     {
166       if (vcl_segment_attach (segment_handle, (char *) mp->segment_name,
167                               SSVM_SEGMENT_SHM, -1))
168         goto failed;
169     }
170
171   vcm->app_index = clib_net_to_host_u32 (mp->app_index);
172   vcm->app_state = STATE_APP_ATTACHED;
173   return;
174
175 failed:
176   vcm->app_state = STATE_APP_FAILED;
177   for (i = clib_max (n_fds - 1, 0); i < vec_len (fds); i++)
178     close (fds[i]);
179   vec_free (fds);
180 }
181
182 static void
183 vl_api_app_worker_add_del_reply_t_handler (vl_api_app_worker_add_del_reply_t *
184                                            mp)
185 {
186   int n_fds = 0, *fds = 0, i;
187   u64 segment_handle;
188   vcl_worker_t *wrk;
189   u32 wrk_index;
190
191   if (mp->retval)
192     {
193       clib_warning ("VCL<%d>: add/del worker failed: %U", getpid (),
194                     format_api_error, ntohl (mp->retval));
195       goto failed;
196     }
197
198   if (!mp->is_add)
199     return;
200
201   wrk_index = mp->context;
202   wrk = vcl_worker_get_if_valid (wrk_index);
203   if (!wrk)
204     return;
205
206   wrk->vpp_wrk_index = clib_net_to_host_u32 (mp->wrk_index);
207   wrk->app_event_queue = uword_to_pointer (mp->app_event_queue_address,
208                                            svm_msg_q_t *);
209   wrk->ctrl_mq = vcm->ctrl_mq;
210
211   segment_handle = clib_net_to_host_u64 (mp->segment_handle);
212   if (segment_handle == VCL_INVALID_SEGMENT_HANDLE)
213     {
214       clib_warning ("invalid segment handle");
215       goto failed;
216     }
217
218   if (mp->n_fds)
219     {
220       vec_validate (fds, mp->n_fds);
221       if (vl_socket_client_recv_fd_msg2 (&wrk->bapi_sock_ctx, fds, mp->n_fds,
222                                          5))
223         goto failed;
224
225       if (mp->fd_flags & SESSION_FD_F_VPP_MQ_SEGMENT)
226         if (vcl_segment_attach (vcl_vpp_worker_segment_handle (wrk_index),
227                                 "vpp-worker-seg", SSVM_SEGMENT_MEMFD,
228                                 fds[n_fds++]))
229           goto failed;
230
231       if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
232         if (vcl_segment_attach (segment_handle, (char *) mp->segment_name,
233                                 SSVM_SEGMENT_MEMFD, fds[n_fds++]))
234           goto failed;
235
236       if (mp->fd_flags & SESSION_FD_F_MQ_EVENTFD)
237         {
238           svm_msg_q_set_consumer_eventfd (wrk->app_event_queue, fds[n_fds]);
239           vcl_mq_epoll_add_evfd (wrk, wrk->app_event_queue);
240           n_fds++;
241         }
242
243       vec_free (fds);
244     }
245   else
246     {
247       if (vcl_segment_attach (segment_handle, (char *) mp->segment_name,
248                               SSVM_SEGMENT_SHM, -1))
249         goto failed;
250     }
251   vcm->app_state = STATE_APP_READY;
252   VDBG (0, "worker %u vpp-worker %u added", wrk_index, wrk->vpp_wrk_index);
253   return;
254
255 failed:
256   vcm->app_state = STATE_APP_FAILED;
257   for (i = clib_max (n_fds - 1, 0); i < vec_len (fds); i++)
258     close (fds[i]);
259   vec_free (fds);
260 }
261
262 static void
263 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
264 {
265   ssvm_segment_type_t seg_type = SSVM_SEGMENT_SHM;
266   u64 segment_handle;
267   int fd = -1;
268
269   if (mp->fd_flags)
270     {
271       vl_socket_client_recv_fd_msg (&fd, 1, 5);
272       seg_type = SSVM_SEGMENT_MEMFD;
273     }
274
275   segment_handle = clib_net_to_host_u64 (mp->segment_handle);
276   if (segment_handle == VCL_INVALID_SEGMENT_HANDLE)
277     {
278       clib_warning ("invalid segment handle");
279       return;
280     }
281
282   if (vcl_segment_attach (segment_handle, (char *) mp->segment_name,
283                           seg_type, fd))
284     {
285       clib_warning ("VCL<%d>: svm_fifo_segment_attach ('%s') failed",
286                     getpid (), mp->segment_name);
287       return;
288     }
289
290   VDBG (1, "VCL<%d>: mapped new segment '%s' size %d", getpid (),
291         mp->segment_name, mp->segment_size);
292 }
293
294 static void
295 vl_api_unmap_segment_t_handler (vl_api_unmap_segment_t * mp)
296 {
297   u64 segment_handle = clib_net_to_host_u64 (mp->segment_handle);
298   vcl_segment_detach (segment_handle);
299   VDBG (1, "Unmapped segment: %d", segment_handle);
300 }
301
302 static void
303   vl_api_application_tls_cert_add_reply_t_handler
304   (vl_api_application_tls_cert_add_reply_t * mp)
305 {
306   if (mp->retval)
307     VDBG (0, "add cert failed: %U", format_api_error, ntohl (mp->retval));
308   vcm->app_state = STATE_APP_READY;
309 }
310
311 static void
312   vl_api_application_tls_key_add_reply_t_handler
313   (vl_api_application_tls_key_add_reply_t * mp)
314 {
315   if (mp->retval)
316     VDBG (0, "add key failed: %U", format_api_error, ntohl (mp->retval));
317   vcm->app_state = STATE_APP_READY;
318 }
319
320 #define foreach_sock_msg                                                \
321 _(SESSION_ENABLE_DISABLE_REPLY, session_enable_disable_reply)           \
322 _(APP_ATTACH_REPLY, app_attach_reply)                                   \
323 _(APPLICATION_TLS_CERT_ADD_REPLY, application_tls_cert_add_reply)       \
324 _(APPLICATION_TLS_KEY_ADD_REPLY, application_tls_key_add_reply)         \
325 _(MAP_ANOTHER_SEGMENT, map_another_segment)                             \
326 _(UNMAP_SEGMENT, unmap_segment)                                         \
327 _(APP_WORKER_ADD_DEL_REPLY, app_worker_add_del_reply)                   \
328
329 void
330 vppcom_api_hookup (void)
331 {
332 #define _(N, n)                                                 \
333     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
334                            vl_api_##n##_t_handler,              \
335                            vl_noop_handler,                     \
336                            vl_api_##n##_t_endian,               \
337                            vl_api_##n##_t_print,                \
338                            sizeof(vl_api_##n##_t), 1);
339   foreach_sock_msg;
340 #undef _
341 }
342
343 /*
344  * VPP-API message functions
345  */
346 void
347 vppcom_send_session_enable_disable (u8 is_enable)
348 {
349   vcl_worker_t *wrk = vcl_worker_get_current ();
350   vl_api_session_enable_disable_t *bmp;
351   bmp = vl_msg_api_alloc (sizeof (*bmp));
352   memset (bmp, 0, sizeof (*bmp));
353
354   bmp->_vl_msg_id = ntohs (VL_API_SESSION_ENABLE_DISABLE);
355   bmp->client_index = wrk->my_client_index;
356   bmp->context = htonl (0xfeedface);
357   bmp->is_enable = is_enable;
358   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
359 }
360
361 void
362 vppcom_app_send_attach (void)
363 {
364   vcl_worker_t *wrk = vcl_worker_get_current ();
365   u8 tls_engine = CRYPTO_ENGINE_OPENSSL;
366   vl_api_app_attach_t *bmp;
367   u8 nsid_len = vec_len (vcm->cfg.namespace_id);
368   u8 app_is_proxy = (vcm->cfg.app_proxy_transport_tcp ||
369                      vcm->cfg.app_proxy_transport_udp);
370
371   tls_engine = vcm->cfg.tls_engine ? vcm->cfg.tls_engine : tls_engine;
372
373   bmp = vl_msg_api_alloc (sizeof (*bmp));
374   memset (bmp, 0, sizeof (*bmp));
375
376   bmp->_vl_msg_id = ntohs (VL_API_APP_ATTACH);
377   bmp->client_index = wrk->my_client_index;
378   bmp->context = htonl (0xfeedface);
379   bmp->options[APP_OPTIONS_FLAGS] =
380     APP_OPTIONS_FLAGS_ACCEPT_REDIRECT | APP_OPTIONS_FLAGS_ADD_SEGMENT |
381     (vcm->cfg.app_scope_local ? APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE : 0) |
382     (vcm->cfg.app_scope_global ? APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE : 0) |
383     (app_is_proxy ? APP_OPTIONS_FLAGS_IS_PROXY : 0) |
384     (vcm->cfg.use_mq_eventfd ? APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD : 0);
385   bmp->options[APP_OPTIONS_PROXY_TRANSPORT] =
386     (u64) ((vcm->cfg.app_proxy_transport_tcp ? 1 << TRANSPORT_PROTO_TCP : 0) |
387            (vcm->cfg.app_proxy_transport_udp ? 1 << TRANSPORT_PROTO_UDP : 0));
388   bmp->options[APP_OPTIONS_SEGMENT_SIZE] = vcm->cfg.segment_size;
389   bmp->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = vcm->cfg.add_segment_size;
390   bmp->options[APP_OPTIONS_RX_FIFO_SIZE] = vcm->cfg.rx_fifo_size;
391   bmp->options[APP_OPTIONS_TX_FIFO_SIZE] = vcm->cfg.tx_fifo_size;
392   bmp->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] =
393     vcm->cfg.preallocated_fifo_pairs;
394   bmp->options[APP_OPTIONS_EVT_QUEUE_SIZE] = vcm->cfg.event_queue_size;
395   bmp->options[APP_OPTIONS_TLS_ENGINE] = tls_engine;
396   if (nsid_len)
397     {
398       bmp->namespace_id_len = nsid_len;
399       clib_memcpy_fast (bmp->namespace_id, vcm->cfg.namespace_id, nsid_len);
400       bmp->options[APP_OPTIONS_NAMESPACE_SECRET] = vcm->cfg.namespace_secret;
401     }
402   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
403 }
404
405 void
406 vppcom_app_send_detach (void)
407 {
408   vcl_worker_t *wrk = vcl_worker_get_current ();
409   vl_api_application_detach_t *bmp;
410   bmp = vl_msg_api_alloc (sizeof (*bmp));
411   memset (bmp, 0, sizeof (*bmp));
412
413   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
414   bmp->client_index = wrk->my_client_index;
415   bmp->context = htonl (0xfeedface);
416   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & bmp);
417 }
418
419 void
420 vcl_send_app_worker_add_del (u8 is_add)
421 {
422   vcl_worker_t *wrk = vcl_worker_get_current ();
423   vl_api_app_worker_add_del_t *mp;
424
425   mp = vl_msg_api_alloc (sizeof (*mp));
426   memset (mp, 0, sizeof (*mp));
427
428   mp->_vl_msg_id = ntohs (VL_API_APP_WORKER_ADD_DEL);
429   mp->client_index = wrk->my_client_index;
430   mp->app_index = clib_host_to_net_u32 (vcm->app_index);
431   mp->context = wrk->wrk_index;
432   mp->is_add = is_add;
433   if (!is_add)
434     mp->wrk_index = clib_host_to_net_u32 (wrk->vpp_wrk_index);
435
436   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & mp);
437 }
438
439 void
440 vcl_send_child_worker_del (vcl_worker_t * child_wrk)
441 {
442   vcl_worker_t *wrk = vcl_worker_get_current ();
443   vl_api_app_worker_add_del_t *mp;
444
445   mp = vl_msg_api_alloc (sizeof (*mp));
446   memset (mp, 0, sizeof (*mp));
447
448   mp->_vl_msg_id = ntohs (VL_API_APP_WORKER_ADD_DEL);
449   mp->client_index = wrk->my_client_index;
450   mp->app_index = clib_host_to_net_u32 (vcm->app_index);
451   mp->context = wrk->wrk_index;
452   mp->is_add = 0;
453   mp->wrk_index = clib_host_to_net_u32 (child_wrk->vpp_wrk_index);
454
455   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & mp);
456 }
457
458 void
459 vppcom_send_application_tls_cert_add (vcl_session_t * session, char *cert,
460                                       u32 cert_len)
461 {
462   vcl_worker_t *wrk = vcl_worker_get_current ();
463   vl_api_application_tls_cert_add_t *cert_mp;
464
465   cert_mp = vl_msg_api_alloc (sizeof (*cert_mp) + cert_len);
466   clib_memset (cert_mp, 0, sizeof (*cert_mp));
467   cert_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_CERT_ADD);
468   cert_mp->client_index = wrk->my_client_index;
469   cert_mp->context = session->session_index;
470   cert_mp->cert_len = clib_host_to_net_u16 (cert_len);
471   clib_memcpy_fast (cert_mp->cert, cert, cert_len);
472   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & cert_mp);
473 }
474
475 void
476 vppcom_send_application_tls_key_add (vcl_session_t * session, char *key,
477                                      u32 key_len)
478 {
479   vcl_worker_t *wrk = vcl_worker_get_current ();
480   vl_api_application_tls_key_add_t *key_mp;
481
482   key_mp = vl_msg_api_alloc (sizeof (*key_mp) + key_len);
483   clib_memset (key_mp, 0, sizeof (*key_mp));
484   key_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_KEY_ADD);
485   key_mp->client_index = wrk->my_client_index;
486   key_mp->context = session->session_index;
487   key_mp->key_len = clib_host_to_net_u16 (key_len);
488   clib_memcpy_fast (key_mp->key, key, key_len);
489   vl_msg_api_send_shmem (wrk->vl_input_queue, (u8 *) & key_mp);
490 }
491
492 u32
493 vcl_max_nsid_len (void)
494 {
495   vl_api_application_attach_t *mp;
496   return (sizeof (mp->namespace_id) - 1);
497 }
498
499 void
500 vppcom_init_error_string_table (void)
501 {
502   vcm->error_string_by_error_number = hash_create (0, sizeof (uword));
503
504 #define _(n, v, s) hash_set (vcm->error_string_by_error_number, -v, s);
505   foreach_vnet_api_error;
506 #undef _
507
508   hash_set (vcm->error_string_by_error_number, 99, "Misc");
509 }
510
511 int
512 vppcom_connect_to_vpp (char *app_name)
513 {
514   vcl_worker_t *wrk = vcl_worker_get_current ();
515   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
516   api_main_t *am;
517
518   vlibapi_set_main (&wrk->bapi_api_ctx);
519   vlibapi_set_memory_client_main (&wrk->bapi_shm_ctx);
520   vppcom_api_hookup ();
521
522   if (vcl_cfg->vpp_api_socket_name)
523     {
524       if (vl_socket_client_connect2 (&wrk->bapi_sock_ctx,
525                                      (char *) vcl_cfg->vpp_api_socket_name,
526                                      app_name, 0 /* default rx/tx buffer */ ))
527         {
528           VERR ("app (%s) socket connect failed!", app_name);
529           return VPPCOM_ECONNREFUSED;
530         }
531
532       if (vl_socket_client_init_shm2 (&wrk->bapi_sock_ctx, 0,
533                                       1 /* want_pthread */ ))
534         {
535           VERR ("app (%s) init shm failed!", app_name);
536           return VPPCOM_ECONNREFUSED;
537         }
538     }
539   else
540     {
541       if (!vcl_cfg->vpp_api_filename)
542         vcl_cfg->vpp_api_filename = format (0, "/vpe-api%c", 0);
543
544       vl_set_memory_root_path ((char *) vcl_cfg->vpp_api_chroot);
545
546       VDBG (0, "app (%s) connecting to VPP api (%s)...",
547             app_name, vcl_cfg->vpp_api_filename);
548
549       if (vl_client_connect_to_vlib ((char *) vcl_cfg->vpp_api_filename,
550                                      app_name, vcm->cfg.vpp_api_q_length) < 0)
551         {
552           VERR ("app (%s) connect failed!", app_name);
553           return VPPCOM_ECONNREFUSED;
554         }
555     }
556
557   am = vlibapi_get_main ();
558   wrk->vl_input_queue = am->shmem_hdr->vl_input_queue;
559   wrk->my_client_index = (u32) am->my_client_index;
560   wrk->wrk_state = STATE_APP_CONN_VPP;
561
562   VDBG (0, "app (%s) is connected to VPP!", app_name);
563   vcl_evt (VCL_EVT_INIT, vcm);
564   return VPPCOM_OK;
565 }
566
567 void
568 vppcom_disconnect_from_vpp (void)
569 {
570   vcl_worker_t *wrk = vcl_worker_get_current ();
571   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
572
573   if (vcl_cfg->vpp_api_socket_name)
574     vl_socket_client_disconnect2 (&wrk->bapi_sock_ctx);
575   else
576     vl_client_disconnect_from_vlib ();
577 }
578
579 /*
580  * fd.io coding-style-patch-verification: ON
581  *
582  * Local Variables:
583  * eval: (c-set-style "gnu")
584  * End:
585  */