summaryrefslogtreecommitdiffstats
path: root/extras/strongswan/vpp_sswan/kernel_vpp_shared.c
diff options
context:
space:
mode:
Diffstat (limited to 'extras/strongswan/vpp_sswan/kernel_vpp_shared.c')
-rw-r--r--extras/strongswan/vpp_sswan/kernel_vpp_shared.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_shared.c b/extras/strongswan/vpp_sswan/kernel_vpp_shared.c
new file mode 100644
index 00000000000..9eabb6879d1
--- /dev/null
+++ b/extras/strongswan/vpp_sswan/kernel_vpp_shared.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2022 Intel and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <library.h>
+#include <utils/debug.h>
+#include <threading/thread.h>
+#include <threading/condvar.h>
+#include <threading/mutex.h>
+#include <collections/array.h>
+#include <collections/hashtable.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibmemory/memclnt.api_enum.h>
+
+#include "kernel_vpp_shared.h"
+
+#define vl_typedefs
+#define vl_endianfun
+/* Include the (first) vlib-api API definition layer */
+#include <vlibmemory/vl_memory_api_h.h>
+/* Include the current layer (third) vpp API definition layer */
+#include <vpp/api/vpe_types.api.h>
+#include <vpp/api/vpe.api.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+typedef struct private_vac_t private_vac_t;
+typedef struct vl_api_header_t vl_api_header_t;
+typedef struct vl_api_rheader_t vl_api_rheader_t;
+typedef struct want_event_reply_t want_event_reply_t;
+
+vac_t *vac;
+
+/**
+ * Private variables and functions of vac_t class.
+ */
+struct private_vac_t
+{
+
+ /**
+ * public part of the vac_t object.
+ */
+ vac_t public;
+
+ /**
+ * Timeout for VPP API replies, in ms
+ */
+ uint16_t read_timeout;
+
+ /**
+ * True if connected to VPP vlib
+ */
+ bool connected_to_vlib;
+
+ /**
+ * True if receive thread is running
+ */
+ bool rx_is_running;
+
+ /**
+ * Receive thread
+ */
+ thread_t *rx;
+
+ /**
+ * Mutex to lock receive queue
+ */
+ mutex_t *queue_lock;
+
+ /**
+ * Condition variable rx thread susspend
+ */
+ condvar_t *suspend_cv;
+
+ /**
+ * Condition variable rx thread resume
+ */
+ condvar_t *resume_cv;
+
+ /**
+ * Condition variable rx thread terminate
+ */
+ condvar_t *terminate_cv;
+
+ /**
+ * Mutex to lock send VPP API message entries
+ */
+ mutex_t *entries_lock;
+
+ /**
+ * VPP API message entries currently active, uintptr_t seq => entry_t
+ */
+ hashtable_t *entries;
+
+ /**
+ * Mutex to lock VPP API event entries
+ */
+ mutex_t *events_lock;
+
+ /**
+ * VPP API event entries currently active, uintptr_t id = event_t
+ */
+ hashtable_t *events;
+
+ /**
+ * Current sequence number for VPP API messages
+ */
+ refcount_t seq;
+};
+
+/**
+ * VPP API message header
+ */
+struct vl_api_header_t
+{
+
+ /** message ID */
+ uint16_t _vl_msg_id;
+
+ /** opaque cookie to identify the client */
+ uint32_t client_index;
+
+ /** client context, to match reply with request */
+ uint32_t context;
+} __attribute__ ((packed));
+
+/**
+ * VPP API response message header
+ */
+struct vl_api_rheader_t
+{
+
+ /** message ID */
+ uint16_t _vl_msg_id;
+
+ /** opaque cookie to identify the client */
+ uint32_t context;
+} __attribute__ ((packed));
+
+/**
+ * VPP API register event response message header
+ */
+struct want_event_reply_t
+{
+
+ /** message ID */
+ uint16_t _vl_msg_id;
+
+ /** opaque cookie to identify the client */
+ uint32_t context;
+
+ /** retrun code for the request */
+ int32_t retval;
+} __attribute__ ((packed));
+
+/**
+ * VPP API request entry the answer for a waiting thread is collected in
+ */
+typedef struct
+{
+ /** Condition variable thread is waiting */
+ condvar_t *condvar;
+ /** Array of reply msgs in a multi-message response, as struct rmsgbuf_t */
+ array_t *rmsgs;
+ /** All response messages received? */
+ bool complete;
+ /** Is VPP API dump? */
+ bool is_dump;
+} entry_t;
+
+/**
+ * Reply message buffer
+ */
+typedef struct
+{
+ /** Data length */
+ uint32_t data_len;
+ /** Reply data */
+ uint8_t data[0];
+} rmsgbuf_t;
+
+/**
+ * VPP API event entry
+ */
+typedef struct
+{
+ /** Event callback */
+ event_cb_t cb;
+ /** User data passed to callback */
+ void *ctx;
+} event_t;
+
+/**
+ * Free VPP API message
+ */
+static void
+vac_free (void *msg)
+{
+ vl_msg_api_free (msg);
+}
+
+/**
+ * Process a single VPP API message
+ */
+static void
+vac_api_handler (private_vac_t *this, void *msg)
+{
+ vl_api_rheader_t *rmp;
+ entry_t *entry;
+ rmsgbuf_t *rmsg;
+ uintptr_t seq, event_id;
+ u16 id = ntohs (*((u16 *) msg));
+ msgbuf_t *msgbuf = (msgbuf_t *) (((u8 *) msg) - offsetof (msgbuf_t, data));
+ int l = ntohl (msgbuf->data_len);
+ event_t *event;
+
+ if (l == 0)
+ {
+ DBG2 (DBG_KNL, "vac msg ID %d has wrong len %d", id, l);
+ vac_free (msg);
+ return;
+ }
+
+ rmp = (void *) msg;
+ seq = (uintptr_t) rmp->context;
+
+ this->entries_lock->lock (this->entries_lock);
+ entry = this->entries->get (this->entries, (void *) seq);
+ if (entry)
+ {
+ if (entry->is_dump)
+ {
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "control_ping_reply_f6b0b8ca");
+ if (id == msg_id)
+ {
+ entry->complete = TRUE;
+ entry->condvar->signal (entry->condvar);
+ vac_free (msg);
+ this->entries_lock->unlock (this->entries_lock);
+ return;
+ }
+ }
+ else
+ {
+ entry->complete = TRUE;
+ entry->condvar->signal (entry->condvar);
+ }
+
+ rmsg = malloc (l + sizeof (msgbuf_t));
+ rmsg->data_len = l;
+ memcpy (rmsg->data, msg, l);
+ array_insert (entry->rmsgs, ARRAY_TAIL, rmsg);
+ }
+ else
+ {
+ this->events_lock->lock (this->events_lock);
+ event_id = (uintptr_t) id;
+ event = this->events->get (this->events, (void *) event_id);
+ if (event)
+ event->cb (msg, l, event->ctx);
+ else
+ DBG1 (DBG_KNL, "received unknown vac msg seq %u id %d len %d, ignored",
+ seq, id, l);
+ this->events_lock->unlock (this->events_lock);
+ }
+
+ this->entries_lock->unlock (this->entries_lock);
+ vac_free (msg);
+}
+
+/**
+ * VPP API receive thread
+ */
+static void *
+vac_rx_thread_fn (private_vac_t *this)
+{
+ svm_queue_t *q;
+ api_main_t *am = vlibapi_get_main ();
+ vl_api_memclnt_keepalive_t *mp;
+ vl_api_memclnt_keepalive_reply_t *rmp;
+ vl_shmem_hdr_t *shmem_hdr;
+ uword msg;
+
+ q = am->vl_input_queue;
+
+ while (TRUE)
+ {
+ while (!svm_queue_sub (q, (u8 *) &msg, SVM_Q_WAIT, 0))
+ {
+ u16 id = ntohs (*((u16 *) msg));
+ switch (id)
+ {
+ case VL_API_RX_THREAD_EXIT:
+ vl_msg_api_free ((void *) msg);
+ this->queue_lock->lock (this->queue_lock);
+ this->terminate_cv->signal (this->terminate_cv);
+ this->queue_lock->unlock (this->queue_lock);
+ DBG3 (DBG_KNL, "vac received rx thread exit [%d]",
+ VL_API_RX_THREAD_EXIT);
+ thread_exit (NULL);
+ return NULL;
+ break;
+
+ case VL_API_MEMCLNT_RX_THREAD_SUSPEND:
+ vl_msg_api_free ((void *) msg);
+ this->queue_lock->lock (this->queue_lock);
+ this->suspend_cv->signal (this->suspend_cv);
+ this->resume_cv->wait (this->resume_cv, this->queue_lock);
+ this->queue_lock->unlock (this->queue_lock);
+ DBG3 (DBG_KNL, "vac received rx thread suspend [%d]",
+ VL_API_MEMCLNT_RX_THREAD_SUSPEND);
+ break;
+
+ case VL_API_MEMCLNT_READ_TIMEOUT:
+ DBG3 (DBG_KNL, "vac received read timeout [%d]",
+ VL_API_MEMCLNT_READ_TIMEOUT);
+ vl_msg_api_free ((void *) msg);
+ break;
+
+ case VL_API_MEMCLNT_KEEPALIVE:
+ mp = (void *) msg;
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ memset (rmp, 0, sizeof (*rmp));
+ u16 msg_id = vl_msg_api_get_msg_index (
+ (u8 *) "memclnt_keepalive_reply_e8d4e804");
+ rmp->_vl_msg_id = ntohs (msg_id);
+ rmp->context = mp->context;
+ shmem_hdr = am->shmem_hdr;
+ vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &rmp);
+ vl_msg_api_free ((void *) msg);
+ DBG3 (DBG_KNL, "vac received keepalive %d",
+ VL_API_MEMCLNT_KEEPALIVE);
+ break;
+
+ default:
+ vac_api_handler (this, (void *) msg);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+METHOD (vac_t, destroy, void, private_vac_t *this)
+{
+ if (this->connected_to_vlib)
+ {
+ if (this->rx)
+ {
+ api_main_t *am = vlibapi_get_main ();
+ vl_api_rx_thread_exit_t *ep;
+ bool timed_out;
+ ep = vl_msg_api_alloc (sizeof (*ep));
+ memset (ep, 0, sizeof (*ep));
+ u16 msg_id =
+ vl_msg_api_get_msg_index ((u8 *) "rx_thread_exit_c3a3a452");
+ ep->_vl_msg_id = ntohs (msg_id);
+ vl_msg_api_send_shmem (am->vl_input_queue, (u8 *) &ep);
+ this->queue_lock->lock (this->queue_lock);
+ timed_out = this->terminate_cv->timed_wait (this->terminate_cv,
+ this->queue_lock, 5000);
+ this->queue_lock->unlock (this->queue_lock);
+ if (timed_out)
+ this->rx->cancel (this->rx);
+ else
+ this->rx->join (this->rx);
+ }
+ vl_client_disconnect ();
+ vl_client_api_unmap ();
+ }
+
+ this->queue_lock->destroy (this->queue_lock);
+ this->suspend_cv->destroy (this->suspend_cv);
+ this->resume_cv->destroy (this->resume_cv);
+ this->terminate_cv->destroy (this->terminate_cv);
+ this->entries->destroy (this->entries);
+ this->entries_lock->destroy (this->entries_lock);
+ this->events->destroy (this->events);
+ this->events_lock->destroy (this->events_lock);
+
+ vac = NULL;
+ free (this);
+}
+
+/**
+ * Write a VPP API message to shared memory
+ */
+static status_t
+vac_write (private_vac_t *this, char *p, int l, uint32_t ctx)
+{
+ api_main_t *am = vlibapi_get_main ();
+ vl_api_header_t *mp = vl_msg_api_alloc (l);
+ memset (mp, 0, sizeof (*mp));
+ svm_queue_t *q;
+
+ if (!this->connected_to_vlib)
+ return FAILED;
+
+ if (!mp)
+ return FAILED;
+
+ memcpy (mp, p, l);
+ mp->client_index = am->my_client_index;
+ mp->context = ctx;
+ q = am->shmem_hdr->vl_input_queue;
+ if (svm_queue_add (q, (u8 *) &mp, 0))
+ {
+ DBG1 (DBG_KNL, "vac vpe_api_write failed");
+ vac_free (mp);
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Clean up a thread waiting entry
+ */
+static void
+destroy_entry (entry_t *entry)
+{
+ entry->condvar->destroy (entry->condvar);
+ array_destroy_function (entry->rmsgs, (void *) free, NULL);
+ free (entry);
+}
+
+/**
+ * Send VPP API message and wait for a reply
+ */
+static status_t
+send_vac (private_vac_t *this, char *in, int in_len, char **out, int *out_len,
+ bool is_dump)
+{
+ entry_t *entry;
+ uint32_t ctx = ref_get (&this->seq);
+ uintptr_t seq = (uintptr_t) ctx;
+ rmsgbuf_t *rmsg;
+ char *ptr;
+ int i;
+
+ this->entries_lock->lock (this->entries_lock);
+ INIT (entry, .condvar = condvar_create (CONDVAR_TYPE_DEFAULT),
+ .rmsgs = array_create (0, 0), .is_dump = is_dump, );
+ this->entries->put (this->entries, (void *) seq, entry);
+
+ if (vac_write (this, in, in_len, ctx))
+ {
+ destroy_entry (entry);
+ this->entries_lock->unlock (this->entries_lock);
+ return FAILED;
+ }
+
+ if (is_dump)
+ {
+ vl_api_control_ping_t *mp;
+ status_t rv;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "control_ping_51077d14");
+ mp->_vl_msg_id = ntohs (msg_id);
+ rv = vac_write (this, (char *) mp, sizeof (*mp), ctx);
+ vl_msg_api_free (mp);
+ if (rv)
+ {
+ DBG2 (DBG_KNL, "vac_write VL_API_CONTROL_PING failed");
+ destroy_entry (entry);
+ this->entries_lock->unlock (this->entries_lock);
+ return FAILED;
+ }
+ }
+
+ while (!entry->complete)
+ {
+ if (this->read_timeout)
+ {
+ if (entry->condvar->timed_wait (entry->condvar, this->entries_lock,
+ this->read_timeout * 1000))
+ {
+ break;
+ }
+ }
+ else
+ {
+ entry->condvar->wait (entry->condvar, this->entries_lock);
+ }
+ }
+
+ this->entries->remove (this->entries, (void *) seq);
+ this->entries_lock->unlock (this->entries_lock);
+
+ if (!entry->complete)
+ {
+ destroy_entry (entry);
+ DBG1 (DBG_KNL, "vac timeout");
+ return OUT_OF_RES;
+ }
+
+ for (i = 0, *out_len = 0; i < array_count (entry->rmsgs); i++)
+ {
+ array_get (entry->rmsgs, i, &rmsg);
+ *out_len += rmsg->data_len;
+ }
+ ptr = malloc (*out_len);
+ *out = ptr;
+ while (array_remove (entry->rmsgs, ARRAY_HEAD, &rmsg))
+ {
+ memcpy (ptr, rmsg->data, rmsg->data_len);
+ ptr += rmsg->data_len;
+ free (rmsg);
+ }
+
+ destroy_entry (entry);
+
+ return SUCCESS;
+}
+
+METHOD (vac_t, vac_send, status_t, private_vac_t *this, char *in, int in_len,
+ char **out, int *out_len)
+{
+ return send_vac (this, in, in_len, out, out_len, FALSE);
+}
+
+METHOD (vac_t, vac_send_dump, status_t, private_vac_t *this, char *in,
+ int in_len, char **out, int *out_len)
+{
+ return send_vac (this, in, in_len, out, out_len, TRUE);
+}
+
+METHOD (vac_t, register_event, status_t, private_vac_t *this, char *in,
+ int in_len, event_cb_t cb, uint16_t event_id, void *ctx)
+{
+ char *out;
+ int out_len;
+ want_event_reply_t *rmp;
+ uintptr_t id = (uintptr_t) event_id;
+ event_t *event;
+
+ if (vac->send (vac, in, in_len, &out, &out_len))
+ return FAILED;
+ rmp = (void *) out;
+ if (rmp->retval)
+ return FAILED;
+ free (out);
+ vl_msg_api_free (in);
+ this->events_lock->lock (this->events_lock);
+ INIT (event, .cb = cb, .ctx = ctx, );
+ this->events->put (this->events, (void *) id, event);
+ this->events_lock->unlock (this->events_lock);
+
+ return SUCCESS;
+}
+
+vac_t *
+vac_create (char *name)
+{
+ private_vac_t *this;
+
+ INIT(this,
+ .public = {
+ .destroy = _destroy,
+ .send = _vac_send,
+ .send_dump = _vac_send_dump,
+ .register_event = _register_event,
+ },
+ .rx_is_running = FALSE,
+ .read_timeout = lib->settings->get_int(lib->settings,
+ "%s.plugins.kernel-vpp.read_timeout", 0, lib->ns),
+ .queue_lock = mutex_create(MUTEX_TYPE_DEFAULT),
+ .suspend_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .resume_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .terminate_cv = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .entries_lock = mutex_create(MUTEX_TYPE_RECURSIVE),
+ .entries = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
+ .events_lock = mutex_create(MUTEX_TYPE_DEFAULT),
+ .events = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
+ .seq = 0,
+ );
+
+ clib_mem_init_thread_safe (0, 256 << 20);
+
+ if (vl_client_api_map ("/vpe-api"))
+ {
+ DBG1 (DBG_KNL, "vac unable to map");
+ destroy (this);
+ return NULL;
+ }
+
+ if (vl_client_connect (name, 0, 32) < 0)
+ {
+ DBG1 (DBG_KNL, "vac unable to connect");
+ vl_client_api_unmap ();
+ destroy (this);
+ return NULL;
+ }
+
+ this->connected_to_vlib = TRUE;
+
+ this->rx = thread_create ((thread_main_t) vac_rx_thread_fn, this);
+ if (!this->rx)
+ {
+ vl_client_api_unmap ();
+ destroy (this);
+ return NULL;
+ }
+ this->rx_is_running = TRUE;
+
+ vac = &this->public;
+ return &this->public;
+}