From: Ilan Tayari <il...@mellanox.com>

Detect kernel capability when adding the first interface.
Ethtool IOCTL requires a valid device, so this cannot be done
before that.

Detect per-device capability using ethtool features when
enumerating devices.

Signed-off-by: Ilan Tayari <il...@mellanox.com>
---
 programs/pluto/kernel.c         |  31 ++++++++++--
 programs/pluto/kernel.h         |   3 +-
 programs/pluto/kernel_netlink.c | 101 +++++++++++++++++++++++++++++++++++++++-
 programs/pluto/server.h         |   7 +++
 4 files changed, 134 insertions(+), 8 deletions(-)

diff --git a/programs/pluto/kernel.c b/programs/pluto/kernel.c
index 9b14caf21..1297db7d9 100644
--- a/programs/pluto/kernel.c
+++ b/programs/pluto/kernel.c
@@ -1714,6 +1714,23 @@ static bool del_spi(ipsec_spi_t spi, int proto,
        return kernel_ops->del_sa(&sa);
 }
 
+static void setup_esp_nic_offload(struct kernel_sa *sa, struct connection *c,
+               bool *nic_offload_fallback)
+{
+       if (c->nic_offload == nic_offload_no)
+               return;
+       if (!c->interface || !c->interface->ip_dev ||
+               !c->interface->ip_dev->id_rname)
+               return;
+
+       if (c->nic_offload == nic_offload_auto) {
+               if (c->interface->ip_dev->id_nic_offload != IFNO_SUPPORTED)
+                       return;
+               *nic_offload_fallback = TRUE;
+       }
+       sa->nic_offload_dev = c->interface->ip_dev->id_rname;
+}
+
 /*
  * Set up one direction of the SA bundle
  */
@@ -1732,6 +1749,8 @@ static bool setup_half_ipsec_sa(struct state *st, bool 
inbound)
        bool incoming_ref_set = FALSE;
        IPsecSAref_t refhim = st->st_refhim;
        IPsecSAref_t new_refhim = IPSEC_SAREF_NULL;
+       bool nic_offload_fallback = FALSE;
+       bool ret;
 
        /* SPIs, saved for spigrouping or undoing, if necessary */
        struct kernel_sa said[EM_MAXRELSPIS];
@@ -1794,9 +1813,6 @@ static bool setup_half_ipsec_sa(struct state *st, bool 
inbound)
        said_boilerplate.transport_proto = c->spd.this.protocol;
        said_boilerplate.sa_lifetime = c->sa_ipsec_life_seconds;
        said_boilerplate.outif = -1;
-       said_boilerplate.nic_offload = c->nic_offload;
-       if (c->nic_offload && c->interface != NULL)
-               said_boilerplate.nic_offload_ifindex = 
if_nametoindex(c->interface->ip_dev->id_rname);
 
 #ifdef HAVE_LABELED_IPSEC
        said_boilerplate.sec_ctx = st->sec_ctx;
@@ -2286,8 +2302,15 @@ static bool setup_half_ipsec_sa(struct state *st, bool 
inbound)
                        said_next->ref = refhim;
                        outgoing_ref_set = TRUE;
                }
+               setup_esp_nic_offload(said_next, c, &nic_offload_fallback);
 
