kernel-netlink: Cleanup and fix some HW offload code issues

Besides some style issues there were some incorrect allocations
for ethtool requests.
This commit is contained in:
Tobias Brunner
2018-03-21 10:29:57 +01:00
parent 338cc5812e
commit ee26f7156f
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2006-2017 Tobias Brunner * Copyright (C) 2006-2018 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2008-2016 Andreas Steffen * Copyright (C) 2008-2016 Andreas Steffen
* Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
@@ -261,19 +261,25 @@ static kernel_algorithm_t compression_algs[] = {
}; };
/** /**
* IPsec offload * IPsec HW offload state in kernel
*/ */
enum nic_offload_state { typedef enum {
NIC_OFFLOAD_UNKNOWN, NL_OFFLOAD_UNKNOWN,
NIC_OFFLOAD_UNSUPPORTED, NL_OFFLOAD_UNSUPPORTED,
NIC_OFFLOAD_SUPPORTED NL_OFFLOAD_SUPPORTED
}; } nl_offload_state_t;
/**
* Global metadata used for IPsec HW offload
*/
static struct { static struct {
unsigned int bit; /** bit in feature set */
unsigned int total_blocks; u_int bit;
enum nic_offload_state state; /** total number of device feature blocks */
} netlink_esp_hw_offload; u_int total_blocks;
/** determined HW offload state */
nl_offload_state_t state;
} netlink_hw_offload;
/** /**
* Look up a kernel algorithm name and its key size * Look up a kernel algorithm name and its key size
@@ -1330,89 +1336,65 @@ static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark)
} }
/** /**
* check if kernel supported HW offload * Check if kernel supports HW offload
*/ */
static void netlink_find_offload_feature(const char *ifname, int fd_for_socket) static void netlink_find_offload_feature(const char *ifname, int query_socket)
{ {
struct ethtool_sset_info *sset_info = NULL; struct ethtool_sset_info *sset_info;
struct ethtool_gstrings *cmd = NULL; struct ethtool_gstrings *cmd = NULL;
struct ifreq ifr; struct ifreq ifr;
uint32_t sset_len, i; uint32_t sset_len, i;
char *str; char *str;
int err; int err;
netlink_esp_hw_offload.state = NIC_OFFLOAD_UNSUPPORTED; netlink_hw_offload.state = NL_OFFLOAD_UNSUPPORTED;
/* Determine number of device-features */ /* determine number of device features */
sset_info = malloc(sizeof(*sset_info)); INIT_EXTRA(sset_info, sizeof(uint32_t),
if (sset_info == NULL) .cmd = ETHTOOL_GSSET_INFO,
{ .sset_mask = 1ULL << ETH_SS_FEATURES,
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); strcpy(ifr.ifr_name, ifname);
ifr.ifr_data = (void*)sset_info;
ifr.ifr_data = (void *)sset_info; err = ioctl(query_socket, SIOCETHTOOL, &ifr);
if (err || sset_info->sset_mask != 1ULL << ETH_SS_FEATURES)
err = ioctl(fd_for_socket, SIOCETHTOOL, &ifr);
if (err != 0)
{
goto out;
}
if (sset_info->sset_mask != 1ULL << ETH_SS_FEATURES)
{ {
goto out; goto out;
} }
sset_len = sset_info->data[0]; sset_len = sset_info->data[0];
/* Retrieve names of device-features */ /* retrieve names of device features */
cmd = malloc(sizeof(*cmd) + ETH_GSTRING_LEN * sset_len); INIT_EXTRA(cmd, ETH_GSTRING_LEN * sset_len,
if (cmd == NULL) .cmd = ETHTOOL_GSTRINGS,
{ .string_set = ETH_SS_FEATURES,
goto out; );
}
memset(cmd, 0, sizeof(*cmd));
cmd->cmd = ETHTOOL_GSTRINGS;
cmd->string_set = ETH_SS_FEATURES;
strcpy(ifr.ifr_name, ifname); strcpy(ifr.ifr_name, ifname);
ifr.ifr_data = (void *)cmd; ifr.ifr_data = (void*)cmd;
err = ioctl(fd_for_socket, SIOCETHTOOL, &ifr);
err = ioctl(query_socket, SIOCETHTOOL, &ifr);
if (err) if (err)
{ {
goto out; goto out;
} }
/* Look for the ESP_HW feature bit */ /* look for the ESP_HW feature bit */
str = (char *)cmd->data; str = (char*)cmd->data;
for (i = 0; i < cmd->len; i++) for (i = 0; i < cmd->len; i++)
{ {
if (strneq(str, "esp-hw-offload", ETH_GSTRING_LEN) == 1) if (strneq(str, "esp-hw-offload", ETH_GSTRING_LEN))
{
netlink_hw_offload.bit = i;
netlink_hw_offload.total_blocks = (sset_len + 31) / 32;
netlink_hw_offload.state = NL_OFFLOAD_SUPPORTED;
break; break;
}
str += ETH_GSTRING_LEN; 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: out:
if (sset_info != NULL) free(sset_info);
{ free(cmd);
free(sset_info);
}
if (cmd != NULL)
{
free(cmd);
}
} }
/** /**
@@ -1423,53 +1405,44 @@ static bool netlink_detect_offload(const char *ifname)
struct ethtool_gfeatures *cmd; struct ethtool_gfeatures *cmd;
uint32_t feature_bit; uint32_t feature_bit;
struct ifreq ifr; struct ifreq ifr;
bool ret = FALSE; int query_socket;
int nl_send_fd;
int block; int block;
bool ret = FALSE;
nl_send_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM); query_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
if (query_socket < 0)
if (nl_send_fd < 0)
{ {
return FALSE; return FALSE;
} }
/* /* kernel requires a real interface in order to query the kernel-wide
* Kernel requires a real interface in order to query the kernel-wide
* capability, so we do it here on first invocation. * capability, so we do it here on first invocation.
*/ */
if (netlink_esp_hw_offload.state == NIC_OFFLOAD_UNKNOWN) if (netlink_hw_offload.state == NL_OFFLOAD_UNKNOWN)
{ {
netlink_find_offload_feature(ifname, nl_send_fd); netlink_find_offload_feature(ifname, query_socket);
} }
if (netlink_hw_offload.state == NL_OFFLOAD_UNSUPPORTED)
if (netlink_esp_hw_offload.state == NIC_OFFLOAD_UNSUPPORTED)
{ {
DBG1(DBG_KNL, "HW offload is not supported by kernel"); DBG1(DBG_KNL, "HW offload is not supported by kernel");
goto out; goto out;
} }
/* Feature is supported by kernel. Query device features */ /* feature is supported by kernel, query device features */
cmd = malloc(sizeof(cmd->features[0]) * INIT_EXTRA(cmd, sizeof(cmd->features[0]) * netlink_hw_offload.total_blocks,
netlink_esp_hw_offload.total_blocks); .cmd = ETHTOOL_GFEATURES,
if (cmd == NULL) .size = netlink_hw_offload.total_blocks,
{ );
goto out;
}
strcpy(ifr.ifr_name, ifname); strcpy(ifr.ifr_name, ifname);
ifr.ifr_data = (void*)cmd;
ifr.ifr_data = (void *)cmd; if (ioctl(query_socket, SIOCETHTOOL, &ifr))
cmd->cmd = ETHTOOL_GFEATURES;
cmd->size = netlink_esp_hw_offload.total_blocks;
if (ioctl(nl_send_fd, SIOCETHTOOL, &ifr))
{ {
goto out_free; goto out_free;
} }
block = netlink_esp_hw_offload.bit / 32; block = netlink_hw_offload.bit / 32;
feature_bit = 1U << (netlink_esp_hw_offload.bit % 32); feature_bit = 1U << (netlink_hw_offload.bit % 32);
if (cmd->features[block].active & feature_bit) if (cmd->features[block].active & feature_bit)
{ {
ret = TRUE; ret = TRUE;
@@ -1477,18 +1450,18 @@ static bool netlink_detect_offload(const char *ifname)
out_free: out_free:
free(cmd); free(cmd);
if (ret == FALSE) if (!ret)
{ {
DBG1(DBG_KNL, "HW offload is not supported by device"); DBG1(DBG_KNL, "HW offload is not supported by device");
} }
out: out:
close(nl_send_fd); close(query_socket);
return ret; return ret;
} }
/** /**
* There are 3 configuration options: * There are 3 HW offload configuration values:
* 1. HW_OFFLOAD_NO : Do not configure HW offload.. * 1. HW_OFFLOAD_NO : Do not configure HW offload.
* 2. HW_OFFLOAD_YES : Configure HW offload. * 2. HW_OFFLOAD_YES : Configure HW offload.
* Fail SA addition if offload is not supported. * Fail SA addition if offload is not supported.
* 3. HW_OFFLOAD_AUTO : Configure HW offload if supported by the kernel * 3. HW_OFFLOAD_AUTO : Configure HW offload if supported by the kernel
@@ -1496,41 +1469,40 @@ out:
* Do not fail SA addition otherwise. * Do not fail SA addition otherwise.
*/ */
static bool config_hw_offload(kernel_ipsec_sa_id_t *id, static bool config_hw_offload(kernel_ipsec_sa_id_t *id,
kernel_ipsec_add_sa_t *data, struct nlmsghdr *hdr, int buflen) 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; host_t *local = data->inbound ? id->dst : id->src;
struct xfrm_user_offload *offload; struct xfrm_user_offload *offload;
bool hw_offload_support; bool hw_offload_yes, ret = FALSE;
bool ret = FALSE;
char *ifname; char *ifname;
/* Do Ipsec configuration without offload */ /* do Ipsec configuration without offload */
if (data->hw_offload == HW_OFFLOAD_NO) if (data->hw_offload == HW_OFFLOAD_NO)
{ {
return TRUE; return TRUE;
} }
hw_offload_yes = (data->hw_offload == HW_OFFLOAD_YES);
if (!charon->kernel->get_interface(charon->kernel, local, &ifname)) if (!charon->kernel->get_interface(charon->kernel, local, &ifname))
{ {
return !cfg_hw_offload_is_yes; return !hw_offload_yes;
} }
/* Check if interface supports hw_offload */ /* check if interface supports hw_offload */
hw_offload_support = netlink_detect_offload(ifname); if (!netlink_detect_offload(ifname))
if (!hw_offload_support)
{ {
ret = !cfg_hw_offload_is_yes; ret = !hw_offload_yes;
goto out; goto out;
} }
/* Activate HW offload */ /* activate HW offload */
offload = netlink_reserve(hdr, buflen, offload = netlink_reserve(hdr, buflen,
XFRMA_OFFLOAD_DEV, sizeof(*offload)); XFRMA_OFFLOAD_DEV, sizeof(*offload));
if (!offload) if (!offload)
{ {
ret = !cfg_hw_offload_is_yes; ret = !hw_offload_yes;
goto out; goto out;
} }
offload->ifindex = if_nametoindex(ifname); offload->ifindex = if_nametoindex(ifname);
@@ -1908,7 +1880,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
sa->replay_window = data->replay_window; sa->replay_window = data->replay_window;
} }
DBG2(DBG_KNL, " HW offload mode = %N", hw_offload_names, data->hw_offload); DBG2(DBG_KNL, " HW offload: %N", hw_offload_names, data->hw_offload);
if (!config_hw_offload(id, data, hdr, sizeof(request))) if (!config_hw_offload(id, data, hdr, sizeof(request)))
{ {
DBG1(DBG_KNL, "failed to configure HW offload"); DBG1(DBG_KNL, "failed to configure HW offload");