[strongSwan-dev] [hw-offload-auto-mode v2 2/3] kernel-netlink: Add new hw_offload mode - auto

adin at mellanox.com adin at mellanox.com
Mon Mar 12 15:34:20 CET 2018


From: Adi Nissim <adin at mellanox.com>

Until now there were 2 hw_offload modes: no/yes
* hw_offload = no  : Configure the SA without HW offload.
* hw_offload = yes : Configure the SA with HW offload.
                     In this case, if the device does not support
                     offloading, SA creation will fail.

This commit introduces a new mode: hw_offload = auto
----------------------------------------------------
If the device and kernel support HW offload, configure
the SA with HW offload, but do not fail SA creation otherwise.

Signed-off-by: Adi Nissim <adin at mellanox.com>
Reviewed-by: Aviv Heller <avivh at mellanox.com>
---
v1 -> v2:
            -Fix the FD leak
---
 .../plugins/kernel_netlink/kernel_netlink_ipsec.c  | 286 +++++++++++++++++++--
 1 file changed, 263 insertions(+), 23 deletions(-)

diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index 4e79dfc..dbeccf8 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -18,15 +18,40 @@
  * for more details.
  */

+/*
+ * Copyright (C) 2018 Mellanox Technologies.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #define _GNU_SOURCE
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/ioctl.h>
 #include <stdint.h>
 #include <linux/ipsec.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/xfrm.h>
 #include <linux/udp.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
 #include <net/if.h>
 #include <unistd.h>
 #include <time.h>
@@ -237,6 +262,21 @@ static kernel_algorithm_t compression_algs[] = {
 };

 /**
+ * IPsec offload
+ */
+enum nic_offload_state {
+	NIC_OFFLOAD_UNKNOWN,
+	NIC_OFFLOAD_UNSUPPORTED,
+	NIC_OFFLOAD_SUPPORTED
+};
+
+static struct {
+	unsigned int bit;
+	unsigned int total_blocks;
+	enum nic_offload_state state;
+} netlink_esp_hw_offload;
+
+/**
  * Look up a kernel algorithm name and its key size
  */
 static const char* lookup_algorithm(transform_type_t type, int ikev2)
@@ -1290,6 +1330,224 @@ static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark)
 	return TRUE;
 }

