vpp-swan: Add plugin for vpp-swan 52/36552/9
authorGabriel Oginski <gabrielx.oginski@intel.com>
Wed, 29 Jun 2022 12:54:30 +0000 (12:54 +0000)
committerFan Zhang <royzhang1980@hotmail.com>
Wed, 28 Sep 2022 17:45:26 +0000 (17:45 +0000)
Added plugin vpp-swan is a plugin that helps offloading
Strongswan IPsec ESP process from Linux Kernel to VPP.

Type: feature
Signed-off-by: Gabriel Oginski <gabrielx.oginski@intel.com>
Change-Id: Iec77945892453fac1890d3c49d7d86fc6b09c893

13 files changed:
MAINTAINERS
extras/strongswan/vpp_sswan/Makefile [new file with mode: 0644]
extras/strongswan/vpp_sswan/README.rst [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel-vpp.conf [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_net.c [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_net.h [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_plugin.c [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_plugin.h [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_shared.c [new file with mode: 0644]
extras/strongswan/vpp_sswan/kernel_vpp_shared.h [new file with mode: 0644]
extras/strongswan/vpp_sswan/swanctl.conf [new file with mode: 0644]

index 4bc34b0..865d1f2 100644 (file)
@@ -817,6 +817,12 @@ I: rpm-packaging
 M:     Laszlo Kiraly <laszlo.kiraly@est.tech>
 F:     src/extras/rpm/opensuse
 
+VPP StrongSwan Daemon
+I:     vpp-swan
+M:     Fan Zhang <roy.fan.zhang@intel.com>
+M:     Gabriel Oginski <gabrielx.oginski@intel.com>
+F:     extras/strongswan/vpp_sswan
+
 THE REST
 I:     misc
 M:     vpp-dev Mailing List <vpp-dev@fd.io>
diff --git a/extras/strongswan/vpp_sswan/Makefile b/extras/strongswan/vpp_sswan/Makefile
new file mode 100644 (file)
index 0000000..cb642c8
--- /dev/null
@@ -0,0 +1,108 @@
+# the directory to the strongSwan sources
+SWANDIR=${CURDIR}/../../../build-root/build-vpp-native/external/sswan
+# location of config.h
+CONFIGH=$(SWANDIR)/config.h
+# default install prefix: /usr/local or /usr
+PREFIX=/usr
+# location of the installed strongSwan libraries
+SWANLIBS=$(PREFIX)/lib/ipsec/
+# location of the strongSwan plugins
+SWANPLUGINS=$(PREFIX)/lib/ipsec/plugins
+# location of the strongSwan archive
+SWANARCHIVE=${CURDIR}/../../../build/external/downloads
+# default install configuration files:
+PREFIX_SYS_CONF=/etc
+# target location of the plugin config snippet: $(PREFIX)/etc/strongswan.d/charon/ or /etc/strongswan.d/charon/
+PLUGINCONF=$(PREFIX_SYS_CONF)/strongswan.d/charon/
+# location of the  VPP libraries
+VPPLIBS=$(CURDIR)/../../../build-root/install-vpp-native/vpp/lib/x86_64-linux-gnu
+# the directory to the VPP sources
+VPPDIR=../../../build-root/install-vpp-native/vpp/include
+
+TARGET=libstrongswan-kernel-vpp.so
+
+# tested only with 5.9.5 and 5.9.6 version of strongSwan
+VERSION_SSWAN=5.9.6
+
+CFLAGS=-O2 -g -Wall -Wextra -fpic
+
+CFLAGS_I=-include $(CONFIGH) \
+       -I$(SWANDIR)/src/libstrongswan \
+       -I$(SWANDIR)/src/libcharon
+
+LDFLAGS= -lvppinfra \
+       -lvlibmemoryclient \
+       -lvppapiclient \
+       -lsvm \
+       -lvlib
+
+VERSION_VPP=$(shell (dpkg -s vpp | grep Version) | grep -Po '(?<=Version: )\d\d.\d\d')
+
+# check if VPP is installed
+ifneq ($(shell test "$(shell ldconfig -p | grep vppinfra.so | awk 'NR==1{print $$1;}')" && echo "yes"), yes)
+# check if VPPDIR exists
+ifeq ($(shell test -d $(VPPDIR) && echo "yes"), yes)
+       CFLAGS_I += -I$(VPPDIR)
+endif
+# check if VPPLIBS exists
+ifeq ($(shell test -d $(VPPLIBS) && echo "yes"), yes)
+       LDFLAGS += -L$(VPPLIBS)
+       LDFLAGS += -Wl,-rpath=$(VPPLIBS)
+endif
+endif
+
+SOURCES=$(wildcard *.c)
+OBJECTS=$(SOURCES:.c=.o)
+
+all: pull-swan $(TARGET)
+
+pull-swan:
+       @if [ -d "${SWANDIR}" ]; then \
+         rm -rf ${SWANDIR} ; \
+       fi
+       @if ! [ -f "${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz" ]; then \
+         curl -o ${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz -LO https://github.com/strongswan/strongswan/archive/${VERSION_SSWAN}.tar.gz; \
+       fi
+       @if ! [ -d "${CURDIR}/../../../build-root/build-vpp-native/external/" ]; then \
+         mkdir ${CURDIR}/../../../build-root/build-vpp-native/external; \
+       fi
+       tar -zxof ${SWANARCHIVE}/strongswan-${VERSION_SSWAN}.tar.gz -C ${CURDIR}/../../../build-root/build-vpp-native/external/
+       mv ${CURDIR}/../../../build-root/build-vpp-native/external/strongswan-${VERSION_SSWAN} ${SWANDIR}
+       cd ${SWANDIR} && ./autogen.sh
+       cd ${SWANDIR} && ./configure --prefix=${PREFIX} --sysconfdir=${PREFIX_SYS_CONF} --enable-libipsec --enable-systemd --enable-swanctl --disable-gmp --enable-openssl
+       cd ${SWANDIR} && make -j$(nproc)
+#      cd ${SWANDIR} && sudo make install
+
+# check if VPP is installed
+ifneq ($(shell test "$(shell ldconfig -p | grep vppinfra.so | awk 'NR==1{print $$1;}')" && echo "yes"), yes)
+       $(info INFO: Not found installed VPP - checking if locally VPP exists)
+# check if VPPDIR exists
+ifneq ($(shell test -d $(VPPDIR) && echo "yes"), yes)
+       $(error ERROR: Not found installed VPP and locally VPP - please install or build)
+else
+# check if VPPLIBS exists
+ifneq ($(shell test -d $(VPPLIBS) && echo "yes"), yes)
+       $(error ERROR: directory $(VPPLIBS) - doesn't exists, please compile VPP before build this)
+else
+       $(info INFO: Found locally VPP)
+endif
+endif
+else
+       $(info INFO: Found installed VPP in version: $(VERSION_VPP))
+endif
+
+$(TARGET): $(OBJECTS)
+       gcc $(CFLAGS) -shared -DPIC $(OBJECTS) $(LDFLAGS) -Wl,-soname -Wl,$(TARGET) -o $@
+       cp $(TARGET) ${SWANDIR}
+
+%.o: %.c
+       gcc $(CFLAGS) $(CFLAGS_I) -c $< -o $@ $(LDFLAGS)
+
+install:
+       cp $(TARGET) $(SWANPLUGINS)/$(TARGET)
+       cp kernel-vpp.conf $(PLUGINCONF)
+
+clean:
+       rm -f *.so *.o
+
+.PHONY: clean install all
\ No newline at end of file
diff --git a/extras/strongswan/vpp_sswan/README.rst b/extras/strongswan/vpp_sswan/README.rst
new file mode 100644 (file)
index 0000000..4def838
--- /dev/null
@@ -0,0 +1,128 @@
+.. _vpp_sswan_doc:
+
+VPP-SSWAN
+=======================
+
+``VPP-SSWAN`` is a StrongSwan plugin that helps offloading Strongswan IPsec ESP
+process from Linux Kernel to ``VPP``.
+
+The kernel-vpp plugin is an interface to the IPsec and networking backend for
+`VPP <https://wiki.fd.io/view/VPP>`__ platform using the
+`VPP C API <https://wiki.fd.io/view/VPP/How_To_Use_The_C_API>`__.
+It provides address and routing lookup functionality and installs routes for
+IPsec traffic.
+It installs and maintains Security Associations and Policies to the
+`VPP IPsec <https://wiki.fd.io/view/VPP/IPSec_and_IKEv2#IPSec>`__.
+
+Prerequisites
+-------------
+
+``VPP`` in release mode should be built before compiling ``vpp-swan plugin``.
+The dependencies of ``StrongSwan`` should be installed before building
+``VPP-SSWAN``. In addition ``libsystemd-dev`` should be installed.
+
+Build VPP Strongswan Plugin
+-------------
+
+The following list of things will be done to build ``vpp-swan plugin``:
+
+- download strongswan source code to:
+``path/to/vpp/build/external/downloads``
+
+- unzip source code strongswan to:
+``path/to/vpp/build-root/build-vpp-native/external/sswan``
+
+- check if you have installed packages: ``libsystemd-dev`` on your OS
+
+- configure strongswan by:
+``./configure --prefix=/usr --sysconfdir=/etc --enable-libipsec
+--enable-systemd --enable-swanctl --disable-gmp --enable-openssl``
+
+- compile strongswan in:
+``path/to/vpp/build-root/build-vpp-native/external/sswan``
+
+- compile ``vpp-swan plugin`` by:
+
+::
+
+   ./make all
+
+- if everything it ok, copy the compiled ``vpp-swan plugin`` to:
+``/usr/lib/ipsec/plugins``
+
+Build/install Strongswan
+-------------
+
+It is recommended to use ``Strongswan`` in version ``5.9.6`` or ``5.9.5``
+installed from this script, due to configuration Strongswan that is required.
+Only version ``5.9.5`` and ``5.9.6`` was tested with this plugin.
+
+To install the built Strongswan, please execute the following command:
+
+::
+
+   path/to/vpp/build-root/build-vpp-native/external/sswan/sudo make install
+
+Insert plugin in runtime mode
+-------------
+
+After builded this plugin and also installed Strongswan you can loaded plugin
+into Strongswan directory by:
+
+::
+
+   ./make install
+
+Or you can do manually copy ``libstrongswan-kernel-vpp.so`` into:
+``/usr/lib/ipsec/plugins`` and also ``kernel-vpp.conf`` into: ``/etc/strongswan.d/charon/``
+
+And also you should restart Strongswan by:
+
+::
+
+   systemctl restart strongswan.service
+
+Configuration Strongswan
+-------------
+In ``swanctl.conf`` file you can find example configuration to initialize
+connections between two endpoints.
+
+Copy this file into: ``/etc/swanctl/conf.d/swanctl.conf``
+
+Configuration VPP
+-------------
+
+In your ``startup.conf`` add these following commands:
+
+::
+
+   plugins {
+     plugin linux_cp_plugin.so { enable }
+     plugin ikev2_plugin.so { disable }
+    }
+
+   linux-cp {
+      lcp-sync
+   }
+
+To enable ``CP Plugin`` and disable ``IKEv2`` plugin.
+
+These following commands executed in ``VPP``:
+
+::
+
+   lcp create eth2 host-if eth2
+   set interface state eth2 up
+   set interface ip address eth2 192.168.0.2/24
+   set int state eth1 up
+   set int ip addr eth1 192.168.200.1/24
+
+To create interface by ``CP Plugin`` and also setup two ethernet interfaces.
+
+Misc
+-------------
+This plugin is based on:
+`https://github.com/matfabia/strongswan
+<https://github.com/matfabia/strongswan>`__
+
+Author: Matus Fabian <matfabia@cisco.com>
\ No newline at end of file
diff --git a/extras/strongswan/vpp_sswan/kernel-vpp.conf b/extras/strongswan/vpp_sswan/kernel-vpp.conf
new file mode 100644 (file)
index 0000000..71f50b5
--- /dev/null
@@ -0,0 +1,7 @@
+kernel-vpp {
+
+    # Whether to load the plugin. Can also be an integer to increase the
+    # priority of this plugin.
+    load = yes
+
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c b/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.c
new file mode 100644 (file)
index 0000000..7849dd6
--- /dev/null
@@ -0,0 +1,1751 @@
+/*
+ * 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 <daemon.h>
+#include <utils/debug.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/vnet.h>
+#include <collections/hashtable.h>
+#include <threading/mutex.h>
+#include <processing/jobs/callback_job.h>
+#include <vpp-api/client/stat_client.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>
+
+#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
+#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
+#include <vnet/ipsec/ipsec.api_enum.h>
+#include <vnet/ipsec/ipsec.api_types.h>
+#include <vnet/interface.api_enum.h>
+#include <vnet/interface.api_types.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+#include "kernel_vpp_ipsec.h"
+#include "kernel_vpp_shared.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <net/if_arp.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#define PRIO_BASE 384
+
+u32 natt_port;
+
+/**
+ * One and only instance of the daemon.
+ */
+daemon_t *charon;
+
+typedef struct private_kernel_vpp_ipsec_t private_kernel_vpp_ipsec_t;
+
+/**
+ * Private variables of kernel_vpp_ipsec class.
+ */
+struct private_kernel_vpp_ipsec_t
+{
+
+  /**
+   * Public interface
+   */
+  kernel_vpp_ipsec_t public;
+
+  /**
+   * Next security association database entry ID to allocate
+   */
+  refcount_t next_sad_id;
+
+  /**
+   * Next security policy database entry ID to allocate
+   */
+  refcount_t next_spd_id;
+
+  /**
+   * Mutex to lock access to installed policies
+   */
+  mutex_t *mutex;
+
+  /**
+   * Hash table of instaled SA, as kernel_ipsec_sa_id_t => sa_t
+   */
+  hashtable_t *sas;
+
+  /**
+   * Hash table of security policy databases, as nterface => spd_t
+   */
+  hashtable_t *spds;
+
+  /**
+   * Linked list of installed routes
+   */
+  linked_list_t *routes;
+
+  /**
+   * Next SPI to allocate
+   */
+  refcount_t nextspi;
+
+  /**
+   * Mix value to distribute SPI allocation randomly
+   */
+  uint32_t mixspi;
+
+  /**
+   * Whether to install routes along policies
+   */
+  bool install_routes;
+};
+
+/**
+ * Security association entry
+ */
+typedef struct
+{
+  /** VPP SA ID */
+  uint32_t sa_id;
+  uint32_t stat_index;
+} sa_t;
+
+/**
+ * Security policy database
+ */
+typedef struct
+{
+  /** VPP SPD ID */
+  uint32_t spd_id;
+  /** Networking interface ID restricting policy */
+  uint32_t sw_if_index;
+  /** Policy count for this SPD */
+  refcount_t policy_num;
+} spd_t;
+
+/**
+ * Installed route
+ */
+typedef struct
+{
+  /** Name of the interface the route is bound to */
+  char *if_name;
+  /** Gateway of route */
+  host_t *gateway;
+  /** Destination network of route */
+  host_t *dst_net;
+  /** Prefix length of dst_net */
+  uint8_t prefixlen;
+  /** References for route */
+  refcount_t refs;
+} route_entry_t;
+
+#define htonll(x)                                                             \
+  ((1 == htonl (1)) ?                                                         \
+          (x) :                                                                    \
+          ((uint64_t) htonl ((x) &0xFFFFFFFF) << 32) | htonl ((x) >> 32))
+#define ntohll(x)                                                             \
+  ((1 == ntohl (1)) ?                                                         \
+          (x) :                                                                    \
+          ((uint64_t) ntohl ((x) &0xFFFFFFFF) << 32) | ntohl ((x) >> 32))
+
+CALLBACK (route_equals, bool, route_entry_t *a, va_list args)
+{
+  host_t *dst_net, *gateway;
+  uint8_t *prefixlen;
+  char *if_name;
+
+  VA_ARGS_VGET (args, if_name, gateway, dst_net, prefixlen);
+
+  return a->if_name && if_name && streq (a->if_name, if_name) &&
+        a->gateway->ip_equals (a->gateway, gateway) &&
+        a->dst_net->ip_equals (a->dst_net, dst_net) &&
+        a->prefixlen == *prefixlen;
+}
+
+/**
+ * Clean up a route entry
+ */
+static void
+route_destroy (route_entry_t *this)
+{
+  this->dst_net->destroy (this->dst_net);
+  this->gateway->destroy (this->gateway);
+  free (this->if_name);
+  free (this);
+}
+
+static uint32_t get_sw_if_index ();
+
+static int
+set_arp (char *ipStr, char *if_name, bool add)
+{
+  char *out = NULL;
+  int out_len = 0;
+  vl_api_ip_neighbor_add_del_t *mp;
+  vl_api_ip_neighbor_add_del_reply_t *rmp;
+  int rc = SUCCESS;
+  uint32_t sw_if_index = ~0;
+
+  FILE *fp;
+  int nread = 0;
+  ssize_t len = 0;
+  char *buffer = NULL;
+  char buf[2][20];
+  char *file = "/proc/net/arp";
+  unsigned char mac[8] = {
+    0,
+  };
+  uint32_t addr;
+
+  if (if_name == NULL || ipStr == NULL)
+    {
+      DBG2 (DBG_KNL, "para is null\n");
+      rc = FAILED;
+    }
+  DBG2 (DBG_KNL, "from kernel read mac\n");
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+  sw_if_index = get_sw_if_index (if_name);
+  if (sw_if_index == ~0)
+    {
+      DBG1 (DBG_KNL, "sw_if_index for %s not found", if_name);
+      goto error;
+    }
+
+  fp = fopen (file, "rb");
+  while ((nread = getline (&buffer, &len, fp)) != -1)
+    {
+      sscanf (buffer, "%s %*s %*s %s %*s %*s", &buf[0], &buf[1]);
+      inet_aton (&buf[0], &addr);
+
+      if (addr == *((u32 *) (ipStr)))
+       {
+         sscanf (buf[1], "%02x:%02x:%02x:%02x:%02x:%02x", &mac[0], &mac[1],
+                 &mac[2], &mac[3], &mac[4], &mac[5]);
+         u16 msg_id =
+           vl_msg_api_get_msg_index ((u8 *) "ip_neighbor_add_del_0607c257");
+         mp->_vl_msg_id = htons (msg_id);
+         mp->is_add = add;
+         memcpy (mp->neighbor.ip_address.un.ip4, (u8 *) &addr, sizeof (addr));
+         mp->neighbor.ip_address.af = 0;
+         memcpy (mp->neighbor.mac_address, mac, 6);
+         mp->neighbor.sw_if_index = htonl (sw_if_index);
+         mp->neighbor.flags = 1;
+
+         if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+           {
+             DBG1 (DBG_KNL, "vac %s neighbor entry",
+                   add ? "adding" : "removing");
+             goto error;
+           }
+         rmp = (void *) out;
+         if (rmp->retval)
+           {
+             DBG1 (DBG_KNL, "%s neighbor add rv:%d", add ? "add" : "remove",
+                   ntohl (rmp->retval));
+             goto error;
+           }
+         fclose (fp);
+         return rc;
+       }
+    }
+  return rc;
+
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  return rc;
+}
+
+static int
+add_Route (char *ipAddr, int len, char *mask, char *gateWay)
+{
+  int fd;
+  int rc = SUCCESS;
+  struct sockaddr_in _sin;
+  struct sockaddr_in *sin = &_sin;
+  struct rtentry rt;
+
+  do
+    {
+      fd = socket (AF_INET, SOCK_DGRAM, 0);
+      if (fd < 0)
+       {
+         DBG2 (DBG_KNL, "addRoute: socket error\n");
+         rc = FAILED;
+         break;
+       }
+      memset (&rt, 0, sizeof (struct rtentry));
+      memset (sin, 0, sizeof (struct sockaddr_in));
+      sin->sin_family = AF_INET;
+      sin->sin_port = 0;
+
+      if (inet_aton (gateWay, &sin->sin_addr) < 0)
+       {
+         rc = FAILED;
+         break;
+       }
+      memcpy (&rt.rt_gateway, sin, sizeof (struct sockaddr_in));
+
+      ((struct sockaddr_in *) &rt.rt_dst)->sin_family = AF_INET;
+      memcpy (&((struct sockaddr_in *) &rt.rt_dst)->sin_addr, ipAddr, len);
+
+      ((struct sockaddr_in *) &rt.rt_genmask)->sin_family = AF_INET;
+      if (inet_aton (mask,
+                    &((struct sockaddr_in *) &rt.rt_genmask)->sin_addr) < 0)
+       {
+         rc = FAILED;
+         break;
+       }
+      rt.rt_flags = RTF_GATEWAY;
+      if (ioctl (fd, SIOCADDRT, &rt) < 0)
+       {
+         rc = FAILED;
+       }
+    }
+  while (0);
+
+  close (fd);
+  return rc;
+}
+
+static int
+set_address (u32 ipAddr, u32 sw_if_index, bool add)
+{
+  char *out = NULL;
+  int out_len = 0;
+  vl_api_sw_interface_add_del_address_t *mp;
+  vl_api_sw_interface_add_del_address_reply_t *rmp;
+
+  int rc = SUCCESS;
+
+  uint32_t addr;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "sw_interface_add_del_address_5463d73b");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->is_add = add;
+  memcpy (mp->prefix.address.un.ip4, (u8 *) &ipAddr, sizeof (ipAddr));
+  mp->prefix.len = 24;
+  mp->sw_if_index = sw_if_index;
+
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG2 (DBG_KNL, "vac %s neighbor entry", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG2 (DBG_KNL, "%s neighbor add rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  return rc;
+
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  return rc;
+}
+
+/**
+ * (Un)-install a single route
+ */
+static void
+manage_route (private_kernel_vpp_ipsec_t *this, bool add,
+             traffic_selector_t *dst_ts, host_t *src, host_t *dst)
+{
+  host_t *dst_net, *gateway;
+  uint8_t prefixlen;
+  char *if_name;
+  route_entry_t *route;
+  bool route_exist = FALSE;
+
+  char *netmask = "255.255.255.0";
+  char *tap_gateway = "1.1.1.1";
+  int arp_rc = 0;
+  if (dst->is_anyaddr (dst))
+    {
+      return;
+    }
+  gateway =
+    charon->kernel->get_nexthop (charon->kernel, dst, -1, NULL, &if_name);
+  dst_ts->to_subnet (dst_ts, &dst_net, &prefixlen);
+  if (!if_name)
+    {
+      if (src->is_anyaddr (src))
+       {
+         return;
+       }
+      if (!charon->kernel->get_interface (charon->kernel, src, &if_name))
+       {
+         return;
+       }
+    }
+  route_exist =
+    this->routes->find_first (this->routes, route_equals, (void **) &route,
+                             if_name, gateway, dst_net, &prefixlen);
+  if (add)
+    {
+      DBG2 (DBG_KNL, "installing route: %H/%d via %H dev %s", dst_net,
+           prefixlen, gateway, if_name);
+      if (route_exist)
+       {
+         unsigned int refs_num = ref_get (&route->refs);
+         DBG2 (DBG_KNL, "add route but it exist %d", refs_num);
+       }
+      else
+       {
+         INIT (route, .if_name = strdup (if_name),
+               .gateway = gateway->clone (gateway),
+               .dst_net = dst_net->clone (dst_net), .prefixlen = prefixlen,
+               .refs = 1, );
+         this->routes->insert_last (this->routes, route);
+         charon->kernel->add_route (charon->kernel,
+                                    dst_net->get_address (dst_net), prefixlen,
+                                    gateway, dst, if_name, 1);
+       }
+
+      add_Route (dst_net->get_address (dst_net).ptr,
+                dst_net->get_address (dst_net).len, netmask, tap_gateway);
+
+      arp_rc = set_arp (gateway->get_address (gateway).ptr, if_name, TRUE);
+      if (arp_rc)
+       DBG2 (DBG_KNL, "arpGet success!\n");
+    }
+  else
+    {
+      DBG2 (DBG_KNL, "uninstalling route: %H/%d via %H dev %s", dst_net,
+           prefixlen, gateway, if_name);
+      if (!route_exist)
+       {
+         DBG2 (DBG_KNL, "del route but it not exist");
+         return;
+       }
+      if (ref_put (&route->refs))
+       {
+         this->routes->remove (this->routes, route, NULL);
+         route_destroy (route);
+         charon->kernel->del_route (charon->kernel,
+                                    dst_net->get_address (dst_net), prefixlen,
+                                    gateway, dst, if_name, 1);
+       }
+    }
+}
+
+/**
+ * Hash function for IPsec SA
+ */
+static u_int
+sa_hash (kernel_ipsec_sa_id_t *sa)
+{
+  return chunk_hash_inc (
+    sa->src->get_address (sa->src),
+    chunk_hash_inc (
+      sa->dst->get_address (sa->dst),
+      chunk_hash_inc (chunk_from_thing (sa->spi),
+                     chunk_hash (chunk_from_thing (sa->proto)))));
+}
+
+/**
+ * Equality function for IPsec SA
+ */
+static bool
+sa_equals (kernel_ipsec_sa_id_t *sa, kernel_ipsec_sa_id_t *other_sa)
+{
+  return sa->src->ip_equals (sa->src, other_sa->src) &&
+        sa->dst->ip_equals (sa->dst, other_sa->dst) &&
+        sa->spi == other_sa->spi && sa->proto == other_sa->proto;
+}
+
+/**
+ * Hash function for interface
+ */
+static u_int
+interface_hash (char *interface)
+{
+  return chunk_hash (chunk_from_str (interface));
+}
+
+/**
+ * Equality function for interface
+ */
+static bool
+interface_equals (char *interface1, char *interface2)
+{
+  return streq (interface1, interface2);
+}
+
+/**
+ * Map an integer x with a one-to-one function using quadratic residues
+ */
+static u_int
+permute (u_int x, u_int p)
+{
+  u_int qr;
+
+  x = x % p;
+  qr = ((uint64_t) x * x) % p;
+  if (x <= p / 2)
+    {
+      return qr;
+    }
+  return p - qr;
+}
+
+/**
+ * Initialize seeds for SPI generation
+ */
+static bool
+init_spi (private_kernel_vpp_ipsec_t *this)
+{
+  bool ok = TRUE;
+  rng_t *rng;
+
+  rng = lib->crypto->create_rng (lib->crypto, RNG_STRONG);
+  if (!rng)
+    {
+      return FALSE;
+    }
+  ok =
+    rng->get_bytes (rng, sizeof (this->nextspi), (uint8_t *) &this->nextspi);
+  if (ok)
+    {
+      ok =
+       rng->get_bytes (rng, sizeof (this->mixspi), (uint8_t *) &this->mixspi);
+    }
+  rng->destroy (rng);
+  return ok;
+}
+
+/**
+ * Calculate policy priority
+ */
+static uint32_t
+calculate_priority (policy_priority_t policy_priority, traffic_selector_t *src,
+                   traffic_selector_t *dst)
+{
+  uint32_t priority = PRIO_BASE;
+  uint16_t port;
+  uint8_t mask, proto;
+  host_t *net;
+
+  switch (policy_priority)
+    {
+    case POLICY_PRIORITY_FALLBACK:
+      priority <<= 1;
+      /* fall-through */
+    case POLICY_PRIORITY_ROUTED:
+      priority <<= 1;
+      /* fall-through */
+    case POLICY_PRIORITY_DEFAULT:
+      priority <<= 1;
+      /* fall-through */
+    case POLICY_PRIORITY_PASS:
+      break;
+    }
+  /* calculate priority based on selector size, small size = high prio */
+  src->to_subnet (src, &net, &mask);
+  priority -= mask;
+  proto = src->get_protocol (src);
+  port = net->get_port (net);
+  net->destroy (net);
+
+  dst->to_subnet (dst, &net, &mask);
+  priority -= mask;
+  proto = max (proto, dst->get_protocol (dst));
+  port = max (port, net->get_port (net));
+  net->destroy (net);
+
+  priority <<= 2; /* make some room for the two flags */
+  priority += port ? 0 : 2;
+  priority += proto ? 0 : 1;
+  return priority;
+}
+
+/**
+ * Get sw_if_index from interface name
+ */
+static uint32_t
+get_sw_if_index (char *interface)
+{
+  char *out = NULL;
+  int out_len, name_filter_len = 0, msg_len = 0;
+  vl_api_sw_interface_dump_t *mp;
+  vl_api_sw_interface_details_t *rmp;
+  uint32_t sw_if_index = ~0;
+
+  name_filter_len = strlen (interface);
+  msg_len = sizeof (*mp) + name_filter_len;
+  mp = vl_msg_api_alloc (msg_len);
+  clib_memset (mp, 0, msg_len);
+  u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "sw_interface_dump_aa610c27");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->name_filter_valid = TRUE;
+  mp->name_filter.length = htonl (name_filter_len);
+  memcpy ((char *) mp->name_filter.buf, interface, name_filter_len);
+
+  if (vac->send (vac, (char *) mp, msg_len, &out, &out_len))
+    {
+      goto error;
+    }
+  if (!out_len)
+    {
+      goto error;
+    }
+  rmp = (vl_api_sw_interface_details_t *) out;
+  sw_if_index = ntohl (rmp->sw_if_index);
+
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  return sw_if_index;
+}
+/**
+ * (Un)-install a security policy database
+ */
+static status_t
+spd_add_del (bool add, uint32_t spd_id)
+{
+  char *out = NULL;
+  int out_len;
+  vl_api_ipsec_spd_add_del_t *mp;
+  vl_api_ipsec_spd_add_del_reply_t *rmp;
+  status_t rv = FAILED;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+
+  u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_add_del_20e89a95");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->is_add = add;
+  mp->spd_id = htonl (spd_id);
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  rv = SUCCESS;
+
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  return rv;
+}
+
+/**
+ * Enable or disable SPD on an insterface
+ */
+static status_t
+interface_add_del_spd (bool add, uint32_t spd_id, uint32_t sw_if_index)
+{
+  char *out = NULL;
+  int out_len;
+  vl_api_ipsec_interface_add_del_spd_t *mp;
+  vl_api_ipsec_interface_add_del_spd_reply_t *rmp;
+  status_t rv = FAILED;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "ipsec_interface_add_del_spd_80f80cbb");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->is_add = add;
+  mp->spd_id = htonl (spd_id);
+  mp->sw_if_index = htonl (sw_if_index);
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s interface SPD failed",
+           add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s interface SPD failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  rv = SUCCESS;
+
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  return rv;
+}
+
+static int
+bypass_all (bool add, uint32_t spd_id, uint32_t sa_id)
+{
+  vl_api_ipsec_spd_entry_add_del_t *mp;
+  vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
+  char *out = NULL;
+  int out_len;
+  status_t rv = FAILED;
+
+  DBG2 (DBG_KNL, "bypass_all [%s] spd_id %d sa_id %d", add ? "ADD" : "DEL",
+       spd_id, sa_id);
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_entry_add_del_338b7411");
+  mp->_vl_msg_id = ntohs (msg_id);
+  mp->is_add = add;
+  mp->entry.sa_id = ntohl (sa_id);
+  mp->entry.spd_id = ntohl (spd_id);
+  mp->entry.priority = ntohl (INT_MAX - POLICY_PRIORITY_PASS - 1);
+  mp->entry.is_outbound = 0;
+  mp->entry.policy = ntohl (IPSEC_API_SPD_ACTION_BYPASS);
+  memset (mp->entry.local_address_stop.un.ip6, 0xFF, 16);
+  memset (mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
+  mp->entry.remote_port_start = mp->entry.local_port_start = ntohs (0);
+  mp->entry.remote_port_stop = mp->entry.local_port_stop = ntohs (0xFFFF);
+  mp->entry.protocol = IP_API_PROTO_ESP;
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  mp->entry.is_outbound = 1;
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  mp->entry.is_outbound = 0;
+  mp->entry.protocol = IP_API_PROTO_AH;
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  mp->entry.is_outbound = 1;
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+
+  rv = SUCCESS;
+
+error:
+  if (out)
+    free (out);
+  vl_msg_api_free (mp);
+
+  return rv;
+}
+
+static int
+bypass_port (bool add, uint32_t spd_id, uint32_t sa_id, uint16_t port)
+{
+  vl_api_ipsec_spd_entry_add_del_t *mp;
+  vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
+  char *out = NULL;
+  int out_len;
+  status_t rv = FAILED;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_entry_add_del_338b7411");
+  mp->_vl_msg_id = ntohs (msg_id);
+  mp->is_add = add;
+  mp->entry.sa_id = ntohl (sa_id);
+  mp->entry.spd_id = ntohl (spd_id);
+  mp->entry.priority = ntohl (INT_MAX - POLICY_PRIORITY_PASS - 1);
+  mp->entry.policy = ntohl (IPSEC_API_SPD_ACTION_BYPASS);
+  memset (mp->entry.local_address_stop.un.ip6, 0xFF, 16);
+  memset (mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
+  mp->entry.is_outbound = 0;
+  mp->entry.remote_port_start = mp->entry.local_port_start = ntohs (0);
+  mp->entry.remote_port_stop = mp->entry.local_port_stop = ntohs (0xFFFF);
+  mp->entry.protocol = IP_API_PROTO_HOPOPT;
+
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  mp->entry.is_outbound = 1;
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+  rv = SUCCESS;
+
+error:
+  if (out)
+    free (out);
+  vl_msg_api_free (mp);
+
+  return rv;
+}
+
+/**
+ * Add or remove a bypass policy
+ */
+static status_t
+manage_bypass (bool add, uint32_t spd_id, uint32_t sa_id)
+{
+  uint16_t port;
+  status_t rv;
+
+  bypass_all (add, spd_id, sa_id);
+
+  port =
+    lib->settings->get_int (lib->settings, "%s.port", IKEV2_UDP_PORT, lib->ns);
+
+  if (port)
+    {
+      rv = bypass_port (add, spd_id, sa_id, port);
+      if (rv != SUCCESS)
+       {
+         return rv;
+       }
+    }
+
+  port = lib->settings->get_int (lib->settings, "%s.port_nat_t",
+                                IKEV2_NATT_PORT, lib->ns);
+  if (port)
+    {
+      rv = bypass_port (add, spd_id, sa_id, port);
+      if (rv != SUCCESS)
+       {
+         return rv;
+       }
+    }
+
+  return SUCCESS;
+}
+
+/**
+ * Add or remove a policy
+ */
+static status_t
+manage_policy (private_kernel_vpp_ipsec_t *this, bool add,
+              kernel_ipsec_policy_id_t *id,
+              kernel_ipsec_manage_policy_t *data)
+{
+  spd_t *spd;
+  char *out = NULL, *interface = NULL;
+  int out_len;
+  uint32_t sw_if_index, spd_id = ~0, sad_id = ~0;
+  status_t rv = FAILED;
+  uint32_t priority, auto_priority;
+  chunk_t src_from, src_to, dst_from, dst_to;
+  host_t *src, *dst, *addr;
+  vl_api_ipsec_spd_entry_add_del_t *mp;
+  vl_api_ipsec_spd_entry_add_del_reply_t *rmp;
+  bool n_spd = false;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+
+  this->mutex->lock (this->mutex);
+  if (!id->interface)
+    {
+      addr = id->dir == POLICY_IN ? data->dst : data->src;
+      for (int i = 0; i < 5; i++)
+       {
+         if (!charon->kernel->get_interface (charon->kernel, addr,
+                                             &interface))
+           {
+             DBG1 (DBG_KNL, "policy no interface %H", addr);
+             interface = NULL;
+             sleep (1);
+           }
+
+         if (interface)
+           {
+             DBG1 (DBG_KNL, "policy have interface %H", addr);
+             break;
+           }
+       }
+      if (!interface)
+       goto error;
+      id->interface = interface;
+    }
+
+  DBG2 (DBG_KNL, "manage policy [%s] interface [%s]", add ? "ADD" : "DEL",
+       id->interface);
+
+  spd = this->spds->get (this->spds, id->interface);
+  if (!spd)
+    {
+      if (!add)
+       {
+         DBG1 (DBG_KNL, "SPD for %s not found, should not be deleted",
+               id->interface);
+         goto error;
+       }
+      sw_if_index = get_sw_if_index (id->interface);
+      DBG1 (DBG_KNL, "firstly created, spd for %s found sw_if_index is %d",
+           id->interface, sw_if_index);
+      if (sw_if_index == ~0)
+       {
+         DBG1 (DBG_KNL, "sw_if_index for %s not found", id->interface);
+         goto error;
+       }
+      spd_id = ref_get (&this->next_spd_id);
+      if (spd_add_del (TRUE, spd_id))
+       {
+         DBG1 (DBG_KNL, "spd_add_del %d failed!!!!!", spd_id);
+         goto error;
+       }
+      if (interface_add_del_spd (TRUE, spd_id, sw_if_index))
+       {
+         DBG1 (DBG_KNL, "interface_add_del_spd  %d %d failed!!!!!", spd_id,
+               sw_if_index);
+         goto error;
+       }
+      INIT (spd, .spd_id = spd_id, .sw_if_index = sw_if_index,
+           .policy_num = 0, );
+      this->spds->put (this->spds, id->interface, spd);
+      n_spd = true;
+    }
+
+  auto_priority = calculate_priority (data->prio, id->src_ts, id->dst_ts);
+  priority = data->manual_prio ? data->manual_prio : auto_priority;
+
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "ipsec_spd_entry_add_del_338b7411");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->is_add = add;
+  mp->entry.spd_id = htonl (spd->spd_id);
+  mp->entry.priority = htonl (INT_MAX - POLICY_PRIORITY_PASS);
+  mp->entry.is_outbound = id->dir == POLICY_OUT;
+  switch (data->type)
+    {
+    case POLICY_IPSEC:
+      mp->entry.policy = htonl (IPSEC_API_SPD_ACTION_PROTECT);
+      break;
+    case POLICY_PASS:
+      mp->entry.policy = htonl (IPSEC_API_SPD_ACTION_BYPASS);
+      break;
+    case POLICY_DROP:
+      mp->entry.policy = htonl (IPSEC_API_SPD_ACTION_DISCARD);
+      break;
+    }
+  if ((data->type == POLICY_IPSEC) && data->sa)
+    {
+      kernel_ipsec_sa_id_t id = {
+       .src = data->src,
+       .dst = data->dst,
+       .proto = data->sa->esp.use ? IPPROTO_ESP : IPPROTO_AH,
+       .spi = data->sa->esp.use ? data->sa->esp.spi : data->sa->ah.spi,
+      };
+      sa_t *sa = NULL;
+      sa = this->sas->get (this->sas, &id);
+      if (!sa)
+       {
+         DBG1 (DBG_KNL, "SA ID not found");
+         goto error;
+       }
+      sad_id = sa->sa_id;
+      if (n_spd)
+       {
+         if (manage_bypass (TRUE, spd_id, ~0))
+           {
+             DBG1 (DBG_KNL, "manage_bypass %d failed!!!!", spd_id);
+             goto error;
+           }
+       }
+    }
+
+  mp->entry.sa_id = htonl (sad_id);
+
+  bool is_ipv6 = false;
+  if (id->src_ts->get_type (id->src_ts) == TS_IPV6_ADDR_RANGE)
+    {
+      is_ipv6 = true;
+      mp->entry.local_address_start.af = htonl (ADDRESS_IP6);
+      mp->entry.local_address_stop.af = htonl (ADDRESS_IP6);
+      mp->entry.remote_address_start.af = htonl (ADDRESS_IP6);
+      mp->entry.remote_address_stop.af = htonl (ADDRESS_IP6);
+    }
+  else
+    {
+      mp->entry.local_address_start.af = htonl (ADDRESS_IP4);
+      mp->entry.local_address_stop.af = htonl (ADDRESS_IP4);
+      mp->entry.remote_address_start.af = htonl (ADDRESS_IP4);
+      mp->entry.remote_address_stop.af = htonl (ADDRESS_IP4);
+    }
+  mp->entry.protocol = id->src_ts->get_protocol (id->src_ts);
+
+  if (id->dir == POLICY_OUT)
+    {
+      src_from = id->src_ts->get_from_address (id->src_ts);
+      src_to = id->src_ts->get_to_address (id->src_ts);
+      src = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, src_to, 0);
+      dst_from = id->dst_ts->get_from_address (id->dst_ts);
+      dst_to = id->dst_ts->get_to_address (id->dst_ts);
+      dst = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, dst_to, 0);
+    }
+  else
+    {
+      dst_from = id->src_ts->get_from_address (id->src_ts);
+      dst_to = id->src_ts->get_to_address (id->src_ts);
+      dst = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, dst_from, 0);
+      src_from = id->dst_ts->get_from_address (id->dst_ts);
+      src_to = id->dst_ts->get_to_address (id->dst_ts);
+      src = host_create_from_chunk (is_ipv6 ? AF_INET6 : AF_INET, src_from, 0);
+    }
+
+  if (src->is_anyaddr (src) && dst->is_anyaddr (dst))
+    {
+      memset (mp->entry.local_address_stop.un.ip6, 0xFF, 16);
+      memset (mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
+    }
+  else
+    {
+      memcpy (is_ipv6 ? mp->entry.local_address_start.un.ip6 :
+                             mp->entry.local_address_start.un.ip4,
+             src_from.ptr, src_from.len);
+      memcpy (is_ipv6 ? mp->entry.local_address_stop.un.ip6 :
+                             mp->entry.local_address_stop.un.ip4,
+             src_to.ptr, src_to.len);
+      memcpy (is_ipv6 ? mp->entry.remote_address_start.un.ip6 :
+                             mp->entry.remote_address_start.un.ip4,
+             dst_from.ptr, dst_from.len);
+      memcpy (is_ipv6 ? mp->entry.remote_address_stop.un.ip6 :
+                             mp->entry.remote_address_stop.un.ip4,
+             dst_to.ptr, dst_to.len);
+    }
+  mp->entry.local_port_start = htons (id->src_ts->get_from_port (id->src_ts));
+  mp->entry.local_port_stop = htons (id->src_ts->get_to_port (id->src_ts));
+  mp->entry.remote_port_start = htons (id->dst_ts->get_from_port (id->dst_ts));
+  mp->entry.remote_port_stop = htons (id->dst_ts->get_to_port (id->dst_ts));
+
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %s SPD entry failed", add ? "adding" : "removing");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s SPD entry failed rv:%d", add ? "add" : "remove",
+           ntohl (rmp->retval));
+      goto error;
+    }
+
+  if (add)
+    {
+      ref_get (&spd->policy_num);
+    }
+  else
+    {
+      if (ref_put (&spd->policy_num))
+       {
+         DBG1 (
+           DBG_KNL,
+           "policy_num's ref is 0, delete spd_id %d sw_if_index %d sad_id %x",
+           spd->spd_id, spd->sw_if_index, sad_id);
+         interface_add_del_spd (FALSE, spd->spd_id, spd->sw_if_index);
+         manage_bypass (FALSE, spd->spd_id, sad_id);
+         spd_add_del (FALSE, spd->spd_id);
+         this->spds->remove (this->spds, id->interface);
+       }
+    }
+
+  if (this->install_routes && id->dir == POLICY_OUT && !mp->entry.protocol)
+    {
+      if (data->type == POLICY_IPSEC && data->sa->mode != MODE_TRANSPORT)
+       {
+         manage_route (this, add, id->dst_ts, data->src, data->dst);
+       }
+    }
+  rv = SUCCESS;
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  this->mutex->unlock (this->mutex);
+  return rv;
+}
+
+METHOD (kernel_ipsec_t, get_features, kernel_feature_t,
+       private_kernel_vpp_ipsec_t *this)
+{
+  return KERNEL_ESP_V3_TFC;
+}
+
+METHOD (kernel_ipsec_t, get_spi, status_t, private_kernel_vpp_ipsec_t *this,
+       host_t *src, host_t *dst, uint8_t protocol, uint32_t *spi)
+{
+  static const u_int p = 268435399, offset = 0xc0000000;
+
+  *spi = htonl (offset + permute (ref_get (&this->nextspi) ^ this->mixspi, p));
+  return SUCCESS;
+}
+
+METHOD (kernel_ipsec_t, get_cpi, status_t, private_kernel_vpp_ipsec_t *this,
+       host_t *src, host_t *dst, uint16_t *cpi)
+{
+  DBG1 (DBG_KNL, "get_cpi is not supported!!!!!!!!!!!!!!!!!!!!!!!!");
+  return NOT_SUPPORTED;
+}
+
+/**
+ * Helper struct for expiration events
+ */
+typedef struct
+{
+
+  private_kernel_vpp_ipsec_t *manager;
+
+  kernel_ipsec_sa_id_t *sa_id;
+
+  /**
+   * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
+   */
+  uint32_t hard_offset;
+
+} vpp_sa_expired_t;
+
+/**
+ * Callback for expiration events
+ */
+static job_requeue_t
+sa_expired (vpp_sa_expired_t *expired)
+{
+  private_kernel_vpp_ipsec_t *this = expired->manager;
+  sa_t *sa;
+  kernel_ipsec_sa_id_t *id = expired->sa_id;
+
+  this->mutex->lock (this->mutex);
+  sa = this->sas->get (this->sas, id);
+
+  if (sa)
+    {
+      charon->kernel->expire (charon->kernel, id->proto, id->spi, id->dst,
+                             FALSE);
+    }
+
+  free (id);
+  this->mutex->unlock (this->mutex);
+  return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Schedule a job to handle IPsec SA expiration
+ */
+static void
+schedule_expiration (private_kernel_vpp_ipsec_t *this,
+                    kernel_ipsec_add_sa_t *entry,
+                    kernel_ipsec_sa_id_t *entry2)
+{
+  lifetime_cfg_t *lifetime = entry->lifetime;
+  vpp_sa_expired_t *expired;
+  callback_job_t *job;
+  uint32_t timeout;
+  kernel_ipsec_sa_id_t *id;
+
+  if (!lifetime->time.life)
+    { /* no expiration at all */
+      return;
+    }
+
+  INIT (id, .src = entry2->src->clone (entry2->src),
+       .dst = entry2->dst->clone (entry2->dst), .spi = entry2->spi,
+       .proto = entry2->proto, );
+
+  INIT (expired, .manager = this, .sa_id = id, );
+
+  /* schedule a rekey first, a hard timeout will be scheduled then, if any */
+  expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
+  timeout = lifetime->time.rekey;
+
+  if (lifetime->time.life <= lifetime->time.rekey || lifetime->time.rekey == 0)
+    { /* no rekey, schedule hard timeout */
+      expired->hard_offset = 0;
+      timeout = lifetime->time.life;
+    }
+
+  job = callback_job_create ((callback_job_cb_t) sa_expired, expired,
+                            (callback_job_cleanup_t) free, NULL);
+  lib->scheduler->schedule_job (lib->scheduler, (job_t *) job, timeout);
+}
+
+METHOD (kernel_ipsec_t, add_sa, status_t, private_kernel_vpp_ipsec_t *this,
+       kernel_ipsec_sa_id_t *id, kernel_ipsec_add_sa_t *data)
+{
+  char *out = NULL;
+  int out_len;
+  vl_api_ipsec_sad_entry_add_del_t *mp;
+  vl_api_ipsec_sad_entry_add_del_reply_t *rmp;
+  uint32_t sad_id = ref_get (&this->next_sad_id);
+  uint8_t ca = 0, ia = 0;
+  status_t rv = FAILED;
+  chunk_t src, dst;
+  kernel_ipsec_sa_id_t *sa_id;
+  sa_t *sa;
+  int key_len = data->enc_key.len;
+
+  if ((data->enc_alg == ENCR_AES_CTR) ||
+      (data->enc_alg == ENCR_AES_GCM_ICV8) ||
+      (data->enc_alg == ENCR_AES_GCM_ICV12) ||
+      (data->enc_alg == ENCR_AES_GCM_ICV16))
+    {
+      static const int SALT_SIZE =
+       4; /* See how enc_size is calculated at keymat_v2.derive_child_keys */
+      key_len = key_len - SALT_SIZE;
+    }
+  natt_port = lib->settings->get_int (
+    lib->settings, "%s.plugins.socket-default.natt", IKEV2_NATT_PORT, lib->ns);
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "ipsec_sad_entry_add_del_ab64b5c6");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->is_add = 1;
+  mp->entry.sad_id = htonl (sad_id);
+  mp->entry.spi = id->spi;
+  mp->entry.protocol = id->proto == IPPROTO_ESP ? htonl (IPSEC_API_PROTO_ESP) :
+                                                       htonl (IPSEC_API_PROTO_AH);
+
+  switch (data->enc_alg)
+    {
+    case ENCR_NULL:
+      ca = IPSEC_API_CRYPTO_ALG_NONE;
+      break;
+    case ENCR_AES_CBC:
+      switch (key_len * 8)
+       {
+       case 128:
+         ca = IPSEC_API_CRYPTO_ALG_AES_CBC_128;
+         break;
+       case 192:
+         ca = IPSEC_API_CRYPTO_ALG_AES_CBC_192;
+         break;
+       case 256:
+         ca = IPSEC_API_CRYPTO_ALG_AES_CBC_256;
+         break;
+       default:
+         DBG1 (DBG_KNL, "Key length %d is not supported by VPP!",
+               key_len * 8);
+         goto error;
+       }
+      break;
+    case ENCR_AES_CTR:
+      switch (key_len * 8)
+       {
+       case 128:
+         ca = IPSEC_API_CRYPTO_ALG_AES_CTR_128;
+         break;
+       case 192:
+         ca = IPSEC_API_CRYPTO_ALG_AES_CTR_192;
+         break;
+       case 256:
+         ca = IPSEC_API_CRYPTO_ALG_AES_CTR_256;
+         break;
+       default:
+         DBG1 (DBG_KNL, "Key length %d is not supported by VPP!",
+               key_len * 8);
+         goto error;
+       }
+      break;
+    case ENCR_AES_GCM_ICV8:
+    case ENCR_AES_GCM_ICV12:
+    case ENCR_AES_GCM_ICV16:
+      switch (key_len * 8)
+       {
+       case 128:
+         ca = IPSEC_API_CRYPTO_ALG_AES_GCM_128;
+         break;
+       case 192:
+         ca = IPSEC_API_CRYPTO_ALG_AES_GCM_192;
+         break;
+       case 256:
+         ca = IPSEC_API_CRYPTO_ALG_AES_GCM_256;
+         break;
+       default:
+         DBG1 (DBG_KNL, "Key length %d is not supported by VPP!",
+               key_len * 8);
+         goto error;
+       }
+      break;
+    case ENCR_DES:
+      ca = IPSEC_API_CRYPTO_ALG_DES_CBC;
+      break;
+    case ENCR_3DES:
+      ca = IPSEC_API_CRYPTO_ALG_3DES_CBC;
+      break;
+    default:
+      DBG1 (DBG_KNL, "algorithm %N not supported by VPP!",
+           encryption_algorithm_names, data->enc_alg);
+      goto error;
+    }
+  mp->entry.crypto_algorithm = htonl (ca);
+  mp->entry.crypto_key.length = key_len < 128 ? key_len : 128;
+  memcpy (mp->entry.crypto_key.data, data->enc_key.ptr,
+         mp->entry.crypto_key.length);
+
+  /* copy salt for AEAD algorithms */
+  if ((data->enc_alg == ENCR_AES_CTR) ||
+      (data->enc_alg == ENCR_AES_GCM_ICV8) ||
+      (data->enc_alg == ENCR_AES_GCM_ICV12) ||
+      (data->enc_alg == ENCR_AES_GCM_ICV16))
+    {
+      memcpy (&mp->entry.salt, data->enc_key.ptr + mp->entry.crypto_key.length,
+             4);
+    }
+
+  switch (data->int_alg)
+    {
+    case AUTH_UNDEFINED:
+      ia = IPSEC_API_INTEG_ALG_NONE;
+      break;
+    case AUTH_HMAC_MD5_96:
+      ia = IPSEC_API_INTEG_ALG_MD5_96;
+      break;
+    case AUTH_HMAC_SHA1_96:
+      ia = IPSEC_API_INTEG_ALG_SHA1_96;
+      break;
+    case AUTH_HMAC_SHA2_256_96:
+      ia = IPSEC_API_INTEG_ALG_SHA_256_96;
+      break;
+    case AUTH_HMAC_SHA2_256_128:
+      ia = IPSEC_API_INTEG_ALG_SHA_256_128;
+      break;
+    case AUTH_HMAC_SHA2_384_192:
+      ia = IPSEC_API_INTEG_ALG_SHA_384_192;
+      break;
+    case AUTH_HMAC_SHA2_512_256:
+      ia = IPSEC_API_INTEG_ALG_SHA_512_256;
+      break;
+    default:
+      DBG1 (DBG_KNL, "algorithm %N not supported by VPP!",
+           integrity_algorithm_names, data->int_alg);
+      goto error;
+      break;
+    }
+  mp->entry.integrity_algorithm = htonl (ia);
+  mp->entry.integrity_key.length =
+    data->int_key.len < 128 ? data->int_key.len : 128;
+  memcpy (mp->entry.integrity_key.data, data->int_key.ptr,
+         mp->entry.integrity_key.length);
+
+  int flags = IPSEC_API_SAD_FLAG_NONE;
+  if (data->esn)
+    flags |= IPSEC_API_SAD_FLAG_USE_ESN;
+  if (data->mode == MODE_TUNNEL)
+    {
+      if (id->src->get_family (id->src) == AF_INET6)
+       flags |= IPSEC_API_SAD_FLAG_IS_TUNNEL_V6;
+      else
+       flags |= IPSEC_API_SAD_FLAG_IS_TUNNEL;
+    }
+  if (data->encap)
+    {
+      DBG1 (DBG_KNL, "UDP encap!!!!!!!!!!!!!!!!!!!!");
+      flags |= IPSEC_API_SAD_FLAG_UDP_ENCAP;
+    }
+  mp->entry.flags = htonl (flags);
+
+  bool is_ipv6 = false;
+  if (id->src->get_family (id->src) == AF_INET6)
+    {
+      is_ipv6 = true;
+      mp->entry.tunnel_src.af = htonl (ADDRESS_IP6);
+      mp->entry.tunnel_dst.af = htonl (ADDRESS_IP6);
+    }
+  else
+    {
+      mp->entry.tunnel_src.af = htonl (ADDRESS_IP4);
+      mp->entry.tunnel_dst.af = htonl (ADDRESS_IP4);
+    }
+  src = id->src->get_address (id->src);
+  memcpy (is_ipv6 ? mp->entry.tunnel_src.un.ip6 : mp->entry.tunnel_src.un.ip4,
+         src.ptr, src.len);
+  dst = id->dst->get_address (id->dst);
+  memcpy (is_ipv6 ? mp->entry.tunnel_dst.un.ip6 : mp->entry.tunnel_dst.un.ip4,
+         dst.ptr, dst.len);
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac adding SA failed");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "add SA failed rv:%d", ntohl (rmp->retval));
+      goto error;
+    }
+
+  this->mutex->lock (this->mutex);
+  INIT (sa_id, .src = id->src->clone (id->src),
+       .dst = id->dst->clone (id->dst), .spi = id->spi, .proto = id->proto, );
+  INIT (sa, .sa_id = sad_id, .stat_index = ntohl (rmp->stat_index), );
+  DBG4 (DBG_KNL, "put sa by its sa_id %x !!!!!!", sad_id);
+  this->sas->put (this->sas, sa_id, sa);
+  schedule_expiration (this, data, id);
+  this->mutex->unlock (this->mutex);
+  rv = SUCCESS;
+
+error:
+  free (out);
+  vl_msg_api_free (mp);
+  return rv;
+}
+
+METHOD (kernel_ipsec_t, update_sa, status_t, private_kernel_vpp_ipsec_t *this,
+       kernel_ipsec_sa_id_t *id, kernel_ipsec_update_sa_t *data)
+{
+  DBG1 (DBG_KNL,
+       "update sa not supported!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+  return NOT_SUPPORTED;
+}
+
+METHOD (kernel_ipsec_t, query_sa, status_t, private_kernel_vpp_ipsec_t *this,
+       kernel_ipsec_sa_id_t *id, kernel_ipsec_query_sa_t *data,
+       uint64_t *bytes, uint64_t *packets, time_t *time)
+{
+  status_t rv = FAILED;
+  sa_t *sa;
+  u32 *dir;
+  int i, k;
+  stat_segment_data_t *res;
+  u8 **pattern = 0;
+  uint64_t res_bytes = 0;
+  uint64_t res_packets = 0;
+
+  this->mutex->lock (this->mutex);
+  sa = this->sas->get (this->sas, id);
+  this->mutex->unlock (this->mutex);
+  if (!sa)
+    {
+      DBG1 (DBG_KNL, "SA not found");
+      return NOT_FOUND;
+    }
+
+  int rv_stat = stat_segment_connect ("/run/vpp/stats.sock");
+  if (rv_stat != 0)
+    {
+      DBG1 (DBG_KNL, "Not connecting with stats segmentation");
+      return NOT_FOUND;
+    }
+  vec_add1 (pattern, (u8 *) "/net/ipsec/sa");
+  dir = stat_segment_ls ((u8 **) pattern);
+  res = stat_segment_dump (dir);
+  /* i-loop for each results find by pattern - here two:
+   * 1. /net/ipsec/sa
+   * 2. /net/ipsec/sa/lost
+   */
+  for (i = 0; i < vec_len (res); i++)
+    {
+      switch (res[i].type)
+       {
+       /* type for how many packets are lost */
+       case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+         if (res[i].simple_counter_vec == 0)
+           continue;
+         break;
+       /* type for counter for each SA */
+       case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+         if (res[i].combined_counter_vec == 0)
+           continue;
+         /* k-loop for each threads - that you run VPP */
+         for (k = 0; k < vec_len (res[i].combined_counter_vec); k++)
+           {
+             if (sa->stat_index <= vec_len (res[i].combined_counter_vec[k]))
+               {
+                 DBG4 (DBG_KNL, "Thread: %d, Packets: %lu, Bytes: %lu", k,
+                       res[i].combined_counter_vec[k][sa->stat_index].packets,
+                       res[i].combined_counter_vec[k][sa->stat_index].bytes);
+                 res_bytes +=
+                   res[i].combined_counter_vec[k][sa->stat_index].bytes;
+                 res_packets +=
+                   res[i].combined_counter_vec[k][sa->stat_index].packets;
+               }
+           }
+         break;
+       case STAT_DIR_TYPE_NAME_VECTOR:
+         if (res[i].name_vector == 0)
+           continue;
+         break;
+       }
+    }
+  stat_segment_data_free (res);
+  stat_segment_disconnect ();
+
+  vec_free (pattern);
+  vec_free (dir);
+
+  if (bytes)
+    {
+      *bytes = res_bytes;
+    }
+  if (packets)
+    {
+      *packets = res_packets;
+    }
+  if (time)
+    {
+      *time = 0;
+    }
+
+  rv = SUCCESS;
+  return rv;
+}
+
+METHOD (kernel_ipsec_t, del_sa, status_t, private_kernel_vpp_ipsec_t *this,
+       kernel_ipsec_sa_id_t *id, kernel_ipsec_del_sa_t *data)
+{
+  char *out = NULL;
+  int out_len;
+  vl_api_ipsec_sad_entry_add_del_t *mp;
+  vl_api_ipsec_sad_entry_add_del_reply_t *rmp;
+  status_t rv = FAILED;
+  sa_t *sa;
+
+  this->mutex->lock (this->mutex);
+  sa = this->sas->get (this->sas, id);
+  if (!sa)
+    {
+      DBG1 (DBG_KNL, "SA not found");
+      rv = NOT_FOUND;
+      goto error;
+    }
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+  mp->is_add = 0;
+  u16 msg_id =
+    vl_msg_api_get_msg_index ((u8 *) "ipsec_sad_entry_add_del_ab64b5c6");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->entry.sad_id = htonl (sa->sa_id);
+
+  if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG1 (DBG_KNL, "vac removing SA failed");
+      goto error;
+    }
+  rmp = (void *) out;
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "del SA failed rv:%d", ntohl (rmp->retval));
+      goto error;
+    }
+
+  vl_msg_api_free (mp);
+  this->sas->remove (this->sas, id);
+  rv = SUCCESS;
+error:
+  free (out);
+  this->mutex->unlock (this->mutex);
+  return rv;
+}
+
+METHOD (kernel_ipsec_t, flush_sas, status_t, private_kernel_vpp_ipsec_t *this)
+{
+  enumerator_t *enumerator;
+  int out_len;
+  char *out;
+  vl_api_ipsec_sad_entry_add_del_t *mp;
+  sa_t *sa = NULL;
+
+  this->mutex->lock (this->mutex);
+  enumerator = this->sas->create_enumerator (this->sas);
+  while (enumerator->enumerate (enumerator, sa, NULL))
+    {
+      mp = vl_msg_api_alloc (sizeof (*mp));
+      memset (mp, 0, sizeof (*mp));
+      u16 msg_id =
+       vl_msg_api_get_msg_index ((u8 *) "ipsec_sad_entry_add_del_ab64b5c6");
+      mp->_vl_msg_id = htons (msg_id);
+      mp->entry.sad_id = htonl (sa->sa_id);
+      mp->is_add = 0;
+      if (vac->send (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+       {
+         DBG1 (DBG_KNL, "flush_sas failed!!!!");
+         return FALSE;
+       }
+      free (out);
+      vl_msg_api_free (mp);
+      this->sas->remove_at (this->sas, enumerator);
+    }
+  enumerator->destroy (enumerator);
+  this->mutex->unlock (this->mutex);
+
+  return SUCCESS;
+}
+
+METHOD (kernel_ipsec_t, add_policy, status_t, private_kernel_vpp_ipsec_t *this,
+       kernel_ipsec_policy_id_t *id, kernel_ipsec_manage_policy_t *data)
+{
+  return manage_policy (this, TRUE, id, data);
+}
+
+METHOD (kernel_ipsec_t, query_policy, status_t,
+       private_kernel_vpp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
+       kernel_ipsec_query_policy_t *data, time_t *use_time)
+{
+  return NOT_SUPPORTED;
+}
+
+METHOD (kernel_ipsec_t, del_policy, status_t, private_kernel_vpp_ipsec_t *this,
+       kernel_ipsec_policy_id_t *id, kernel_ipsec_manage_policy_t *data)
+{
+  return manage_policy (this, FALSE, id, data);
+}
+
+METHOD (kernel_ipsec_t, flush_policies, status_t,
+       private_kernel_vpp_ipsec_t *this)
+{
+  return NOT_SUPPORTED;
+}
+
+METHOD (kernel_ipsec_t, bypass_socket, bool, private_kernel_vpp_ipsec_t *this,
+       int fd, int family)
+{
+  return FALSE;
+}
+
+METHOD (kernel_ipsec_t, enable_udp_decap, bool,
+       private_kernel_vpp_ipsec_t *this, int fd, int family, u_int16_t port)
+{
+  DBG1 (DBG_KNL, "enable_udp_decap not supported!!!!!!!!!!!!!!!!!!!!!!!!!");
+  return FALSE;
+}
+
+METHOD (kernel_ipsec_t, destroy, void, private_kernel_vpp_ipsec_t *this)
+{
+  this->mutex->destroy (this->mutex);
+  this->sas->destroy (this->sas);
+  this->spds->destroy (this->spds);
+  this->routes->destroy (this->routes);
+  free (this);
+}
+
+kernel_vpp_ipsec_t *
+kernel_vpp_ipsec_create ()
+{
+  private_kernel_vpp_ipsec_t *this;
+
+  INIT(this,
+        .public = {
+            .interface = {
+                .get_features = _get_features,
+                .get_spi = _get_spi,
+                .get_cpi = _get_cpi,
+                .add_sa  = _add_sa,
+                .update_sa = _update_sa,
+                .query_sa = _query_sa,
+                .del_sa = _del_sa,
+                .flush_sas = _flush_sas,
+                .add_policy = _add_policy,
+                .query_policy = _query_policy,
+                .del_policy = _del_policy,
+                .flush_policies = _flush_policies,
+                .bypass_socket = _bypass_socket,
+                .enable_udp_decap = _enable_udp_decap,
+                .destroy = _destroy,
+            },
+        },
+        .next_sad_id = 0,
+        .next_spd_id = 0,
+        .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+        .sas = hashtable_create((hashtable_hash_t)sa_hash,
+                                (hashtable_equals_t)sa_equals, 32),
+        .spds = hashtable_create((hashtable_hash_t)interface_hash,
+                                 (hashtable_equals_t)interface_equals, 4),
+        .routes = linked_list_create(),
+        .install_routes = lib->settings->get_bool(lib->settings,
+                            "%s.install_routes", TRUE, lib->ns),
+    );
+
+  if (!init_spi (this))
+    {
+      destroy (this);
+      return NULL;
+    }
+
+  return &this->public;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h b/extras/strongswan/vpp_sswan/kernel_vpp_ipsec.h
new file mode 100644 (file)
index 0000000..6466988
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef KERNEL_VPP_IPSEC_H_
+#define KERNEL_VPP_IPSEC_H_
+/*
+ * 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 <kernel/kernel_ipsec.h>
+
+typedef struct kernel_vpp_ipsec_t kernel_vpp_ipsec_t;
+
+/**
+ * Implementation of the kernel ipsec interface using Netlink.
+ */
+struct kernel_vpp_ipsec_t
+{
+
+  /**
+   * Implements kernel_ipsec_t interface
+   */
+  kernel_ipsec_t interface;
+};
+
+/**
+ * Create a vpp kernel ipsec interface instance.
+ *
+ * @return          kernel_vpp_ipsec_t instance
+ */
+kernel_vpp_ipsec_t *kernel_vpp_ipsec_create ();
+
+#endif /** KERNEL_VPP_IPSEC_H_ @}*/
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_net.c b/extras/strongswan/vpp_sswan/kernel_vpp_net.c
new file mode 100644 (file)
index 0000000..02f002a
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * 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 <utils/debug.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <threading/thread.h>
+#include <threading/mutex.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>
+
+#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
+#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
+#include <vnet/ip/ip.api_enum.h>
+#include <vnet/ip/ip.api_types.h>
+#include <vnet/interface.api_enum.h>
+#include <vnet/interface.api_types.h>
+#undef vl_typedefs
+#undef vl_endianfun
+
+#include "kernel_vpp_net.h"
+#include "kernel_vpp_shared.h"
+
+typedef struct private_kernel_vpp_net_t private_kernel_vpp_net_t;
+
+/**
+ * Private data of kernel_vpp_net implementation.
+ */
+struct private_kernel_vpp_net_t
+{
+
+  /**
+   * Public interface.
+   */
+  kernel_vpp_net_t public;
+
+  /**
+   * Mutex to access interface list
+   */
+  mutex_t *mutex;
+
+  /**
+   * Known interfaces, as iface_t
+   */
+  linked_list_t *ifaces;
+
+  /**
+   * Inteface update thread
+   */
+  thread_t *net_update;
+
+  /**
+   * TRUE if interface events enabled
+   */
+  bool events_on;
+};
+
+/**
+ * Interface entry
+ */
+typedef struct
+{
+  /** interface index */
+  uint32_t index;
+  /** interface name */
+  char if_name[64];
+  /** list of known addresses, as host_t */
+  linked_list_t *addrs;
+  /** TRUE if up */
+  bool up;
+} iface_t;
+
+/**
+ * Address enumerator
+ */
+typedef struct
+{
+  /** implements enumerator_t */
+  enumerator_t public;
+  /** what kind of address should we enumerate? */
+  kernel_address_type_t which;
+  /** enumerator over interfaces */
+  enumerator_t *ifaces;
+  /** current enumerator over addresses, or NULL */
+  enumerator_t *addrs;
+  /** mutex to unlock on destruction */
+  mutex_t *mutex;
+} addr_enumerator_t;
+
+/**
+ * FIB path entry
+ */
+typedef struct
+{
+  chunk_t next_hop;
+  uint32_t sw_if_index;
+  uint8_t preference;
+} fib_path_t;
+
+/**
+ * Get an iface entry for a local address
+ */
+static iface_t *
+address2entry (private_kernel_vpp_net_t *this, host_t *ip)
+{
+  enumerator_t *ifaces, *addrs;
+  iface_t *entry, *found = NULL;
+  host_t *host;
+
+  ifaces = this->ifaces->create_enumerator (this->ifaces);
+  while (!found && ifaces->enumerate (ifaces, &entry))
+    {
+      addrs = entry->addrs->create_enumerator (entry->addrs);
+      while (!found && addrs->enumerate (addrs, &host))
+       {
+         if (host->ip_equals (host, ip))
+           {
+             found = entry;
+           }
+       }
+      addrs->destroy (addrs);
+    }
+  ifaces->destroy (ifaces);
+
+  return found;
+}
+
+/**
+ * Add or remove a route
+ */
+static status_t
+manage_route (private_kernel_vpp_net_t *this, bool add, chunk_t dst,
+             uint8_t prefixlen, host_t *gtw, char *name)
+{
+  char *out;
+  int out_len;
+  enumerator_t *enumerator;
+  iface_t *entry;
+  vl_api_ip_route_add_del_t *mp;
+  vl_api_ip_route_add_del_reply_t *rmp;
+  vl_api_fib_path_t *apath;
+  bool exists = FALSE;
+
+  this->mutex->lock (this->mutex);
+  enumerator = this->ifaces->create_enumerator (this->ifaces);
+  while (enumerator->enumerate (enumerator, &entry))
+    {
+      if (streq (name, entry->if_name))
+       {
+         exists = TRUE;
+         break;
+       }
+    }
+  enumerator->destroy (enumerator);
+  this->mutex->unlock (this->mutex);
+
+  if (!exists)
+    {
+      DBG1 (DBG_NET, "if_name %s not found", name);
+      return NOT_FOUND;
+    }
+
+  mp = vl_msg_api_alloc (sizeof (*mp) + sizeof (*apath));
+  memset (mp, 0, sizeof (*mp) + sizeof (*apath));
+  u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_route_add_del_b8ecfe0d");
+  mp->_vl_msg_id = ntohs (msg_id);
+  mp->is_add = add;
+  mp->route.prefix.len = prefixlen;
+  mp->route.n_paths = 1;
+  apath = &mp->route.paths[0];
+  apath->sw_if_index = ntohl (entry->index);
+  apath->rpf_id = ~0;
+  apath->weight = 1;
+  switch (dst.len)
+    {
+    case 4:
+      mp->route.prefix.address.af = ntohl (ADDRESS_IP4);
+      memcpy (&mp->route.prefix.address.un.ip4, dst.ptr, dst.len);
+      if (gtw)
+       {
+         chunk_t addr = gtw->get_address (gtw);
+         apath->proto = ntohl (FIB_API_PATH_NH_PROTO_IP4);
+         memcpy (&apath->nh.address.ip4, addr.ptr, dst.len);
+       }
+      break;
+    case 16:
+      mp->route.prefix.address.af = ntohl (ADDRESS_IP6);
+      memcpy (&mp->route.prefix.address.un.ip6, dst.ptr, dst.len);
+      if (gtw)
+       {
+         chunk_t addr = gtw->get_address (gtw);
+         apath->proto = ntohl (FIB_API_PATH_NH_PROTO_IP6);
+         memcpy (&apath->nh.address.ip6, addr.ptr, dst.len);
+       }
+      break;
+    default:
+      vl_msg_api_free (mp);
+      return FAILED;
+    }
+
+  if (vac->send (vac, (char *) mp, sizeof (*mp) + sizeof (*apath), &out,
+                &out_len))
+    {
+      DBG1 (DBG_KNL, "vac %sing route failed", add ? "add" : "remov");
+      vl_msg_api_free (mp);
+      return FAILED;
+    }
+  rmp = (void *) out;
+  vl_msg_api_free (mp);
+  if (rmp->retval)
+    {
+      DBG1 (DBG_KNL, "%s route failed %d", add ? "add" : "delete",
+           ntohl (rmp->retval));
+      free (out);
+      return FAILED;
+    }
+  free (out);
+  return SUCCESS;
+}
+
+/**
+ * Check if an address or net (addr with prefix net bits) is in
+ * subnet (net with net_len net bits)
+ */
+static bool
+addr_in_subnet (chunk_t addr, int prefix, chunk_t net, int net_len)
+{
+  static const u_char mask[] = {
+    0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
+  };
+  int byte = 0;
+
+  if (net_len == 0)
+    { /* any address matches a /0 network */
+      return TRUE;
+    }
+  if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len)
+    {
+      return FALSE;
+    }
+  /* scan through all bytes in network order */
+  while (net_len > 0)
+    {
+      if (net_len < 8)
+       {
+         return (mask[net_len] & addr.ptr[byte]) ==
+                (mask[net_len] & net.ptr[byte]);
+       }
+      else
+       {
+         if (addr.ptr[byte] != net.ptr[byte])
+           {
+             return FALSE;
+           }
+         byte++;
+         net_len -= 8;
+       }
+    }
+  return TRUE;
+}
+
+/**
+ * Get a route: If "nexthop" the nexthop is returned, source addr otherwise
+ */
+static host_t *
+get_route (private_kernel_vpp_net_t *this, host_t *dest, int prefix,
+          bool nexthop, char **iface, host_t *src)
+{
+  fib_path_t path;
+  char *out, *tmp;
+  int out_len, i, num;
+  vl_api_fib_path_t *fp;
+  host_t *addr = NULL;
+  enumerator_t *enumerator;
+  iface_t *entry;
+  int family;
+
+  path.sw_if_index = ~0;
+  path.preference = ~0;
+  path.next_hop = chunk_empty;
+
+  vl_api_ip_route_dump_t *mp;
+  vl_api_ip_route_details_t *rmp;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  clib_memset (mp, 0, sizeof (*mp));
+  u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_route_dump_b9d2e09e");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->table.is_ip6 = dest->get_family (dest) == AF_INET6 ? 1 : 0;
+  if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      vl_msg_api_free (mp);
+      DBG2 (DBG_KNL, "send VL_API_IP_ROUTE_ADD_DEL failed");
+      return NULL;
+    }
+  vl_msg_api_free (mp);
+
+  if (dest->get_family (dest) == AF_INET)
+    {
+      i = 0;
+      family = AF_INET;
+      if (prefix == -1)
+       prefix = 32;
+
+      tmp = out;
+      while (tmp < (out + out_len))
+       {
+         rmp = (void *) tmp;
+         num = rmp->route.n_paths;
+
+         if (rmp->route.prefix.len &&
+             addr_in_subnet (
+               dest->get_address (dest), prefix,
+               chunk_create (rmp->route.prefix.address.un.ip4, 4),
+               rmp->route.prefix.len))
+           {
+             fp = rmp->route.paths;
+             for (i = 0; i < num; i++)
+               {
+#define IS_IP4_ANY(a) (a[0] == 0 && a[1] == 0 && a[2] == 0 & a[3] == 0)
+                 if (fp->type == FIB_API_PATH_TYPE_DROP)
+                   {
+                     fp++;
+                     continue;
+                   }
+                 if ((fp->preference < path.preference) ||
+                     (path.sw_if_index == ~0) ||
+                     IS_IP4_ANY (path.next_hop.ptr))
+                   {
+                     path.sw_if_index = ntohl (fp->sw_if_index);
+                     path.preference = fp->preference;
+                     if (path.next_hop.ptr)
+                       vl_msg_api_free (path.next_hop.ptr);
+                     path.next_hop = chunk_create (fp->nh.address.ip4, 4);
+                   }
+                 fp++;
+               }
+           }
+         tmp += sizeof (*rmp) + (sizeof (*fp) * num);
+       }
+    }
+  else
+    {
+      DBG1 (DBG_KNL, "not yet support ip6");
+      return NULL;
+    }
+
+  if (path.next_hop.len)
+    {
+      if (nexthop)
+       {
+         if (iface)
+           {
+             *iface = NULL;
+             this->mutex->lock (this->mutex);
+             enumerator = this->ifaces->create_enumerator (this->ifaces);
+             while (enumerator->enumerate (enumerator, &entry))
+               {
+                 if (entry->index == path.sw_if_index)
+                   {
+                     *iface = strdup (entry->if_name);
+                     break;
+                   }
+               }
+             enumerator->destroy (enumerator);
+             this->mutex->unlock (this->mutex);
+           }
+         addr = host_create_from_chunk (family, path.next_hop, 0);
+       }
+      else
+       {
+         if (src)
+           {
+             addr = src->clone (src);
+           }
+       }
+    }
+
+  free (out);
+
+  return addr;
+}
+
+METHOD (enumerator_t, addr_enumerate, bool, addr_enumerator_t *this,
+       va_list args)
+{
+  iface_t *entry;
+  host_t **host;
+
+  VA_ARGS_VGET (args, host);
+
+  while (TRUE)
+    {
+      while (!this->addrs)
+       {
+         if (!this->ifaces->enumerate (this->ifaces, &entry))
+           {
+             return FALSE;
+           }
+         if (!entry->up && !(this->which & ADDR_TYPE_DOWN))
+           {
+             continue;
+           }
+         this->addrs = entry->addrs->create_enumerator (entry->addrs);
+       }
+      if (this->addrs->enumerate (this->addrs, host))
+       {
+         return TRUE;
+       }
+      this->addrs->destroy (this->addrs);
+      this->addrs = NULL;
+    }
+}
+
+METHOD (enumerator_t, addr_destroy, void, addr_enumerator_t *this)
+{
+  DESTROY_IF (this->addrs);
+  this->ifaces->destroy (this->ifaces);
+  this->mutex->unlock (this->mutex);
+  free (this);
+}
+
+METHOD (kernel_net_t, get_interface_name, bool, private_kernel_vpp_net_t *this,
+       host_t *ip, char **name)
+{
+  iface_t *entry;
+
+  this->mutex->lock (this->mutex);
+  entry = address2entry (this, ip);
+  if (entry && name)
+    {
+      *name = strdup (entry->if_name);
+    }
+  this->mutex->unlock (this->mutex);
+
+  return entry != NULL;
+}
+
+METHOD (kernel_net_t, create_address_enumerator, enumerator_t *,
+       private_kernel_vpp_net_t *this, kernel_address_type_t which)
+{
+  addr_enumerator_t *enumerator;
+
+  if (!(which & ADDR_TYPE_REGULAR))
+    {
+      /* we currently have no virtual, but regular IPs only */
+      return enumerator_create_empty ();
+    }
+
+  this->mutex->lock (this->mutex);
+
+  INIT(enumerator,
+        .public = {
+            .enumerate = enumerator_enumerate_default,
+            .venumerate = _addr_enumerate,
+            .destroy = _addr_destroy,
+        },
+        .which = which,
+        .ifaces = this->ifaces->create_enumerator(this->ifaces),
+        .mutex = this->mutex,
+    );
+  return &enumerator->public;
+}
+
+METHOD (kernel_net_t, get_source_addr, host_t *,
+       private_kernel_vpp_net_t *this, host_t *dest, host_t *src)
+{
+  return get_route (this, dest, -1, FALSE, NULL, src);
+}
+
+METHOD (kernel_net_t, get_nexthop, host_t *, private_kernel_vpp_net_t *this,
+       host_t *dest, int prefix, host_t *src, char **iface)
+{
+  return get_route (this, dest, prefix, TRUE, iface, src);
+}
+
+METHOD (kernel_net_t, add_ip, status_t, private_kernel_vpp_net_t *this,
+       host_t *virtual_ip, int prefix, char *iface_name)
+{
+  return NOT_SUPPORTED;
+}
+
+METHOD (kernel_net_t, del_ip, status_t, private_kernel_vpp_net_t *this,
+       host_t *virtual_ip, int prefix, bool wait)
+{
+  return NOT_SUPPORTED;
+}
+
+METHOD (kernel_net_t, add_route, status_t, private_kernel_vpp_net_t *this,
+       chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip,
+       char *if_name)
+{
+  return manage_route (this, TRUE, dst_net, prefixlen, gateway, if_name);
+}
+
+METHOD (kernel_net_t, del_route, status_t, private_kernel_vpp_net_t *this,
+       chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip,
+       char *if_name)
+{
+  return manage_route (this, FALSE, dst_net, prefixlen, gateway, if_name);
+}
+
+static void
+iface_destroy (iface_t *this)
+{
+  this->addrs->destroy_offset (this->addrs, offsetof (host_t, destroy));
+  free (this);
+}
+
+METHOD (kernel_net_t, destroy, void, private_kernel_vpp_net_t *this)
+{
+  this->net_update->cancel (this->net_update);
+  this->mutex->destroy (this->mutex);
+  this->ifaces->destroy_function (this->ifaces, (void *) iface_destroy);
+  free (this);
+}
+
+/**
+ * Update addresses for an iface entry
+ */
+static void
+update_addrs (private_kernel_vpp_net_t *this, iface_t *entry)
+{
+  char *out;
+  int out_len, i, num;
+  vl_api_ip_address_dump_t *mp;
+  vl_api_ip_address_details_t *rmp, *tmp;
+  linked_list_t *addrs;
+  host_t *host;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  clib_memset (mp, 0, sizeof (*mp));
+  u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_address_dump_2d033de4");
+  mp->_vl_msg_id = htons (msg_id);
+  mp->sw_if_index = htonl (entry->index);
+  mp->is_ipv6 = 0;
+  if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG2 (DBG_NET, "update_addrs : send VL_API_IP_ADDRESS_DUMP ipv4 failed");
+      vl_msg_api_free (mp);
+      return;
+    }
+  num = out_len / sizeof (*rmp);
+  addrs = linked_list_create ();
+  tmp = (vl_api_ip_address_details_t *) out;
+  for (i = 0; i < num; i++)
+    {
+      tmp += i;
+      rmp = tmp;
+      host = host_create_from_chunk (
+       AF_INET, chunk_create (rmp->prefix.address.un.ip4, 4), 0);
+      addrs->insert_last (addrs, host);
+    }
+  free (out);
+
+  mp->is_ipv6 = 1;
+  if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
+    {
+      DBG2 (DBG_NET, "update_addrs : send VL_API_IP_ADDRESS_DUMP ipv6 failed");
+      vl_msg_api_free (mp);
+      return;
+    }
+  num = out_len / sizeof (*rmp);
+  tmp = (vl_api_ip_address_details_t *) out;
+  for (i = 0; i < num; i++)
+    {
+      tmp += i;
+      rmp = tmp;
+      host = host_create_from_chunk (
+       AF_INET6, chunk_create (rmp->prefix.address.un.ip6, 16), 0);
+      addrs->insert_last (addrs, host);
+    }
+  vl_msg_api_free (mp);
+  free (out);
+
+  entry->addrs->destroy (entry->addrs);
+  entry->addrs =
+    linked_list_create_from_enumerator (addrs->create_enumerator (addrs));
+  addrs->destroy (addrs);
+}
+
+/**
+ * VPP API interface event callback
+ */
+static void
+event_cb (char *data, int data_len, void *ctx)
+{
+  private_kernel_vpp_net_t *this = ctx;
+  vl_api_sw_interface_event_t *event;
+  iface_t *entry;
+  enumerator_t *enumerator;
+
+  event = (void *) data;
+  this->mutex->lock (this->mutex);
+  enumerator = this->ifaces->create_enumerator (this->ifaces);
+  while (enumerator->enumerate (enumerator, &entry))
+    {
+      if (entry->index == ntohl (event->sw_if_index))
+       {
+         if (event->deleted)
+           {
+             this->ifaces->remove_at (this->ifaces, enumerator);
+             DBG2 (DBG_NET, "interface deleted %u %s", entry->index,
+                   entry->if_name);
+             iface_destroy (entry);
+           }
+         else if (entry->up != (event->flags & IF_STATUS_API_FLAG_LINK_UP))
+           {
+             entry->up =
+               (event->flags & IF_STATUS_API_FLAG_LINK_UP) ? TRUE : FALSE;
+             DBG2 (DBG_NET, "interface state changed %u %s %s", entry->index,
+                   entry->if_name, entry->up ? "UP" : "DOWN");
+           }
+         break;
+       }
+    }
+  enumerator->destroy (enumerator);
+  this->mutex->unlock (this->mutex);
+  free (data);
+}
+
+/**
+ * Inteface update thread (update interface list and interface address)
+ */
+static void *
+net_update_thread_fn (private_kernel_vpp_net_t *this)
+{
+  status_t rv;
+  while (1)
+    {
+      char *out;
+      int out_len;
+      vl_api_sw_interface_dump_t *mp;
+      vl_api_sw_interface_details_t *rmp;
+      enumerator_t *enumerator;
+      iface_t *entry;
+
+      mp = vl_msg_api_alloc (sizeof (*mp));
+      memset (mp, 0, sizeof (*mp));
+      u16 msg_id =
+       vl_msg_api_get_msg_index ((u8 *) "sw_interface_dump_aa610c27");
+      mp->_vl_msg_id = htons (msg_id);
+      mp->name_filter_valid = 0;
+      rv = vac->send_dump (vac, (u8 *) mp, sizeof (*mp), &out, &out_len);
+      if (!rv)
+       {
+         int i, num;
+         this->mutex->lock (this->mutex);
+         enumerator = this->ifaces->create_enumerator (this->ifaces);
+         num = out_len / sizeof (*rmp);
+         rmp = (vl_api_sw_interface_details_t *) out;
+         for (i = 0; i < num; i++)
+           {
+             bool exists = FALSE;
+             if (i)
+               rmp += 1;
+             while (enumerator->enumerate (enumerator, &entry))
+               {
+                 if (entry->index == ntohl (rmp->sw_if_index))
+                   {
+                     exists = TRUE;
+                     break;
+                   }
+               }
+             if (!exists)
+               {
+                 INIT (entry, .index = ntohl (rmp->sw_if_index),
+                       .up = (rmp->flags & IF_STATUS_API_FLAG_LINK_UP) ?
+                                     TRUE :
+                                     FALSE,
+                       .addrs = linked_list_create (), );
+                 memcpy (entry->if_name, rmp->interface_name, 63);
+                 this->ifaces->insert_last (this->ifaces, entry);
+               }
+             update_addrs (this, entry);
+           }
+         enumerator->destroy (enumerator);
+         this->mutex->unlock (this->mutex);
+         free (out);
+       }
+      vl_msg_api_free (mp);
+
+      if (!this->events_on)
+       {
+         vl_api_want_interface_events_t *emp;
+         api_main_t *am = vlibapi_get_main ();
+
+         emp = vl_msg_api_alloc (sizeof (*emp));
+         clib_memset (emp, 0, sizeof (*emp));
+         u16 msg_id =
+           vl_msg_api_get_msg_index ((u8 *) "want_interface_events_476f5a08");
+         emp->_vl_msg_id = ntohs (msg_id);
+         emp->enable_disable = 1;
+         emp->pid = ntohl (am->our_pid);
+         rv = vac->register_event (vac, (char *) emp, sizeof (*emp), event_cb,
+                                   VL_API_SW_INTERFACE_EVENT, this);
+         if (!rv)
+           this->events_on = TRUE;
+       }
+
+      sleep (2);
+    }
+  return NULL;
+}
+
+kernel_vpp_net_t *
+kernel_vpp_net_create ()
+{
+  private_kernel_vpp_net_t *this;
+
+  INIT(this,
+        .public = {
+            .interface = {
+                .get_interface = _get_interface_name,
+                .create_address_enumerator = _create_address_enumerator,
+                .get_source_addr = _get_source_addr,
+                .get_nexthop = _get_nexthop,
+                .add_ip = _add_ip,
+                .del_ip = _del_ip,
+                .add_route = _add_route,
+                .del_route = _del_route,
+                .destroy = _destroy,
+            },
+        },
+        .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+        .ifaces = linked_list_create(),
+        .events_on = FALSE,
+    );
+
+  this->net_update =
+    thread_create ((thread_main_t) net_update_thread_fn, this);
+
+  return &this->public;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_net.h b/extras/strongswan/vpp_sswan/kernel_vpp_net.h
new file mode 100644 (file)
index 0000000..bf5359c
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef KERNEL_VPP_NET_H_
+#define KERNEL_VPP_NET_H_
+/*
+ * 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 <kernel/kernel_net.h>
+
+typedef struct kernel_vpp_net_t kernel_vpp_net_t;
+
+/**
+ * Implementation of the kernel network interface using Netlink.
+ */
+struct kernel_vpp_net_t
+{
+
+  /**
+   * Implements kernel_net_t interface
+   */
+  kernel_net_t interface;
+};
+
+/**
+ * Create a vpp kernel network interface instance.
+ *
+ * @return          kernel_vpp_net_t instance
+ */
+kernel_vpp_net_t *kernel_vpp_net_create ();
+
+#endif /** KERNEL_VPP_NET_H_ @}*/
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_plugin.c b/extras/strongswan/vpp_sswan/kernel_vpp_plugin.c
new file mode 100644 (file)
index 0000000..9791987
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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 <utils/debug.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.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
+
+#include "kernel_vpp_plugin.h"
+#include "kernel_vpp_shared.h"
+#include "kernel_vpp_ipsec.h"
+#include "kernel_vpp_net.h"
+
+typedef struct private_kernel_vpp_plugin_t private_kernel_vpp_plugin_t;
+
+/**
+ * private data of kernel vpp plugin
+ */
+struct private_kernel_vpp_plugin_t
+{
+  /**
+   * implements plugin interface
+   */
+  kernel_vpp_plugin_t public;
+
+  vac_t *vac;
+};
+
+METHOD (plugin_t, get_name, char *, private_kernel_vpp_plugin_t *this)
+{
+  return "kernel-vpp";
+}
+
+METHOD (plugin_t, get_features, int, private_kernel_vpp_plugin_t *this,
+       plugin_feature_t *features[])
+{
+  static plugin_feature_t f[] = {
+    PLUGIN_CALLBACK (kernel_ipsec_register, kernel_vpp_ipsec_create),
+    PLUGIN_PROVIDE (CUSTOM, "kernel-ipsec"),
+    PLUGIN_CALLBACK (kernel_net_register, kernel_vpp_net_create),
+    PLUGIN_PROVIDE (CUSTOM, "kernel-net"),
+  };
+  *features = f;
+  return countof (f);
+}
+
+METHOD (plugin_t, destroy, void, private_kernel_vpp_plugin_t *this)
+{
+  if (this->vac)
+    {
+      lib->set (lib, "kernel-vpp-vac", NULL);
+      this->vac->destroy (this->vac);
+    }
+  free (this);
+}
+
+plugin_t *
+kernel_vpp_plugin_create ()
+{
+  private_kernel_vpp_plugin_t *this;
+
+  INIT(this,
+        .public = {
+            .plugin = {
+                .get_name = _get_name,
+                .get_features = _get_features,
+                .destroy = _destroy,
+            },
+        },
+    );
+
+  this->vac = vac_create ("strongswan");
+  if (!this->vac)
+    {
+      DBG1 (DBG_KNL, "vac_create failed");
+      destroy (this);
+      return NULL;
+    }
+  lib->set (lib, "kernel-vpp-vac", this->vac);
+
+  return &this->public.plugin;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_plugin.h b/extras/strongswan/vpp_sswan/kernel_vpp_plugin.h
new file mode 100644 (file)
index 0000000..0214029
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef KERNEL_VPP_PLUGIN_H_
+#define KERNEL_VPP_PLUGIN_H_
+/*
+ * 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 <plugins/plugin.h>
+
+typedef struct kernel_vpp_plugin_t kernel_vpp_plugin_t;
+
+/**
+ * vpp kernel interface plugin
+ */
+struct kernel_vpp_plugin_t
+{
+
+  /**
+   * implements plugin interface
+   */
+  plugin_t plugin;
+};
+
+#endif /** KERNEL_VPP_PLUGIN_H_ @}*/
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_shared.c b/extras/strongswan/vpp_sswan/kernel_vpp_shared.c
new file mode 100644 (file)
index 0000000..9eabb68
--- /dev/null
@@ -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;
+}
diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_shared.h b/extras/strongswan/vpp_sswan/kernel_vpp_shared.h
new file mode 100644 (file)
index 0000000..c699d49
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef KERNEL_VPP_SHARED_H_
+#define KERNEL_VPP_SHARED_H_
+/*
+ * 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.
+ */
+
+typedef struct vac_t vac_t;
+
+/**
+ * Callback function invoked for received event messages.
+ *
+ * @param data     associated event message, destroyed by VPP API wrapper
+ * @param data_len length of the event message
+ * @param ctx      user data, as passed to register_event
+ */
+typedef void (*event_cb_t) (char *data, int data_len, void *ctx);
+
+/**
+ * Wrapper around VPP binary API client.
+ */
+struct vac_t
+{
+
+  /**
+   * Destroy the VPP API client.
+   */
+  void (*destroy) (vac_t *this);
+
+  /**
+   * Send VPP API message and wait for a reply
+   *
+   * @param in      VPP API message to send
+   * @param in_len  length of the message to send
+   * @param out     received VPP API message
+   * @param out_len length of the received message
+   */
+  status_t (*send) (vac_t *this, char *in, int in_len, char **out,
+                   int *out_len);
+
+  /**
+   * Send VPP API dump message and wait for a reply.
+   *
+   * @param in      VPP API message to send
+   * @param in_len  length of the message to send
+   * @param out     received VPP API message
+   * @param out_len length of the received message
+   */
+  status_t (*send_dump) (vac_t *this, char *in, int in_len, char **out,
+                        int *out_len);
+
+  /**
+   * Register for VPP API event of a given kind.
+   *
+   * @param in       VPP API event message to register
+   * @param in_len   length of the event message to register
+   * @param cb       callback function to register
+   * @param event_id event ID
+   * @param ctx      user data passed to callback invocations
+   */
+  status_t (*register_event) (vac_t *this, char *in, int in_len, event_cb_t cb,
+                             uint16_t event_id, void *ctx);
+};
+
+extern vac_t *vac;
+
+/**
+ * Establishing a binary API connection to VPP.
+ *
+ * @param name client name
+ * @return     vac_t instance
+ */
+vac_t *vac_create (char *name);
+
+#endif /* KERNEL_VPP_SHARED_H_ */
diff --git a/extras/strongswan/vpp_sswan/swanctl.conf b/extras/strongswan/vpp_sswan/swanctl.conf
new file mode 100644 (file)
index 0000000..f3e7a78
--- /dev/null
@@ -0,0 +1,35 @@
+connections {
+  net-net {
+    local_addrs = 192.168.0.2
+    remote_addrs = 192.168.0.1
+    local {
+      auth = psk
+      id = sun.strongswan.org
+    }
+    remote {
+      auth = psk
+      id = moon.strongswan.org
+    }
+    children {
+      net-net {
+        local_ts = 192.168.200.0/24
+        remote_ts = 192.168.100.0/24
+        esp_proposals = aes128-sha1-modp2048
+        rekey_time = 240m
+      }
+    }
+    version = 2
+    mobike = yes
+    encap = no # NAT-T if needed
+    proposals = aes128-sha256-x25519
+    }
+}
+secrets {
+  ike-net-net {
+    id = moon.strongswan.org
+    secret = simplepsk
+  }
+}
+
+# Include config snippets
+include conf.d/*.conf