From 4e88e041ad47bf422bbb2a0940f77aba11ea2178 Mon Sep 17 00:00:00 2001 From: Gabriel Oginski Date: Wed, 29 Jun 2022 12:54:30 +0000 Subject: vpp-swan: Add plugin for vpp-swan 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 Change-Id: Iec77945892453fac1890d3c49d7d86fc6b09c893 --- extras/strongswan/vpp_sswan/kernel_vpp_net.c | 752 +++++++++++++++++++++++++++ 1 file changed, 752 insertions(+) create mode 100644 extras/strongswan/vpp_sswan/kernel_vpp_net.c (limited to 'extras/strongswan/vpp_sswan/kernel_vpp_net.c') diff --git a/extras/strongswan/vpp_sswan/kernel_vpp_net.c b/extras/strongswan/vpp_sswan/kernel_vpp_net.c new file mode 100644 index 00000000000..02f002a3183 --- /dev/null +++ b/extras/strongswan/vpp_sswan/kernel_vpp_net.c @@ -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 +#include +#include +#include +#include + +#define vl_typedefs +#define vl_endianfun +/* Include the (first) vlib-api API definition layer */ +#include +/* Include the current layer (third) vpp API definition layer */ +#include +#include + +#include +#include +#include +#include +#include +#include +#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; +} -- cgit 1.2.3-korg