kernel-netlink: Check for offloading support in constructor
authorThomas Egerer <thomas.egerer@secunet.com>
Wed, 9 Oct 2019 15:16:29 +0000 (17:16 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 26 Nov 2019 10:00:28 +0000 (11:00 +0100)
This avoids races that could potentially occur when doing the check during
SA installation.

Signed-off-by: Thomas Egerer <thomas.egerer@secunet.com>
conf/plugins/kernel-netlink.opt
src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c

index 0e368ca..1ca5a42 100644 (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.
 
index 27bb379..4465d41 100644 (file)
@@ -1347,39 +1347,34 @@ static bool add_uint32(struct nlmsghdr *hdr, int buflen,
 #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,23 +1439,16 @@ static bool netlink_detect_offload(const char *ifname)
        int block;
        bool ret = FALSE;
 
-       query_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
-       if (query_socket < 0)
+       if (!netlink_hw_offload.supported)
        {
+               DBG1(DBG_KNL, "HW offload is not supported by kernel");
                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)
+       query_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM);
+       if (query_socket < 0)
        {
-               DBG1(DBG_KNL, "HW offload is not supported by kernel");
-               goto out;
+               return FALSE;
        }
 
        /* feature is supported by kernel, query device features */
@@ -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;
 }