+/**
+ * check if kernel supported HW offload
+ */
+static void netlink_find_offload_feature(const char *ifname, int fd_for_socket)
+{
+	struct ethtool_sset_info *sset_info = NULL;
+	struct ethtool_gstrings *cmd = NULL;
+	struct ifreq ifr;
+	uint32_t sset_len, i;
+	char *str;
+	int err;
+
+	netlink_esp_hw_offload.state = NIC_OFFLOAD_UNSUPPORTED;
+
+	/* Determine number of device-features */
+	sset_info = malloc(sizeof(*sset_info));
+	if (sset_info == NULL)
+	{
+		goto out;
+	}
+	memset(sset_info, 0, sizeof(*sset_info));
+
+	sset_info->cmd = ETHTOOL_GSSET_INFO;
+	sset_info->sset_mask = 1ULL << ETH_SS_FEATURES;
+	strcpy(ifr.ifr_name, ifname);
+
+	ifr.ifr_data = (void *)sset_info;
+
+	err = ioctl(fd_for_socket, SIOCETHTOOL, &ifr);
+	if (err != 0)
+	{
+		goto out;
+	}
+
+	if (sset_info->sset_mask != 1ULL << ETH_SS_FEATURES)
+	{
+		goto out;
+	}
+	sset_len = sset_info->data[0];
+
+	/* Retrieve names of device-features */
+	cmd = malloc(sizeof(*cmd) + ETH_GSTRING_LEN * sset_len);
+	if (cmd == NULL)
+	{
+		goto out;
+	}
+
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->cmd = ETHTOOL_GSTRINGS;
+	cmd->string_set = ETH_SS_FEATURES;
+	strcpy(ifr.ifr_name, ifname);
+	ifr.ifr_data = (void *)cmd;
+	err = ioctl(fd_for_socket, SIOCETHTOOL, &ifr);
+	if (err)
+	{
+		goto out;
+	}
+
+	/* Look for the ESP_HW feature bit */
+	str = (char *)cmd->data;
+	for (i = 0; i < cmd->len; i++)
+	{
+		if (strneq(str, "esp-hw-offload", ETH_GSTRING_LEN) == 1)
+			break;
+		str += ETH_GSTRING_LEN;
+	}
+	if (i >= cmd->len)
+	{
+		goto out;
+	}
+
+	netlink_esp_hw_offload.bit = i;
+	netlink_esp_hw_offload.total_blocks = (sset_len + 31) / 32;
+	netlink_esp_hw_offload.state = NIC_OFFLOAD_SUPPORTED;
+
+out:
+	if (sset_info != NULL)
+	{
+		free(sset_info);
+	}
+	if (cmd != NULL)
+	{
+		free(cmd);
+	}
+}
+
+/**
+ * Check if interface supported HW offload
+ */
+static bool netlink_detect_offload(const char *ifname)
+{
+	struct ethtool_gfeatures *cmd;
+	uint32_t feature_bit;
+	struct ifreq ifr;
+	bool ret = FALSE;
+	int nl_send_fd;
+	int block;
+
+	nl_send_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
+
+	if (nl_send_fd < 0)
+	{
+		return FALSE;
+	}
+
+	/*
+	 * Kernel requires a real interface in order to query the kernel-wide
+	 * capability, so we do it here on first invocation.
+	 */
+	if (netlink_esp_hw_offload.state == NIC_OFFLOAD_UNKNOWN)
+	{
+		netlink_find_offload_feature(ifname, nl_send_fd);
+	}
+
+	if (netlink_esp_hw_offload.state == NIC_OFFLOAD_UNSUPPORTED)
+	{
+		DBG1(DBG_KNL, "HW offload is not supported by kernel");
+		goto out;
+	}
+
+	/* Feature is supported by kernel. Query device features */
+	cmd = malloc(sizeof(cmd->features[0]) *
+			netlink_esp_hw_offload.total_blocks);
+	if (cmd == NULL)
+	{
+		goto out;
+	}
+
+	strcpy(ifr.ifr_name, ifname);
+
+	ifr.ifr_data = (void *)cmd;
+	cmd->cmd = ETHTOOL_GFEATURES;
+	cmd->size = netlink_esp_hw_offload.total_blocks;
+
+	if (ioctl(nl_send_fd, SIOCETHTOOL, &ifr))
+	{
+		goto out_free;
+	}
+
+	block = netlink_esp_hw_offload.bit / 32;
+	feature_bit = 1U << (netlink_esp_hw_offload.bit % 32);
+	if (cmd->features[block].active & feature_bit)
+	{
+		ret = TRUE;
+	}
+
+out_free:
+	free(cmd);
+	if (ret == FALSE)
+	{
+		DBG1(DBG_KNL, "HW offload is not supported by device");
+	}
+out:
+	close(nl_send_fd);
+	return ret;
+}
+
+/**
+ * There are 3 configuration options:
+ * 1. HW_OFFLOAD_NO   : Do not configure HW offload..
+ * 2. HW_OFFLOAD_YES  : Configure HW offload.
+ *                      Fail SA addition if offload is not supported.
+ * 3. HW_OFFLOAD_AUTO : Configure HW offload if supported by the kernel
+ *                      and device.
+ *                      Do not fail SA addition otherwise.
+ */
+static bool config_hw_offload(kernel_ipsec_sa_id_t *id,
+		kernel_ipsec_add_sa_t *data, struct nlmsghdr *hdr, int buflen)
+{
+	bool cfg_hw_offload_is_yes = (data->hw_offload == HW_OFFLOAD_YES);
+	host_t *local = data->inbound ? id->dst : id->src;
+	struct xfrm_user_offload *offload;
+	bool hw_offload_support;
+	bool ret = FALSE;
+	char *ifname;
+
+	/* Do Ipsec configuration without offload */
+	if (data->hw_offload == HW_OFFLOAD_NO)
+	{
+		return TRUE;
+	}
+
+	if (!charon->kernel->get_interface(charon->kernel, local, &ifname))
+	{
+		return !cfg_hw_offload_is_yes;
+	}
+
+	/* Check if interface supports hw_offload */
+	hw_offload_support = netlink_detect_offload(ifname);
+
+	if (!hw_offload_support)
+	{
+		ret = !cfg_hw_offload_is_yes;
+		goto out;
+	}
+
+	/* Activate HW offload */
+	offload = netlink_reserve(hdr, buflen,
+							  XFRMA_OFFLOAD_DEV, sizeof(*offload));
+	if (!offload)
+	{
+		ret = !cfg_hw_offload_is_yes;
+		goto out;
+	}
+	offload->ifindex = if_nametoindex(ifname);
+	if (local->get_family(local) == AF_INET6)
+	{
+		offload->flags |= XFRM_OFFLOAD_IPV6;
+	}
+	offload->flags |= data->inbound ? XFRM_OFFLOAD_INBOUND : 0;
+
+	ret = TRUE;
+
+out:
+	free(ifname);
+	return ret;
+}
+
 METHOD(kernel_ipsec_t, add_sa, status_t,
 	private_kernel_netlink_ipsec_t *this, kernel_ipsec_sa_id_t *id,
 	kernel_ipsec_add_sa_t *data)
@@ -1650,30 +1908,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
 				 data->replay_window);
 			sa->replay_window = data->replay_window;
 		}
-		if (data->hw_offload)
-		{
-			host_t *local = data->inbound ? id->dst : id->src;
-			char *ifname;

-			if (charon->kernel->get_interface(charon->kernel, local, &ifname))
-			{
-				struct xfrm_user_offload *offload;
-
-				offload = netlink_reserve(hdr, sizeof(request),
-										  XFRMA_OFFLOAD_DEV, sizeof(*offload));
-				if (!offload)
-				{
-					free(ifname);
-					goto failed;
-				}
-				offload->ifindex = if_nametoindex(ifname);
-				if (local->get_family(local) == AF_INET6)
-				{
-					offload->flags |= XFRM_OFFLOAD_IPV6;
-				}
-				offload->flags |= data->inbound ? XFRM_OFFLOAD_INBOUND : 0;
-				free(ifname);
-			}
+		DBG2(DBG_KNL, "  HW offload mode = %N", hw_offload_names, data->hw_offload);
+		if (!config_hw_offload(id, data, hdr, sizeof(request)))
+		{
+			DBG1(DBG_KNL, "failed to configure HW offload");
+			goto failed;
 		}
 	}

--
1.8.3.1



More information about the Dev mailing list