[Swan-dev] [libreswan RFC 3/3] kernel, netlink: Add support for nic_offload='auto' mode

ilant at mellanox.com ilant at mellanox.com
Tue Jul 11 15:09:07 UTC 2017


From: Ilan Tayari <ilant at mellanox.com>

Provide per-device detection callback in kernel_ops.

Detect kernel capability when adding nic_offload SA for the
first time.
Ethtool IOCTL requires a valid device, so this cannot be done
at init time.

Detect per-device capability using ethtool features.

Signed-off-by: Ilan Tayari <ilant at mellanox.com>
---
 programs/pluto/kernel.c         | 33 ++++++++++++--
 programs/pluto/kernel.h         |  4 +-
 programs/pluto/kernel_netlink.c | 99 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 128 insertions(+), 8 deletions(-)

diff --git a/programs/pluto/kernel.c b/programs/pluto/kernel.c
index b26edfc98..6b8c9e8b0 100644
--- a/programs/pluto/kernel.c
+++ b/programs/pluto/kernel.c
@@ -1714,6 +1714,25 @@ static bool del_spi(ipsec_spi_t spi, int proto,
 	return kernel_ops->del_sa(&sa);
 }
 
+static void setup_esp_nic_offload(struct kernel_sa *sa, struct connection *c,
+		bool *nic_offload_fallback)
+{
+	if (c->nic_offload == nic_offload_never)
+		return;
+	if (!c->interface || !c->interface->ip_dev ||
+		!c->interface->ip_dev->id_rname)
+		return;
+
+	if (c->nic_offload == nic_offload_auto) {
+		if (!kernel_ops->nic_offload_supported)
+			return;
+		if (!kernel_ops->nic_offload_supported(sa, c->interface->ip_dev->id_rname))
+			return;
+		*nic_offload_fallback = TRUE;
+	}
+	sa->nic_offload_dev = c->interface->ip_dev->id_rname;
+}
+
 /*
  * Set up one direction of the SA bundle
  */
@@ -1732,6 +1751,8 @@ static bool setup_half_ipsec_sa(struct state *st, bool inbound)
 	bool incoming_ref_set = FALSE;
 	IPsecSAref_t refhim = st->st_refhim;
 	IPsecSAref_t new_refhim = IPSEC_SAREF_NULL;
+	bool nic_offload_fallback = FALSE;
+	bool ret;
 
 	/* SPIs, saved for spigrouping or undoing, if necessary */
 	struct kernel_sa said[EM_MAXRELSPIS];
@@ -1794,9 +1815,6 @@ static bool setup_half_ipsec_sa(struct state *st, bool inbound)
 	said_boilerplate.transport_proto = c->spd.this.protocol;
 	said_boilerplate.sa_lifetime = c->sa_ipsec_life_seconds;
 	said_boilerplate.outif = -1;
-	said_boilerplate.nic_offload = c->nic_offload;
-	if (c->nic_offload && c->interface != NULL)
-		said_boilerplate.nic_offload_ifindex = if_nametoindex(c->interface->ip_dev->id_rname);
 
 #ifdef HAVE_LABELED_IPSEC
 	said_boilerplate.sec_ctx = st->sec_ctx;
@@ -2286,8 +2304,15 @@ static bool setup_half_ipsec_sa(struct state *st, bool inbound)
 			said_next->ref = refhim;
 			outgoing_ref_set = TRUE;
 		}
+		setup_esp_nic_offload(said_next, c, &nic_offload_fallback);
 
