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