Discussion:
[strongSwan-dev] [hw-offload-auto-mode 1/3] ipsec-types: Create new enum hw_offload_t
a***@mellanox.com
2018-03-05 16:26:01 UTC
Permalink
From: Adi Nissim <***@mellanox.com>

Add the new enum in order to add HW offload auto mode.

Signed-off-by: Adi Nissim <***@mellanox.com>
Reviewed-by: Aviv Heller <***@mellanox.com>
---
src/libstrongswan/ipsec/ipsec_types.c | 6 ++++++
src/libstrongswan/ipsec/ipsec_types.h | 15 +++++++++++++++
2 files changed, 21 insertions(+)

diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c
index c992eb5..0e94071 100644
--- a/src/libstrongswan/ipsec/ipsec_types.c
+++ b/src/libstrongswan/ipsec/ipsec_types.c
@@ -37,6 +37,12 @@ ENUM(ipcomp_transform_names, IPCOMP_NONE, IPCOMP_LZJH,
"IPCOMP_LZJH"
);

+ENUM(hw_offload_names, HW_OFFLOAD_NO, HW_OFFLOAD_AUTO,
+ "no",
+ "yes",
+ "auto",
+);
+
/*
* See header
*/
diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h
index 1db78ba..fef9f83 100644
--- a/src/libstrongswan/ipsec/ipsec_types.h
+++ b/src/libstrongswan/ipsec/ipsec_types.h
@@ -26,6 +26,7 @@ typedef enum policy_dir_t policy_dir_t;
typedef enum policy_type_t policy_type_t;
typedef enum policy_priority_t policy_priority_t;
typedef enum ipcomp_transform_t ipcomp_transform_t;
+typedef enum hw_offload_t hw_offload_t;
typedef struct ipsec_sa_cfg_t ipsec_sa_cfg_t;
typedef struct lifetime_cfg_t lifetime_cfg_t;
typedef struct mark_t mark_t;
@@ -117,6 +118,20 @@ enum ipcomp_transform_t {
extern enum_name_t *ipcomp_transform_names;

/**
+ * HW off-load mode options
+ */
+enum hw_offload_t {
+ HW_OFFLOAD_NO = 0,
+ HW_OFFLOAD_YES = 1,
+ HW_OFFLOAD_AUTO = 2,
+};
+
+/**
+ * enum names for hw_offload_t.
+ */
+extern enum_name_t *hw_offload_names;
+
+/**
* This struct contains details about IPsec SA(s) tied to a policy.
*/
struct ipsec_sa_cfg_t {
--
1.8.3.1
a***@mellanox.com
2018-03-05 16:26:02 UTC
Permalink
From: Adi Nissim <***@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 <***@mellanox.com>
Reviewed-by: Aviv Heller <***@mellanox.com>
---
.../plugins/kernel_netlink/kernel_netlink_ipsec.c | 262 +++++++++++++++++++--
1 file changed, 239 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..9ca431c 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -21,12 +21,15 @@
#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 +240,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 +1308,222 @@ 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");
+ return FALSE;
+ }
+
+ /* Feature is supported by kernel. Query device features */
+ cmd = malloc(sizeof(cmd->features[0]) *
+ netlink_esp_hw_offload.total_blocks);
+ if (cmd == NULL)
+ {
+ return FALSE;
+ }
+
+ 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;
+ }
+
+ 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(cmd);
+ if (ret == FALSE)
+ {
+ DBG1(DBG_KNL, "HW offload is not supported by device");
+ }
+ 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 +1884,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
a***@mellanox.com
2018-03-05 16:26:03 UTC
Permalink
From: Adi Nissim <***@mellanox.com>

Until now the configuration avaliable to user for HW offload were:
hw_offload = no
hw_offload = yes

With this commit users will be able to configure auto mode using:
hw_offload = auto.

Signed-off-by: Adi Nissim <***@mellanox.com>
Reviewed-by: Aviv Heller <***@mellanox.com>
---
src/libcharon/config/child_cfg.c | 14 +++++++++++++
src/libcharon/config/child_cfg.h | 16 ++++++++++-----
src/libcharon/kernel/kernel_ipsec.h | 6 ++++--
src/libcharon/plugins/vici/vici_config.c | 34 ++++++++++++++++++++++----------
src/libcharon/sa/child_sa.c | 2 +-
5 files changed, 54 insertions(+), 18 deletions(-)

diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c
index ec2a124..db16092 100644
--- a/src/libcharon/config/child_cfg.c
+++ b/src/libcharon/config/child_cfg.c
@@ -142,6 +142,11 @@ struct private_child_cfg_t {
* anti-replay window size
*/
uint32_t replay_window;
+
+ /**
+ * HW offload mode
+ */
+ hw_offload_t hw_offload;
};

