kernel-netlink: Check for offloading support in constructor

This avoids races that could potentially occur when doing the check during
SA installation.

Signed-off-by: Thomas Egerer <thomas.egerer@secunet.com>
This commit is contained in:
Thomas Egerer 2019-10-09 17:16:29 +02:00 committed by Tobias Brunner
parent 29b4b2e8e2
commit a605452c03
2 changed files with 44 additions and 41 deletions

View File

@ -18,6 +18,16 @@ charon.plugins.kernel-netlink.fwmark =
inverts the meaning (i.e. the rule only applies to packets that don't match
the mark).
charon.plugins.kernel-netlink.hw_offload_feature_interface = lo
Interface to be used to find hardware offload feature flag on.
If the kernel supports hardware offloading, the plugin needs to find the
feature flag which represents hardware offloading support for network
devices. Using the loopback device for this purpose is usually fine, since
it should always be present. For rare cases in which the loopback device
cannot be used to obtain the appropriate feature flag, this option can
be used to specify an alternative interface for offload feature detection.
charon.plugins.kernel-netlink.mss = 0
MSS to set on installed routes, 0 to disable.

View File

@ -1346,40 +1346,35 @@ static bool add_uint32(struct nlmsghdr *hdr, int buflen,
* ETHTOOL_GFEATURES since 2.6.39, so check for the latter */
#ifdef ETHTOOL_GFEATURES
/**
* IPsec HW offload state in kernel
*/
typedef enum {
NL_OFFLOAD_UNKNOWN,
NL_OFFLOAD_UNSUPPORTED,
NL_OFFLOAD_SUPPORTED
} nl_offload_state_t;
/**
* Global metadata used for IPsec HW offload
*/
static struct {
/** determined HW offload support */
bool supported;
/** bit in feature set */
u_int bit;
/** total number of device feature blocks */
u_int total_blocks;
/** determined HW offload state */
nl_offload_state_t state;
} netlink_hw_offload;
/**
* Check if kernel supports HW offload
* Check if kernel supports HW offload and determine feature flag
*/
static void netlink_find_offload_feature(const char *ifname, int query_socket)
static void netlink_find_offload_feature(const char *ifname)
{
struct ethtool_sset_info *sset_info;
struct ethtool_gstrings *cmd = NULL;
struct ifreq ifr;
uint32_t sset_len, i;
char *str;
int err;
int err, query_socket;
netlink_hw_offload.state = NL_OFFLOAD_UNSUPPORTED;
query_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
if (query_socket < 0)
{
return;
}
/* determine number of device features */
INIT_EXTRA(sset_info, sizeof(uint32_t),
@ -1418,9 +1413,9 @@ static void netlink_find_offload_feature(const char *ifname, int query_socket)
{
if (strneq(str, "esp-hw-offload", ETH_GSTRING_LEN))
{
netlink_hw_offload.supported = TRUE;
netlink_hw_offload.bit = i;
netlink_hw_offload.total_blocks = (sset_len + 31) / 32;
netlink_hw_offload.state = NL_OFFLOAD_SUPPORTED;
break;
}
str += ETH_GSTRING_LEN;
@ -1429,6 +1424,7 @@ static void netlink_find_offload_feature(const char *ifname, int query_socket)
out:
free(sset_info);
free(cmd);
close(query_socket);
}
/**
@ -1443,25 +1439,18 @@ static bool netlink_detect_offload(const char *ifname)
int block;
bool ret = FALSE;
if (!netlink_hw_offload.supported)
{
DBG1(DBG_KNL, "HW offload is not supported by kernel");
return FALSE;
}
query_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
if (query_socket < 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_hw_offload.state == NL_OFFLOAD_UNKNOWN)
{
netlink_find_offload_feature(ifname, query_socket);
}
if (netlink_hw_offload.state == NL_OFFLOAD_UNSUPPORTED)
{
DBG1(DBG_KNL, "HW offload is not supported by kernel");
goto out;
}
/* feature is supported by kernel, query device features */
INIT_EXTRA(cmd, sizeof(cmd->features[0]) * netlink_hw_offload.total_blocks,
.cmd = ETHTOOL_GFEATURES,
@ -1471,31 +1460,31 @@ static bool netlink_detect_offload(const char *ifname)
ifr.ifr_name[IFNAMSIZ-1] = '\0';
ifr.ifr_data = (void*)cmd;
if (ioctl(query_socket, SIOCETHTOOL, &ifr))
if (!ioctl(query_socket, SIOCETHTOOL, &ifr))
{
goto out_free;
block = netlink_hw_offload.bit / 32;
feature_bit = 1U << (netlink_hw_offload.bit % 32);
if (cmd->features[block].active & feature_bit)
{
ret = TRUE;
}
}
block = netlink_hw_offload.bit / 32;
feature_bit = 1U << (netlink_hw_offload.bit % 32);
if (cmd->features[block].active & feature_bit)
{
ret = TRUE;
}
out_free:
free(cmd);
if (!ret)
{
DBG1(DBG_KNL, "HW offload is not supported by device");
}
out:
free(cmd);
close(query_socket);
return ret;
}
#else
static void netlink_find_offload_feature(const char *ifname)
{
}
static bool netlink_detect_offload(const char *ifname)
{
return FALSE;
@ -3698,5 +3687,9 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
(watcher_cb_t)receive_events, this);
}
netlink_find_offload_feature(lib->settings->get_str(lib->settings,
"%s.hw_offload_feature_interface", "lo",
lib->ns));
return &this->public;
}