kernel-netlink: Add new automatic hw_offload mode
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 <[email protected]>
Reviewed-by: Aviv Heller <[email protected]>
This commit is contained in:
committed by
Tobias Brunner
parent
aad9bd3613
commit
338cc5812e
@@ -17,16 +17,40 @@
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* 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>
|
||||
@@ -236,6 +260,21 @@ static kernel_algorithm_t compression_algs[] = {
|
||||
{IPCOMP_LZJH, "lzjh" },
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -1290,6 +1329,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 +1907,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
|
||||
data->replay_window);
|
||||
sa->replay_window = data->replay_window;
|
||||
}
|
||||
if (data->hw_offload)
|
||||
|
||||
DBG2(DBG_KNL, " HW offload mode = %N", hw_offload_names, data->hw_offload);
|
||||
if (!config_hw_offload(id, data, hdr, sizeof(request)))
|
||||
{
|
||||
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);
|
||||
}
|
||||
DBG1(DBG_KNL, "failed to configure HW offload");
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user