9eabb6879d13794cc3c3ac4e190fed97be4be76c
[vpp.git] / extras / strongswan / vpp_sswan / kernel_vpp_shared.c
1 /*
2  * Copyright (c) 2022 Intel and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
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 <library.h>
17 #include <utils/debug.h>
18 #include <threading/thread.h>
19 #include <threading/condvar.h>
20 #include <threading/mutex.h>
21 #include <collections/array.h>
22 #include <collections/hashtable.h>
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vlibmemory/memclnt.api_enum.h>
26
27 #include "kernel_vpp_shared.h"
28
29 #define vl_typedefs
30 #define vl_endianfun
31 /* Include the (first) vlib-api API definition layer */
32 #include <vlibmemory/vl_memory_api_h.h>
33 /* Include the current layer (third) vpp API definition layer */
34 #include <vpp/api/vpe_types.api.h>
35 #include <vpp/api/vpe.api.h>
36 #undef vl_typedefs
37 #undef vl_endianfun
38
39 typedef struct private_vac_t private_vac_t;
40 typedef struct vl_api_header_t vl_api_header_t;
41 typedef struct vl_api_rheader_t vl_api_rheader_t;
42 typedef struct want_event_reply_t want_event_reply_t;
43
44 vac_t *vac;
45
46 /**
47  * Private variables and functions of vac_t class.
48  */
49 struct private_vac_t
50 {
51
52   /**
53    * public part of the vac_t object.
54    */
55   vac_t public;
56
57   /**
58    * Timeout for VPP API replies, in ms
59    */
60   uint16_t read_timeout;
61
62   /**
63    * True if connected to VPP vlib
64    */
65   bool connected_to_vlib;
66
67   /**
68    * True if receive thread is running
69    */
70   bool rx_is_running;
71
72   /**
73    * Receive thread
74    */
75   thread_t *rx;
76
77   /**
78    * Mutex to lock receive queue
79    */
80   mutex_t *queue_lock;
81
82   /**
83    * Condition variable rx thread susspend
84    */
85   condvar_t *suspend_cv;
86
87   /**
88    * Condition variable rx thread resume
89    */
90   condvar_t *resume_cv;
91
92   /**
93    * Condition variable rx thread terminate
94    */
95   condvar_t *terminate_cv;
96
97   /**
98    * Mutex to lock send VPP API message entries
99    */
100   mutex_t *entries_lock;
101
102   /**
103    * VPP API message entries currently active, uintptr_t seq => entry_t
104    */
105   hashtable_t *entries;
106
107   /**
108    * Mutex to lock VPP API event entries
109    */
110   mutex_t *events_lock;
111
112   /**
113    * VPP API event entries currently active, uintptr_t id = event_t
114    */
115   hashtable_t *events;
116
117   /**
118    * Current sequence number for VPP API messages
119    */
120   refcount_t seq;
121 };
122
123 /**
124  * VPP API message header
125  */
126 struct vl_api_header_t
127 {
128
129   /** message ID */
130   uint16_t _vl_msg_id;
131
132   /** opaque cookie to identify the client */
133   uint32_t client_index;
134
135   /** client context, to match reply with request */
136   uint32_t context;
137 } __attribute__ ((packed));
138
139 /**
140  * VPP API response message header
141  */
142 struct vl_api_rheader_t
143 {
144
145   /** message ID */
146   uint16_t _vl_msg_id;
147
148   /** opaque cookie to identify the client */
149   uint32_t context;
150 } __attribute__ ((packed));
151
152 /**
153  * VPP API register event response message header
154  */
155 struct want_event_reply_t
156 {
157
158   /** message ID */
159   uint16_t _vl_msg_id;
160
161   /** opaque cookie to identify the client */
162   uint32_t context;
163
164   /** retrun code for the request */
165   int32_t retval;
166 } __attribute__ ((packed));
167
168 /**
169  * VPP API request entry the answer for a waiting thread is collected in
170  */
171 typedef struct
172 {
173   /** Condition variable thread is waiting */
174   condvar_t *condvar;
175   /** Array of reply msgs in a multi-message response, as struct rmsgbuf_t */
176   array_t *rmsgs;
177   /** All response messages received? */
178   bool complete;
179   /** Is VPP API dump? */
180   bool is_dump;
181 } entry_t;
182
183 /**
184  * Reply message buffer
185  */
186 typedef struct
187 {
188   /** Data length */
189   uint32_t data_len;
190   /** Reply data */
191   uint8_t data[0];
192 } rmsgbuf_t;
193
194 /**
195  * VPP API event entry
196  */
197 typedef struct
198 {
199   /** Event callback */
200   event_cb_t cb;
201   /** User data passed to callback */
202   void *ctx;
203 } event_t;
204
205 /**
206  * Free VPP API message
207  */
208 static void
209 vac_free (void *msg)
210 {
211   vl_msg_api_free (msg);
212 }
213
214 /**
215  * Process a single VPP API message
216  */
217 static void
218 vac_api_handler (private_vac_t *this, void *msg)
219 {
220   vl_api_rheader_t *rmp;
221   entry_t *entry;
222   rmsgbuf_t *rmsg;
223   uintptr_t seq, event_id;
224   u16 id = ntohs (*((u16 *) msg));
225   msgbuf_t *msgbuf = (msgbuf_t *) (((u8 *) msg) - offsetof (msgbuf_t, data));
226   int l = ntohl (msgbuf->data_len);
227   event_t *event;
228
229   if (l == 0)
230     {
231       DBG2 (DBG_KNL, "vac msg ID %d has wrong len %d", id, l);
232       vac_free (msg);
233       return;
234     }
235
236   rmp = (void *) msg;
237   seq = (uintptr_t) rmp->context;
238
239   this->entries_lock->lock (this->entries_lock);
240   entry = this->entries->get (this->entries, (void *) seq);
241   if (entry)
242     {
243       if (entry->is_dump)
244         {
245           u16 msg_id =
246             vl_msg_api_get_msg_index ((u8 *) "control_ping_reply_f6b0b8ca");
247           if (id == msg_id)
248             {
249               entry->complete = TRUE;
250               entry->condvar->signal (entry->condvar);
251               vac_free (msg);
252               this->entries_lock->unlock (this->entries_lock);
253               return;
254             }
255         }
256       else
257         {
258           entry->complete = TRUE;
259           entry->condvar->signal (entry->condvar);
260         }
261
262       rmsg = malloc (l + sizeof (msgbuf_t));
263       rmsg->data_len = l;
264       memcpy (rmsg->data, msg, l);
265       array_insert (entry->rmsgs, ARRAY_TAIL, rmsg);
266     }
267   else
268     {
269       this->events_lock->lock (this->events_lock);
270       event_id = (uintptr_t) id;
271       event = this->events->get (this->events, (void *) event_id);
272       if (event)
273         event->cb (msg, l, event->ctx);
274       else
275         DBG1 (DBG_KNL, "received unknown vac msg seq %u id %d len %d, ignored",
276               seq, id, l);
277       this->events_lock->unlock (this->events_lock);
278     }
279
280   this->entries_lock->unlock (this->entries_lock);
281   vac_free (msg);
282 }
283
284 /**
285  * VPP API receive thread
286  */
287 static void *
288 vac_rx_thread_fn (private_vac_t *this)
289 {
290   svm_queue_t *q;
291   api_main_t *am = vlibapi_get_main ();
292   vl_api_memclnt_keepalive_t *mp;
293   vl_api_memclnt_keepalive_reply_t *rmp;
294   vl_shmem_hdr_t *shmem_hdr;
295   uword msg;
296
297   q = am->vl_input_queue;
298
299   while (TRUE)
300     {
301       while (!svm_queue_sub (q, (u8 *) &msg, SVM_Q_WAIT, 0))
302         {
303           u16 id = ntohs (*((u16 *) msg));
304           switch (id)
305             {
306             case VL_API_RX_THREAD_EXIT:
307               vl_msg_api_free ((void *) msg);
308               this->queue_lock->lock (this->queue_lock);
309               this->terminate_cv->signal (this->terminate_cv);
310               this->queue_lock->unlock (this->queue_lock);
311               DBG3 (DBG_KNL, "vac received rx thread exit [%d]",
312                     VL_API_RX_THREAD_EXIT);
313               thread_exit (NULL);
314               return NULL;
315               break;
316
317             case VL_API_MEMCLNT_RX_THREAD_SUSPEND:
318               vl_msg_api_free ((void *) msg);
319               this->queue_lock->lock (this->queue_lock);
320               this->suspend_cv->signal (this->suspend_cv);
321               this->resume_cv->wait (this->resume_cv, this->queue_lock);
322               this->queue_lock->unlock (this->queue_lock);
323               DBG3 (DBG_KNL, "vac received rx thread suspend [%d]",
324                     VL_API_MEMCLNT_RX_THREAD_SUSPEND);
325               break;
326
327             case VL_API_MEMCLNT_READ_TIMEOUT:
328               DBG3 (DBG_KNL, "vac received read timeout [%d]",
329                     VL_API_MEMCLNT_READ_TIMEOUT);
330               vl_msg_api_free ((void *) msg);
331               break;
332
333             case VL_API_MEMCLNT_KEEPALIVE:
334               mp = (void *) msg;
335               rmp = vl_msg_api_alloc (sizeof (*rmp));
336               memset (rmp, 0, sizeof (*rmp));
337               u16 msg_id = vl_msg_api_get_msg_index (
338                 (u8 *) "memclnt_keepalive_reply_e8d4e804");
339               rmp->_vl_msg_id = ntohs (msg_id);
340               rmp->context = mp->context;
341               shmem_hdr = am->shmem_hdr;
342               vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &rmp);
343               vl_msg_api_free ((void *) msg);
344               DBG3 (DBG_KNL, "vac received keepalive %d",
345                     VL_API_MEMCLNT_KEEPALIVE);
346               break;
347
348             default:
349               vac_api_handler (this, (void *) msg);
350             }
351         }
352     }
353
354   return NULL;
355 }
356
357 METHOD (vac_t, destroy, void, private_vac_t *this)
358 {
359   if (this->connected_to_vlib)
360     {
361       if (this->rx)
362         {
363           api_main_t *am = vlibapi_get_main ();
364           vl_api_rx_thread_exit_t *ep;
365           bool timed_out;
366           ep = vl_msg_api_alloc (sizeof (*ep));
367           memset (ep, 0, sizeof (*ep));
368           u16 msg_id =
369             vl_msg_api_get_msg_index ((u8 *) "rx_thread_exit_c3a3a452");
370           ep->_vl_msg_id = ntohs (msg_id);
371           vl_msg_api_send_shmem (am->vl_input_queue, (u8 *) &ep);
372           this->queue_lock->lock (this->queue_lock);
373           timed_out = this->terminate_cv->timed_wait (this->terminate_cv,
374                                                       this->queue_lock, 5000);
375           this->queue_lock->unlock (this->queue_lock);
376           if (timed_out)
377             this->rx->cancel (this->rx);
378           else
379             this->rx->join (this->rx);
380         }
381       vl_client_disconnect ();
382       vl_client_api_unmap ();
383     }
384
385   this->queue_lock->destroy (this->queue_lock);
386   this->suspend_cv->destroy (this->suspend_cv);
387   this->resume_cv->destroy (this->resume_cv);
388   this->terminate_cv->destroy (this->terminate_cv);
389   this->entries->destroy (this->entries);
390   this->entries_lock->destroy (this->entries_lock);
391   this->events->destroy (this->events);
392   this->events_lock->destroy (this->events_lock);
393
394   vac = NULL;
395   free (this);
396 }
397
398 /**
399  * Write a VPP API message to shared memory
400  */
401 static status_t
402 vac_write (private_vac_t *this, char *p, int l, uint32_t ctx)
403 {
404   api_main_t *am = vlibapi_get_main ();
405   vl_api_header_t *mp = vl_msg_api_alloc (l);
406   memset (mp, 0, sizeof (*mp));
407   svm_queue_t *q;
408
409   if (!this->connected_to_vlib)
410     return FAILED;
411
412   if (!mp)
413     return FAILED;
414
415   memcpy (mp, p, l);
416   mp->client_index = am->my_client_index;
417   mp->context = ctx;
418   q = am->shmem_hdr->vl_input_queue;
419   if (svm_queue_add (q, (u8 *) &mp, 0))
420     {
421       DBG1 (DBG_KNL, "vac vpe_api_write failed");
422       vac_free (mp);
423       return FAILED;
424     }
425
426   return SUCCESS;
427 }
428
429 /**
430  * Clean up a thread waiting entry
431  */
432 static void
433 destroy_entry (entry_t *entry)
434 {
435   entry->condvar->destroy (entry->condvar);
436   array_destroy_function (entry->rmsgs, (void *) free, NULL);
437   free (entry);
438 }
439
440 /**
441  * Send VPP API message and wait for a reply
442  */
443 static status_t
444 send_vac (private_vac_t *this, char *in, int in_len, char **out, int *out_len,
445           bool is_dump)
446 {
447   entry_t *entry;
448   uint32_t ctx = ref_get (&this->seq);
449   uintptr_t seq = (uintptr_t) ctx;
450   rmsgbuf_t *rmsg;
451   char *ptr;
452   int i;
453
454   this->entries_lock->lock (this->entries_lock);
455   INIT (entry, .condvar = condvar_create (CONDVAR_TYPE_DEFAULT),
456         .rmsgs = array_create (0, 0), .is_dump = is_dump, );
457   this->entries->put (this->entries, (void *) seq, entry);
458
459   if (vac_write (this, in, in_len, ctx))
460     {
461       destroy_entry (entry);
462       this->entries_lock->unlock (this->entries_lock);
463       return FAILED;
464     }
465
466   if (is_dump)
467     {
468       vl_api_control_ping_t *mp;
469       status_t rv;
470       mp = vl_msg_api_alloc (sizeof (*mp));
471       memset (mp, 0, sizeof (*mp));
472       u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "control_ping_51077d14");
473       mp->_vl_msg_id = ntohs (msg_id);
474       rv = vac_write (this, (char *) mp, sizeof (*mp), ctx);
475       vl_msg_api_free (mp);
476       if (rv)
477         {
478           DBG2 (DBG_KNL, "vac_write VL_API_CONTROL_PING failed");
479           destroy_entry (entry);
480           this->entries_lock->unlock (this->entries_lock);
481           return FAILED;
482         }
483     }
484
485   while (!entry->complete)
486     {
487       if (this->read_timeout)
488         {
489           if (entry->condvar->timed_wait (entry->condvar, this->entries_lock,
490                                           this->read_timeout * 1000))
491             {
492               break;
493             }
494         }
495       else
496         {
497           entry->condvar->wait (entry->condvar, this->entries_lock);
498         }
499     }
500
501   this->entries->remove (this->entries, (void *) seq);
502   this->entries_lock->unlock (this->entries_lock);
503
504   if (!entry->complete)
505     {
506       destroy_entry (entry);
507       DBG1 (DBG_KNL, "vac timeout");
508       return OUT_OF_RES;
509     }
510
511   for (i = 0, *out_len = 0; i < array_count (entry->rmsgs); i++)
512     {
513       array_get (entry->rmsgs, i, &rmsg);
514       *out_len += rmsg->data_len;
515     }
516   ptr = malloc (*out_len);
517   *out = ptr;
518   while (array_remove (entry->rmsgs, ARRAY_HEAD, &rmsg))
519     {
520       memcpy (ptr, rmsg->data, rmsg->data_len);
521       ptr += rmsg->data_len;
522       free (rmsg);
523     }
524
525   destroy_entry (entry);
526
527   return SUCCESS;
528 }
529
530 METHOD (vac_t, vac_send, status_t, private_vac_t *this, char *in, int in_len,
531         char **out, int *out_len)
532 {
533   return send_vac (this, in, in_len, out, out_len, FALSE);
534 }
535
536 METHOD (vac_t, vac_send_dump, status_t, private_vac_t *this, char *in,
537         int in_len, char **out, int *out_len)
538 {
539   return send_vac (this, in, in_len, out, out_len, TRUE);
540 }
541
542 METHOD (vac_t, register_event, status_t, private_vac_t *this, char *in,
543         int in_len, event_cb_t cb, uint16_t event_id, void *ctx)
544 {
545   char *out;
546   int out_len;
547   want_event_reply_t *rmp;
548   uintptr_t id = (uintptr_t) event_id;
549   event_t *event;
550
551   if (vac->send (vac, in, in_len, &out, &out_len))
552     return FAILED;
553   rmp = (void *) out;
554   if (rmp->retval)
555     return FAILED;
556   free (out);
557   vl_msg_api_free (in);
558   this->events_lock->lock (this->events_lock);
559   INIT (event, .cb = cb, .ctx = ctx, );
560   this->events->put (this->events, (void *) id, event);
561   this->events_lock->unlock (this->events_lock);
562
563   return SUCCESS;
564 }
565
566 vac_t *
567 vac_create (char *name)
568 {
569   private_vac_t *this;
570
571   INIT(this,
572             .public = {
573                     .destroy = _destroy,
574                     .send = _vac_send,
575                     .send_dump = _vac_send_dump,
576                     .register_event = _register_event,
577             },
578             .rx_is_running = FALSE,
579             .read_timeout = lib->settings->get_int(lib->settings,
580                             "%s.plugins.kernel-vpp.read_timeout", 0, lib->ns),
581             .queue_lock = mutex_create(MUTEX_TYPE_DEFAULT),
582             .suspend_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
583             .resume_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
584             .terminate_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
585             .entries_lock = mutex_create(MUTEX_TYPE_RECURSIVE),
586             .entries = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
587             .events_lock = mutex_create(MUTEX_TYPE_DEFAULT),
588             .events = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
589             .seq = 0,
590     );
591
592   clib_mem_init_thread_safe (0, 256 << 20);
593
594   if (vl_client_api_map ("/vpe-api"))
595     {
596       DBG1 (DBG_KNL, "vac unable to map");
597       destroy (this);
598       return NULL;
599     }
600
601   if (vl_client_connect (name, 0, 32) < 0)
602     {
603       DBG1 (DBG_KNL, "vac unable to connect");
604       vl_client_api_unmap ();
605       destroy (this);
606       return NULL;
607     }
608
609   this->connected_to_vlib = TRUE;
610
611   this->rx = thread_create ((thread_main_t) vac_rx_thread_fn, this);
612   if (!this->rx)
613     {
614       vl_client_api_unmap ();
615       destroy (this);
616       return NULL;
617     }
618   this->rx_is_running = TRUE;
619
620   vac = &this->public;
621   return &this->public;
622 }