-		if (!kernel_ops->add_sa(said_next, replace)) {
+		ret = kernel_ops->add_sa(said_next, replace);
+		if (!ret && said_next->nic_offload_dev && nic_offload_fallback) {
+			/* Fallback to non-nic-offload crypto */
+			said_next->nic_offload_dev = NULL;
+			ret = kernel_ops->add_sa(said_next, replace);
+		}
+		if (!ret) {
 			/* scrub keys from memory */
 			memset(said_next->enckey, 0, said_next->enckeylen);
 			memset(said_next->authkey, 0, said_next->authkeylen);
diff --git a/programs/pluto/kernel.h b/programs/pluto/kernel.h
index 0b6b20c8c..d190bbbd2 100644
--- a/programs/pluto/kernel.h
+++ b/programs/pluto/kernel.h
@@ -117,8 +117,7 @@ struct kernel_sa {
 #ifdef HAVE_LABELED_IPSEC
 	struct xfrm_user_sec_ctx_ike *sec_ctx;
 #endif
-	bool nic_offload;
-	int nic_offload_ifindex;
+	const char *nic_offload_dev;
 
 	deltatime_t sa_lifetime; /* number of seconds until SA expires */
 	/* below two need to enabled and used, instead of getting passed */
@@ -191,6 +190,7 @@ struct kernel_ops {
 	void (*remove_orphaned_holds)(int transportproto,
 				      const ip_subnet *ours,
 				      const ip_subnet *his);
+	bool (*nic_offload_supported)(const struct kernel_sa *sa, const char* ifname);
 	bool (*add_sa)(const struct kernel_sa *sa, bool replace);
 	bool (*grp_sa)(const struct kernel_sa *sa_outer,
 		       const struct kernel_sa *sa_inner);
diff --git a/programs/pluto/kernel_netlink.c b/programs/pluto/kernel_netlink.c
index 2e26a899f..2d282fea1 100644
--- a/programs/pluto/kernel_netlink.c
+++ b/programs/pluto/kernel_netlink.c
@@ -38,8 +38,11 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #include <stdint.h>
 #include <linux/pfkeyv2.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
 #include <unistd.h>
 
 #include "kameipsec.h"
@@ -81,6 +84,9 @@
 /* Minimum priority number in SPD used by pluto. */
 #define MIN_SPD_PRIORITY 1024
 
+#define NIC_OFFLOAD_UNKNOWN -2
+#define NIC_OFFLOAD_UNSUPPORTED -1
+
 struct aead_alg {
 	int id;
 	int icvlen;
@@ -89,6 +95,7 @@ struct aead_alg {
 
 static int netlinkfd = NULL_FD;
 static int netlink_bcast_fd = NULL_FD;
+static int netlink_esp_hw_offload = NIC_OFFLOAD_UNKNOWN;
 
 #define NE(x) { x, #x }	/* Name Entry -- shorthand for sparse_names */
 
@@ -890,6 +897,93 @@ static bool netlink_raw_eroute(const ip_address *this_host,
 	return ok;
 }
 
+static void netlink_find_offload_feature(const char *ifname)
+{
+	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 = NIC_OFFLOAD_UNSUPPORTED;
+
+	/* Determine number of device-features */
+	sset_info = alloc_bytes(sizeof(*sset_info) + sizeof(sset_info->data[0]), "ethtool_sset_info");
+	sset_info->cmd = ETHTOOL_GSSET_INFO;
+	sset_info->sset_mask = 1ULL << ETH_SS_FEATURES;
+	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)sset_info;
+	err = ioctl(netlinkfd, SIOCETHTOOL, &ifr);
+	if (err)
+		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 = alloc_bytes(sizeof(*cmd) + ETH_GSTRING_LEN * sset_len, "ethtool_gstrings");
+	cmd->cmd = ETHTOOL_GSTRINGS;
+	cmd->string_set = ETH_SS_FEATURES;
+	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)cmd;
+	err = ioctl(netlinkfd, 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 (strncmp(str, "esp-hw-offload", ETH_GSTRING_LEN) == 0)
+			break;
+		str += ETH_GSTRING_LEN;
+	}
+	if (i >= cmd->len)
+		goto out;
+
+	netlink_esp_hw_offload = i;
+
+out:
+	if (sset_info)
+		pfree(sset_info);
+	if (cmd)
+		pfree(cmd);
+}
+
+static bool netlink_sa_offload_supported(const struct kernel_sa *sa UNUSED, const char *ifname)
+{
+	struct ethtool_gfeatures *cmd;
+	uint32_t feature_bit;
+	struct ifreq ifr;
+	int blocks;
+	bool ret = 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 == NIC_OFFLOAD_UNKNOWN)
+		netlink_find_offload_feature(ifname);
+
+	if (netlink_esp_hw_offload == NIC_OFFLOAD_UNSUPPORTED)
+		return false;
+
+	/* Feature is supported by kernel. Query device features */
+	blocks = (netlink_esp_hw_offload + 31) / 32;
+	feature_bit = 1 << (netlink_esp_hw_offload % 31);
+
+	cmd = alloc_bytes(sizeof(*cmd) + sizeof(cmd->features[0]) * blocks, "ethtool_gfeatures");
+	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)cmd;
+	cmd->cmd = ETHTOOL_GFEATURES;
+	cmd->size = blocks;
+	if (ioctl(netlinkfd, SIOCETHTOOL, &ifr) == 0)
+		ret = !!(cmd->features[blocks-1].active & feature_bit);
+
+	pfree(cmd);
+	return ret;
+}
+
 /*
  * netlink_add_sa - Add an SA into the kernel SPDB via netlink
  *
@@ -1257,13 +1351,13 @@ static bool netlink_add_sa(const struct kernel_sa *sa, bool replace)
 		attr = (struct rtattr *)((char *)attr + attr->rta_len);
 	}
 
-	if (sa->nic_offload) {
+	if (sa->nic_offload_dev != NULL) {
 		struct xfrm_user_offload xuo;
 
 		xuo.flags |= sa->inbound ? XFRM_OFFLOAD_INBOUND : 0;
 		if (sa->src->u.v4.sin_family == AF_INET6)
 			xuo.flags |= XFRM_OFFLOAD_IPV6;
-		xuo.ifindex = sa->nic_offload_ifindex;
+		xuo.ifindex = if_nametoindex(sa->nic_offload_dev);
 
 		attr->rta_type = XFRMA_OFFLOAD_DEV;
 		attr->rta_len = RTA_LENGTH(sizeof(xuo));
@@ -2349,6 +2443,7 @@ const struct kernel_ops netkey_kernel_ops = {
 	.pfkey_register_response = pfkey_register_response,
 	.process_msg = netlink_process_msg,
 	.raw_eroute = netlink_raw_eroute,
+	.nic_offload_supported = netlink_sa_offload_supported,
 	.add_sa = netlink_add_sa,
 	.del_sa = netlink_del_sa,
 	.get_sa = netlink_get_sa,
-- 
2.11.0



More information about the Swan-dev mailing list