-               if (!kernel_ops->add_sa(said_next, replace)) {
+               ret = kernel_ops->add_sa(said_next, replace);
+               if (!ret && said_next->nic_offload_dev && nic_offload_fallback) 
{
+                       /* Fallback to non-nic-offload crypto */
+                       said_next->nic_offload_dev = NULL;
+                       ret = kernel_ops->add_sa(said_next, replace);
+               }
+               if (!ret) {
                        /* scrub keys from memory */
                        memset(said_next->enckey, 0, said_next->enckeylen);
                        memset(said_next->authkey, 0, said_next->authkeylen);
diff --git a/programs/pluto/kernel.h b/programs/pluto/kernel.h
index 0b6b20c8c..bf519fe70 100644
--- a/programs/pluto/kernel.h
+++ b/programs/pluto/kernel.h
@@ -117,8 +117,7 @@ struct kernel_sa {
 #ifdef HAVE_LABELED_IPSEC
        struct xfrm_user_sec_ctx_ike *sec_ctx;
 #endif
-       bool nic_offload;
-       int nic_offload_ifindex;
+       const char *nic_offload_dev;
 
        deltatime_t sa_lifetime; /* number of seconds until SA expires */
        /* below two need to enabled and used, instead of getting passed */
diff --git a/programs/pluto/kernel_netlink.c b/programs/pluto/kernel_netlink.c
index 13ad2e6f3..1c5816721 100644
--- a/programs/pluto/kernel_netlink.c
+++ b/programs/pluto/kernel_netlink.c
@@ -38,8 +38,11 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #include <stdint.h>
 #include <linux/pfkeyv2.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
 #include <unistd.h>
 
 #include "kameipsec.h"
@@ -81,6 +84,9 @@
 /* Minimum priority number in SPD used by pluto. */
 #define MIN_SPD_PRIORITY 1024
 
+#define NIC_OFFLOAD_UNKNOWN -2
+#define NIC_OFFLOAD_UNSUPPORTED -1
+
 struct aead_alg {
        int id;
        int icvlen;
@@ -89,6 +95,7 @@ struct aead_alg {
 
 static int netlinkfd = NULL_FD;
 static int netlink_bcast_fd = NULL_FD;
+static int netlink_esp_hw_offload = NIC_OFFLOAD_UNKNOWN;
 
 #define NE(x) { x, #x }        /* Name Entry -- shorthand for sparse_names */
 
@@ -891,6 +898,95 @@ static bool netlink_raw_eroute(const ip_address *this_host,
        return ok;
 }
 
+static void netlink_find_offload_feature(const char *ifname)
+{
+       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 = NIC_OFFLOAD_UNSUPPORTED;
+
+       /* Determine number of device-features */
+       sset_info = alloc_bytes(sizeof(*sset_info) + 
sizeof(sset_info->data[0]), "ethtool_sset_info");
+       sset_info->cmd = ETHTOOL_GSSET_INFO;
+       sset_info->sset_mask = 1ULL << ETH_SS_FEATURES;
+       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_data = (void *)sset_info;
+       err = ioctl(netlinkfd, SIOCETHTOOL, &ifr);
+       if (err)
+               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 = alloc_bytes(sizeof(*cmd) + ETH_GSTRING_LEN * sset_len, 
"ethtool_gstrings");
+       cmd->cmd = ETHTOOL_GSTRINGS;
+       cmd->string_set = ETH_SS_FEATURES;
+       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_data = (void *)cmd;
+       err = ioctl(netlinkfd, 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 (strncmp(str, "esp-hw-offload", ETH_GSTRING_LEN) == 0)
+                       break;
+               str += ETH_GSTRING_LEN;
+       }
+       if (i >= cmd->len)
+               goto out;
+
+       netlink_esp_hw_offload = i;
+
+out:
+       if (sset_info)
+               pfree(sset_info);
+       if (cmd)
+               pfree(cmd);
+}
+
+static enum iface_nic_offload netlink_detect_offload(const char *ifname)
+{
+       enum iface_nic_offload ret = IFNO_UNSUPPORTED;
+       struct ethtool_gfeatures *cmd;
+       uint32_t feature_bit;
+       struct ifreq ifr;
+       int blocks;
+
+       /*
+        * 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 == NIC_OFFLOAD_UNKNOWN)
+               netlink_find_offload_feature(ifname);
+
+       if (netlink_esp_hw_offload == NIC_OFFLOAD_UNSUPPORTED)
+               return ret;
+
+       /* Feature is supported by kernel. Query device features */
+       blocks = (netlink_esp_hw_offload + 31) / 32;
+       feature_bit = 1 << (netlink_esp_hw_offload % 31);
+
+       cmd = alloc_bytes(sizeof(*cmd) + sizeof(cmd->features[0]) * blocks, 
"ethtool_gfeatures");
+       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_data = (void *)cmd;
+       cmd->cmd = ETHTOOL_GFEATURES;
+       cmd->size = blocks;
+       if ((ioctl(netlinkfd, SIOCETHTOOL, &ifr) == 0) &&
+               (cmd->features[blocks-1].active & feature_bit))
+               ret = IFNO_SUPPORTED;
+
+       pfree(cmd);
+       return ret;
+}
+
 /*
  * netlink_add_sa - Add an SA into the kernel SPDB via netlink
  *
@@ -1258,13 +1354,13 @@ static bool netlink_add_sa(const struct kernel_sa *sa, 
bool replace)
                attr = (struct rtattr *)((char *)attr + attr->rta_len);
        }
 
-       if (sa->nic_offload) {
+       if (sa->nic_offload_dev) {
                struct xfrm_user_offload xuo = { 0, 0 };
 
                xuo.flags |= sa->inbound ? XFRM_OFFLOAD_INBOUND : 0;
                if (sa->src->u.v4.sin_family == AF_INET6)
                        xuo.flags |= XFRM_OFFLOAD_IPV6;
-               xuo.ifindex = sa->nic_offload_ifindex;
+               xuo.ifindex = if_nametoindex(sa->nic_offload_dev);
 
                attr->rta_type = XFRMA_OFFLOAD_DEV;
                attr->rta_len = RTA_LENGTH(sizeof(xuo));
@@ -2181,6 +2277,7 @@ static void netlink_process_raw_ifaces(struct raw_iface 
*rifaces)
                                id->id_vname = clone_str(v->name,
                                                        "virtual device name 
netlink");
                                id->id_count++;
+                               id->id_nic_offload = 
netlink_detect_offload(ifp->name);
 
                                q->ip_addr = ifp->addr;
                                q->fd = fd;
diff --git a/programs/pluto/server.h b/programs/pluto/server.h
index 31515e8a4..ba18f45aa 100644
--- a/programs/pluto/server.h
+++ b/programs/pluto/server.h
@@ -40,6 +40,12 @@ extern unsigned int pluto_max_halfopen; /* Max allowed 
half-open IKE SA's before
 extern unsigned int pluto_ddos_threshold; /* Max incoming IKE before 
activating DCOOKIES */
 extern deltatime_t pluto_shunt_lifetime; /* lifetime before we cleanup bare 
shunts (for OE) */
 
+/* device_nic_offload: NIC offload capability of an interface */
+enum iface_nic_offload {
+       IFNO_UNSUPPORTED,
+       IFNO_SUPPORTED,
+};
+
 /* interface: a terminal point for IKE traffic, IPsec transport mode
  * and IPsec tunnels.
  * Essentially:
@@ -57,6 +63,7 @@ struct iface_dev {
        int id_count;
        char *id_vname; /* virtual (ipsec) device name */
        char *id_rname; /* real device name */
+       enum iface_nic_offload id_nic_offload;
 };
 
 struct iface_port {
-- 
2.11.0

_______________________________________________
Swan-dev mailing list
Swan-dev@lists.libreswan.org
https://lists.libreswan.org/mailman/listinfo/swan-dev

Reply via email to