On Wed, Aug 02, 2017 at 06:22:28PM +0300, il...@mellanox.com wrote:
> 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)

libreswan code do not treat pointers as boolean; atleast try to avoid it.
the above line would something like :
c->interface != NULL || c->interface->ip_dev != NULL || 
c->interface->ip_dev->id_rname != NULL 

There are a few more cases where pointer is used as boolean, it will good to 
clean up. I have pointed this out in the previous reviews.

> +             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