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