METHOD(child_cfg_t, get_name, char*,
@@ -461,6 +466,13 @@ METHOD(child_cfg_t, get_start_action, action_t,
return this->start_action;
}

+
+METHOD(child_cfg_t, get_hw_offload, hw_offload_t,
+ private_child_cfg_t *this)
+{
+ return this->hw_offload;
+}
+
METHOD(child_cfg_t, get_dpd_action, action_t,
private_child_cfg_t *this)
{
@@ -646,6 +658,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.equals = _equals,
.get_ref = _get_ref,
.destroy = _destroy,
+ .get_hw_offload = _get_hw_offload,
},
.name = strdup(name),
.options = data->options,
@@ -668,6 +681,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.other_ts = linked_list_create(),
.replay_window = lib->settings->get_int(lib->settings,
"%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
+ .hw_offload = data->hw_offload,
);

return &this->public;
diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h
index e2834fa..49af06c 100644
--- a/src/libcharon/config/child_cfg.h
+++ b/src/libcharon/config/child_cfg.h
@@ -183,6 +183,13 @@ struct child_cfg_t {
action_t (*get_dpd_action) (child_cfg_t *this);

/**
+ * Get the HW offload mode to use for the CHILD_SA.
+ *
+ * @return hw offload mode
+ */
+ hw_offload_t (*get_hw_offload) (child_cfg_t *this);
+
+ /**
* Action to take if CHILD_SA gets closed.
*
* @return close action
@@ -305,14 +312,11 @@ enum child_cfg_option_t {
/** Install outbound FWD IPsec policies to bypass drop policies */
OPT_FWD_OUT_POLICIES = (1<<4),

- /** Enable hardware offload, if supported by the IPsec backend */
- OPT_HW_OFFLOAD = (1<<5),
-
/** Force 96-bit truncation for SHA-256 */
- OPT_SHA256_96 = (1<<6),
+ OPT_SHA256_96 = (1<<5),

/** Set mark on inbound SAs */
- OPT_MARK_IN_SA = (1<<7),
+ OPT_MARK_IN_SA = (1<<6),
};

/**
@@ -347,6 +351,8 @@ struct child_cfg_create_t {
action_t close_action;
/** updown script to execute on up/down event (cloned) */
char *updown;
+ /** HW offload mode : no/yes/auto */
+ hw_offload_t hw_offload;
};

/**
diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h
index b753040..943185f 100644
--- a/src/libcharon/kernel/kernel_ipsec.h
+++ b/src/libcharon/kernel/kernel_ipsec.h
@@ -91,8 +91,10 @@ struct kernel_ipsec_add_sa_t {
uint16_t cpi;
/** TRUE to enable UDP encapsulation for NAT traversal */
bool encap;
- /** TRUE to enable hardware offloading if available */
- bool hw_offload;
+ /** no(without offload)/yes(activate offload)/
+ * auto(if offload is supported activate it)
+ */
+ hw_offload_t hw_offload;
/** TRUE to use Extended Sequence Numbers */
bool esn;
/** TRUE if initiator of the exchange creating the SA */
diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c
index e0e2955..02fd291 100644
--- a/src/libcharon/plugins/vici/vici_config.c
+++ b/src/libcharon/plugins/vici/vici_config.c
@@ -533,7 +533,7 @@ static void log_child_data(child_data_t *data, char *name)
DBG2(DBG_CFG, " proposals = %#P", data->proposals);
DBG2(DBG_CFG, " local_ts = %#R", data->local_ts);
DBG2(DBG_CFG, " remote_ts = %#R", data->remote_ts);
- DBG2(DBG_CFG, " hw_offload = %u", has_opt(OPT_HW_OFFLOAD));
+ DBG2(DBG_CFG, " hw_offload = %N", hw_offload_names, cfg->hw_offload);
DBG2(DBG_CFG, " sha256_96 = %u", has_opt(OPT_SHA256_96));
}

@@ -892,14 +892,6 @@ CALLBACK(parse_opt_ipcomp, bool,
return parse_option(out, OPT_IPCOMP, v);
}

-/**
- * Parse OPT_HW_OFFLOAD option
- */
-CALLBACK(parse_opt_hw_offl, bool,
- child_cfg_option_t *out, chunk_t v)
-{
- return parse_option(out, OPT_HW_OFFLOAD, v);
-}

/**
* Parse OPT_SHA256_96 option
@@ -944,6 +936,28 @@ CALLBACK(parse_action, bool,
}

/**
+ * Parse an hw_offload_t
+ */
+CALLBACK(parse_hw_offload, bool,
+ action_t *out, chunk_t v)
+{
+ enum_map_t map[] = {
+ { "no", HW_OFFLOAD_NO },
+ { "yes", HW_OFFLOAD_YES },
+ { "auto", HW_OFFLOAD_AUTO },
+ };
+ int d;
+
+ if (parse_map(map, countof(map), &d, v))
+ {
+ *out = d;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
* Parse a uint32_t with the given base
*/
static bool parse_uint32_base(uint32_t *out, chunk_t v, int base)
@@ -1578,7 +1592,7 @@ CALLBACK(child_kv, bool,
{ "tfc_padding", parse_tfc, &child->cfg.tfc },
{ "priority", parse_uint32, &child->cfg.priority },
{ "interface", parse_string, &child->cfg.interface },
- { "hw_offload", parse_opt_hw_offl, &child->cfg.options },
+ { "hw_offload", parse_hw_offload, &child->cfg.hw_offload },
{ "sha256_96", parse_opt_sha256_96,&child->cfg.options },
};

diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 91da4d3..af1c801 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -888,7 +888,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr,
.ipcomp = this->ipcomp,
.cpi = cpi,
.encap = this->encap,
- .hw_offload = this->config->has_option(this->config, OPT_HW_OFFLOAD),
+ .hw_offload = this->config->get_hw_offload(this->config),
.esn = esn,
.initiator = initiator,
.inbound = inbound,
--
1.8.3.1

Loading...