mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-30 04:22:44 +00:00
4d0c95384f
sys/netinet and sys/netipsec are both part of the 'blessed' netstack, so can access struct ifnet directly. With this structure becoming private very soon, the necessary files need to get direct access. Sponsored by: Juniper Networks, Inc.
1167 lines
30 KiB
C
1167 lines
30 KiB
C
/*-
|
|
* Copyright (c) 2021,2022 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_inet6.h"
|
|
#include "opt_ipsec.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/ck.h>
|
|
#include <sys/eventhandler.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/pctrie.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/taskqueue.h>
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_var.h>
|
|
#include <net/if_private.h>
|
|
#include <net/vnet.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/ip6.h>
|
|
#include <netinet6/ip6_var.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/tcp_var.h>
|
|
|
|
#include <netipsec/key.h>
|
|
#include <netipsec/keydb.h>
|
|
#include <netipsec/key_debug.h>
|
|
#include <netipsec/xform.h>
|
|
#include <netipsec/ipsec.h>
|
|
#include <netipsec/ipsec_offload.h>
|
|
#include <netipsec/ah_var.h>
|
|
#include <netipsec/esp.h>
|
|
#include <netipsec/esp_var.h>
|
|
#include <netipsec/ipcomp_var.h>
|
|
|
|
#ifdef IPSEC_OFFLOAD
|
|
|
|
static struct mtx ipsec_accel_sav_tmp;
|
|
static struct unrhdr *drv_spi_unr;
|
|
static struct mtx ipsec_accel_cnt_lock;
|
|
static struct taskqueue *ipsec_accel_tq;
|
|
|
|
struct ipsec_accel_install_newkey_tq {
|
|
struct secasvar *sav;
|
|
struct vnet *install_vnet;
|
|
struct task install_task;
|
|
};
|
|
|
|
struct ipsec_accel_forget_tq {
|
|
struct vnet *forget_vnet;
|
|
struct task forget_task;
|
|
struct secasvar *sav;
|
|
};
|
|
|
|
struct ifp_handle_sav {
|
|
CK_LIST_ENTRY(ifp_handle_sav) sav_link;
|
|
CK_LIST_ENTRY(ifp_handle_sav) sav_allh_link;
|
|
struct secasvar *sav;
|
|
struct ifnet *ifp;
|
|
void *ifdata;
|
|
uint64_t drv_spi;
|
|
uint32_t flags;
|
|
size_t hdr_ext_size;
|
|
uint64_t cnt_octets;
|
|
uint64_t cnt_allocs;
|
|
};
|
|
|
|
#define IFP_HS_HANDLED 0x00000001
|
|
#define IFP_HS_REJECTED 0x00000002
|
|
#define IFP_HS_MARKER 0x00000010
|
|
|
|
static CK_LIST_HEAD(, ifp_handle_sav) ipsec_accel_all_sav_handles;
|
|
|
|
struct ifp_handle_sp {
|
|
CK_LIST_ENTRY(ifp_handle_sp) sp_link;
|
|
CK_LIST_ENTRY(ifp_handle_sp) sp_allh_link;
|
|
struct secpolicy *sp;
|
|
struct ifnet *ifp;
|
|
void *ifdata;
|
|
uint32_t flags;
|
|
};
|
|
|
|
#define IFP_HP_HANDLED 0x00000001
|
|
#define IFP_HP_REJECTED 0x00000002
|
|
#define IFP_HP_MARKER 0x00000004
|
|
|
|
static CK_LIST_HEAD(, ifp_handle_sp) ipsec_accel_all_sp_handles;
|
|
|
|
static void *
|
|
drvspi_sa_trie_alloc(struct pctrie *ptree)
|
|
{
|
|
void *res;
|
|
|
|
res = malloc(pctrie_node_size(), M_IPSEC_MISC, M_ZERO | M_NOWAIT);
|
|
if (res != NULL)
|
|
pctrie_zone_init(res, 0, 0);
|
|
return (res);
|
|
}
|
|
|
|
static void
|
|
drvspi_sa_trie_free(struct pctrie *ptree, void *node)
|
|
{
|
|
free(node, M_IPSEC_MISC);
|
|
}
|
|
|
|
PCTRIE_DEFINE(DRVSPI_SA, ifp_handle_sav, drv_spi,
|
|
drvspi_sa_trie_alloc, drvspi_sa_trie_free);
|
|
static struct pctrie drv_spi_pctrie;
|
|
|
|
static eventhandler_tag ipsec_accel_ifdetach_event_tag;
|
|
|
|
static void ipsec_accel_sa_newkey_impl(struct secasvar *sav);
|
|
static int ipsec_accel_handle_sav(struct secasvar *sav, struct ifnet *ifp,
|
|
u_int drv_spi, void *priv, uint32_t flags, struct ifp_handle_sav **ires);
|
|
static void ipsec_accel_forget_sav_clear(struct secasvar *sav);
|
|
static struct ifp_handle_sav *ipsec_accel_is_accel_sav_ptr(struct secasvar *sav,
|
|
struct ifnet *ifp);
|
|
static int ipsec_accel_sa_lifetime_op_impl(struct secasvar *sav,
|
|
struct seclifetime *lft_c, if_t ifp, enum IF_SA_CNT_WHICH op,
|
|
struct rm_priotracker *sahtree_trackerp);
|
|
static void ipsec_accel_sa_recordxfer(struct secasvar *sav, struct mbuf *m);
|
|
static void ipsec_accel_sync_imp(void);
|
|
static bool ipsec_accel_is_accel_sav_impl(struct secasvar *sav);
|
|
static struct mbuf *ipsec_accel_key_setaccelif_impl(struct secasvar *sav);
|
|
static void ipsec_accel_on_ifdown_impl(struct ifnet *ifp);
|
|
static void ipsec_accel_drv_sa_lifetime_update_impl(struct secasvar *sav,
|
|
if_t ifp, u_int drv_spi, uint64_t octets, uint64_t allocs);
|
|
static int ipsec_accel_drv_sa_lifetime_fetch_impl(struct secasvar *sav,
|
|
if_t ifp, u_int drv_spi, uint64_t *octets, uint64_t *allocs);
|
|
static void ipsec_accel_ifdetach_event(void *arg, struct ifnet *ifp);
|
|
|
|
static void
|
|
ipsec_accel_init(void *arg)
|
|
{
|
|
mtx_init(&ipsec_accel_sav_tmp, "ipasat", MTX_DEF, 0);
|
|
mtx_init(&ipsec_accel_cnt_lock, "ipascn", MTX_DEF, 0);
|
|
drv_spi_unr = new_unrhdr(IPSEC_ACCEL_DRV_SPI_MIN,
|
|
IPSEC_ACCEL_DRV_SPI_MAX, &ipsec_accel_sav_tmp);
|
|
ipsec_accel_tq = taskqueue_create("ipsec_offload", M_WAITOK,
|
|
taskqueue_thread_enqueue, &ipsec_accel_tq);
|
|
(void)taskqueue_start_threads(&ipsec_accel_tq,
|
|
1 /* Must be single-threaded */, PWAIT,
|
|
"ipsec_offload");
|
|
ipsec_accel_sa_newkey_p = ipsec_accel_sa_newkey_impl;
|
|
ipsec_accel_forget_sav_p = ipsec_accel_forget_sav_impl;
|
|
ipsec_accel_spdadd_p = ipsec_accel_spdadd_impl;
|
|
ipsec_accel_spddel_p = ipsec_accel_spddel_impl;
|
|
ipsec_accel_sa_lifetime_op_p = ipsec_accel_sa_lifetime_op_impl;
|
|
ipsec_accel_sync_p = ipsec_accel_sync_imp;
|
|
ipsec_accel_is_accel_sav_p = ipsec_accel_is_accel_sav_impl;
|
|
ipsec_accel_key_setaccelif_p = ipsec_accel_key_setaccelif_impl;
|
|
ipsec_accel_on_ifdown_p = ipsec_accel_on_ifdown_impl;
|
|
ipsec_accel_drv_sa_lifetime_update_p =
|
|
ipsec_accel_drv_sa_lifetime_update_impl;
|
|
ipsec_accel_drv_sa_lifetime_fetch_p =
|
|
ipsec_accel_drv_sa_lifetime_fetch_impl;
|
|
pctrie_init(&drv_spi_pctrie);
|
|
ipsec_accel_ifdetach_event_tag = EVENTHANDLER_REGISTER(
|
|
ifnet_departure_event, ipsec_accel_ifdetach_event, NULL,
|
|
EVENTHANDLER_PRI_ANY);
|
|
}
|
|
SYSINIT(ipsec_accel_init, SI_SUB_VNET_DONE, SI_ORDER_ANY,
|
|
ipsec_accel_init, NULL);
|
|
|
|
static void
|
|
ipsec_accel_fini(void *arg)
|
|
{
|
|
EVENTHANDLER_DEREGISTER(ifnet_departure_event,
|
|
ipsec_accel_ifdetach_event_tag);
|
|
ipsec_accel_sa_newkey_p = NULL;
|
|
ipsec_accel_forget_sav_p = NULL;
|
|
ipsec_accel_spdadd_p = NULL;
|
|
ipsec_accel_spddel_p = NULL;
|
|
ipsec_accel_sa_lifetime_op_p = NULL;
|
|
ipsec_accel_sync_p = NULL;
|
|
ipsec_accel_is_accel_sav_p = NULL;
|
|
ipsec_accel_key_setaccelif_p = NULL;
|
|
ipsec_accel_on_ifdown_p = NULL;
|
|
ipsec_accel_drv_sa_lifetime_update_p = NULL;
|
|
ipsec_accel_drv_sa_lifetime_fetch_p = NULL;
|
|
ipsec_accel_sync_imp();
|
|
clean_unrhdr(drv_spi_unr); /* avoid panic, should go later */
|
|
clear_unrhdr(drv_spi_unr);
|
|
delete_unrhdr(drv_spi_unr);
|
|
taskqueue_drain_all(ipsec_accel_tq);
|
|
taskqueue_free(ipsec_accel_tq);
|
|
mtx_destroy(&ipsec_accel_sav_tmp);
|
|
mtx_destroy(&ipsec_accel_cnt_lock);
|
|
}
|
|
SYSUNINIT(ipsec_accel_fini, SI_SUB_VNET_DONE, SI_ORDER_ANY,
|
|
ipsec_accel_fini, NULL);
|
|
|
|
SYSCTL_NODE(_net_inet_ipsec, OID_AUTO, offload, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
|
|
"");
|
|
|
|
static bool ipsec_offload_verbose = false;
|
|
SYSCTL_BOOL(_net_inet_ipsec_offload, OID_AUTO, verbose, CTLFLAG_RW,
|
|
&ipsec_offload_verbose, 0,
|
|
"Verbose SA/SP offload install and deinstall");
|
|
|
|
static void
|
|
dprintf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (!ipsec_offload_verbose)
|
|
return;
|
|
|
|
va_start(ap, fmt);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_alloc_forget_tq(struct secasvar *sav)
|
|
{
|
|
void *ftq;
|
|
|
|
if (sav->accel_forget_tq != 0)
|
|
return;
|
|
|
|
ftq = malloc(sizeof(struct ipsec_accel_forget_tq), M_TEMP, M_WAITOK);
|
|
if (!atomic_cmpset_ptr(&sav->accel_forget_tq, 0, (uintptr_t)ftq))
|
|
free(ftq, M_TEMP);
|
|
}
|
|
|
|
static bool
|
|
ipsec_accel_sa_install_match(if_t ifp, void *arg)
|
|
{
|
|
if ((ifp->if_capenable2 & IFCAP2_BIT(IFCAP2_IPSEC_OFFLOAD)) == 0)
|
|
return (false);
|
|
if (ifp->if_ipsec_accel_m->if_sa_newkey == NULL) {
|
|
dprintf("driver bug ifp %s if_sa_newkey NULL\n",
|
|
if_name(ifp));
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
static int
|
|
ipsec_accel_sa_newkey_cb(if_t ifp, void *arg)
|
|
{
|
|
struct ipsec_accel_install_newkey_tq *tq;
|
|
void *priv;
|
|
u_int drv_spi;
|
|
int error;
|
|
|
|
tq = arg;
|
|
|
|
dprintf("ipsec_accel_sa_newkey_act: ifp %s h %p spi %#x "
|
|
"flags %#x seq %d\n",
|
|
if_name(ifp), ifp->if_ipsec_accel_m->if_sa_newkey,
|
|
be32toh(tq->sav->spi), tq->sav->flags, tq->sav->seq);
|
|
priv = NULL;
|
|
drv_spi = alloc_unr(drv_spi_unr);
|
|
if (tq->sav->accel_ifname != NULL &&
|
|
strcmp(tq->sav->accel_ifname, if_name(ifp)) != 0) {
|
|
error = ipsec_accel_handle_sav(tq->sav,
|
|
ifp, drv_spi, priv, IFP_HS_REJECTED, NULL);
|
|
goto out;
|
|
}
|
|
if (drv_spi == -1) {
|
|
/* XXXKIB */
|
|
dprintf("ipsec_accel_sa_install_newkey: cannot alloc "
|
|
"drv_spi if %s spi %#x\n", if_name(ifp),
|
|
be32toh(tq->sav->spi));
|
|
return (ENOMEM);
|
|
}
|
|
error = ifp->if_ipsec_accel_m->if_sa_newkey(ifp, tq->sav,
|
|
drv_spi, &priv);
|
|
if (error != 0) {
|
|
if (error == EOPNOTSUPP) {
|
|
dprintf("ipsec_accel_sa_newkey: driver "
|
|
"refused sa if %s spi %#x\n",
|
|
if_name(ifp), be32toh(tq->sav->spi));
|
|
error = ipsec_accel_handle_sav(tq->sav,
|
|
ifp, drv_spi, priv, IFP_HS_REJECTED, NULL);
|
|
/* XXXKIB */
|
|
} else {
|
|
dprintf("ipsec_accel_sa_newkey: driver "
|
|
"error %d if %s spi %#x\n",
|
|
error, if_name(ifp), be32toh(tq->sav->spi));
|
|
/* XXXKIB */
|
|
}
|
|
} else {
|
|
error = ipsec_accel_handle_sav(tq->sav, ifp,
|
|
drv_spi, priv, IFP_HS_HANDLED, NULL);
|
|
if (error != 0) {
|
|
/* XXXKIB */
|
|
dprintf("ipsec_accel_sa_newkey: handle_sav "
|
|
"err %d if %s spi %#x\n", error,
|
|
if_name(ifp), be32toh(tq->sav->spi));
|
|
}
|
|
}
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_sa_newkey_act(void *context, int pending)
|
|
{
|
|
struct ipsec_accel_install_newkey_tq *tq;
|
|
void *tqf;
|
|
struct secasvar *sav;
|
|
|
|
tq = context;
|
|
tqf = NULL;
|
|
sav = tq->sav;
|
|
CURVNET_SET(tq->install_vnet);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
if ((sav->accel_flags & (SADB_KEY_ACCEL_INST |
|
|
SADB_KEY_ACCEL_DEINST)) == 0 &&
|
|
sav->state == SADB_SASTATE_MATURE) {
|
|
sav->accel_flags |= SADB_KEY_ACCEL_INST;
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
if_foreach_sleep(ipsec_accel_sa_install_match, context,
|
|
ipsec_accel_sa_newkey_cb, context);
|
|
ipsec_accel_alloc_forget_tq(sav);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
|
|
/*
|
|
* If ipsec_accel_forget_sav() raced with us and set
|
|
* the flag, do its work. Its task cannot execute in
|
|
* parallel since ipsec_accel taskqueue is single-threaded.
|
|
*/
|
|
if ((sav->accel_flags & SADB_KEY_ACCEL_DEINST) != 0) {
|
|
tqf = (void *)sav->accel_forget_tq;
|
|
sav->accel_forget_tq = 0;
|
|
ipsec_accel_forget_sav_clear(sav);
|
|
}
|
|
}
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
key_freesav(&tq->sav);
|
|
CURVNET_RESTORE();
|
|
free(tq, M_TEMP);
|
|
free(tqf, M_TEMP);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_sa_newkey_impl(struct secasvar *sav)
|
|
{
|
|
struct ipsec_accel_install_newkey_tq *tq;
|
|
|
|
if ((sav->accel_flags & (SADB_KEY_ACCEL_INST |
|
|
SADB_KEY_ACCEL_DEINST)) != 0)
|
|
return;
|
|
|
|
dprintf(
|
|
"ipsec_accel_sa_install_newkey: spi %#x flags %#x seq %d\n",
|
|
be32toh(sav->spi), sav->flags, sav->seq);
|
|
|
|
tq = malloc(sizeof(*tq), M_TEMP, M_NOWAIT);
|
|
if (tq == NULL) {
|
|
dprintf("ipsec_accel_sa_install_newkey: no memory for tq, "
|
|
"spi %#x\n", be32toh(sav->spi));
|
|
/* XXXKIB */
|
|
return;
|
|
}
|
|
|
|
refcount_acquire(&sav->refcnt);
|
|
|
|
TASK_INIT(&tq->install_task, 0, ipsec_accel_sa_newkey_act, tq);
|
|
tq->sav = sav;
|
|
tq->install_vnet = curthread->td_vnet;
|
|
taskqueue_enqueue(ipsec_accel_tq, &tq->install_task);
|
|
}
|
|
|
|
static int
|
|
ipsec_accel_handle_sav(struct secasvar *sav, struct ifnet *ifp,
|
|
u_int drv_spi, void *priv, uint32_t flags, struct ifp_handle_sav **ires)
|
|
{
|
|
struct ifp_handle_sav *ihs, *i;
|
|
int error;
|
|
|
|
MPASS(__bitcount(flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) == 1);
|
|
|
|
ihs = malloc(sizeof(*ihs), M_IPSEC_MISC, M_WAITOK | M_ZERO);
|
|
ihs->ifp = ifp;
|
|
ihs->sav = sav;
|
|
ihs->drv_spi = drv_spi;
|
|
ihs->ifdata = priv;
|
|
ihs->flags = flags;
|
|
ihs->hdr_ext_size = esp_hdrsiz(sav);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) {
|
|
if (i->ifp == ifp) {
|
|
error = EALREADY;
|
|
goto errout;
|
|
}
|
|
}
|
|
error = DRVSPI_SA_PCTRIE_INSERT(&drv_spi_pctrie, ihs);
|
|
if (error != 0)
|
|
goto errout;
|
|
if_ref(ihs->ifp);
|
|
CK_LIST_INSERT_HEAD(&sav->accel_ifps, ihs, sav_link);
|
|
CK_LIST_INSERT_HEAD(&ipsec_accel_all_sav_handles, ihs, sav_allh_link);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
if (ires != NULL)
|
|
*ires = ihs;
|
|
return (0);
|
|
errout:
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
free(ihs, M_IPSEC_MISC);
|
|
if (ires != NULL)
|
|
*ires = NULL;
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_forget_handle_sav(struct ifp_handle_sav *i, bool freesav)
|
|
{
|
|
struct ifnet *ifp;
|
|
struct secasvar *sav;
|
|
|
|
mtx_assert(&ipsec_accel_sav_tmp, MA_OWNED);
|
|
|
|
CK_LIST_REMOVE(i, sav_link);
|
|
CK_LIST_REMOVE(i, sav_allh_link);
|
|
DRVSPI_SA_PCTRIE_REMOVE(&drv_spi_pctrie, i->drv_spi);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
NET_EPOCH_WAIT();
|
|
ifp = i->ifp;
|
|
sav = i->sav;
|
|
if ((i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) ==
|
|
IFP_HS_HANDLED) {
|
|
dprintf("sa deinstall %s %p spi %#x ifl %#x\n",
|
|
if_name(ifp), sav, be32toh(sav->spi), i->flags);
|
|
ifp->if_ipsec_accel_m->if_sa_deinstall(ifp,
|
|
i->drv_spi, i->ifdata);
|
|
}
|
|
if_rele(ifp);
|
|
free_unr(drv_spi_unr, i->drv_spi);
|
|
free(i, M_IPSEC_MISC);
|
|
if (freesav)
|
|
key_freesav(&sav);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_forget_sav_clear(struct secasvar *sav)
|
|
{
|
|
struct ifp_handle_sav *i;
|
|
|
|
for (;;) {
|
|
i = CK_LIST_FIRST(&sav->accel_ifps);
|
|
if (i == NULL)
|
|
break;
|
|
ipsec_accel_forget_handle_sav(i, false);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_forget_sav_act(void *arg, int pending)
|
|
{
|
|
struct ipsec_accel_forget_tq *tq;
|
|
struct secasvar *sav;
|
|
|
|
tq = arg;
|
|
sav = tq->sav;
|
|
CURVNET_SET(tq->forget_vnet);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
ipsec_accel_forget_sav_clear(sav);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
key_freesav(&sav);
|
|
CURVNET_RESTORE();
|
|
free(tq, M_TEMP);
|
|
}
|
|
|
|
void
|
|
ipsec_accel_forget_sav_impl(struct secasvar *sav)
|
|
{
|
|
struct ipsec_accel_forget_tq *tq;
|
|
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
sav->accel_flags |= SADB_KEY_ACCEL_DEINST;
|
|
tq = (void *)atomic_load_ptr(&sav->accel_forget_tq);
|
|
if (tq == NULL || !atomic_cmpset_ptr(&sav->accel_forget_tq,
|
|
(uintptr_t)tq, 0)) {
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
return;
|
|
}
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
|
|
refcount_acquire(&sav->refcnt);
|
|
TASK_INIT(&tq->forget_task, 0, ipsec_accel_forget_sav_act, tq);
|
|
tq->forget_vnet = curthread->td_vnet;
|
|
tq->sav = sav;
|
|
taskqueue_enqueue(ipsec_accel_tq, &tq->forget_task);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_on_ifdown_sav(struct ifnet *ifp)
|
|
{
|
|
struct ifp_handle_sav *i, *marker;
|
|
|
|
marker = malloc(sizeof(*marker), M_IPSEC_MISC, M_WAITOK | M_ZERO);
|
|
marker->flags = IFP_HS_MARKER;
|
|
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
CK_LIST_INSERT_HEAD(&ipsec_accel_all_sav_handles, marker,
|
|
sav_allh_link);
|
|
for (;;) {
|
|
i = CK_LIST_NEXT(marker, sav_allh_link);
|
|
if (i == NULL)
|
|
break;
|
|
CK_LIST_REMOVE(marker, sav_allh_link);
|
|
CK_LIST_INSERT_AFTER(i, marker, sav_allh_link);
|
|
if (i->ifp == ifp) {
|
|
refcount_acquire(&i->sav->refcnt); /* XXXKIB wrap ? */
|
|
ipsec_accel_forget_handle_sav(i, true);
|
|
}
|
|
}
|
|
CK_LIST_REMOVE(marker, sav_allh_link);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
free(marker, M_IPSEC_MISC);
|
|
}
|
|
|
|
static struct ifp_handle_sav *
|
|
ipsec_accel_is_accel_sav_ptr_raw(struct secasvar *sav, struct ifnet *ifp)
|
|
{
|
|
struct ifp_handle_sav *i;
|
|
|
|
if ((ifp->if_capenable2 & IFCAP2_BIT(IFCAP2_IPSEC_OFFLOAD)) == 0)
|
|
return (NULL);
|
|
CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) {
|
|
if (i->ifp == ifp)
|
|
return (i);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
static struct ifp_handle_sav *
|
|
ipsec_accel_is_accel_sav_ptr(struct secasvar *sav, struct ifnet *ifp)
|
|
{
|
|
NET_EPOCH_ASSERT();
|
|
return (ipsec_accel_is_accel_sav_ptr_raw(sav, ifp));
|
|
}
|
|
|
|
static bool
|
|
ipsec_accel_is_accel_sav_impl(struct secasvar *sav)
|
|
{
|
|
return (!CK_LIST_EMPTY(&sav->accel_ifps));
|
|
}
|
|
|
|
static struct secasvar *
|
|
ipsec_accel_drvspi_to_sa(u_int drv_spi)
|
|
{
|
|
struct ifp_handle_sav *i;
|
|
|
|
i = DRVSPI_SA_PCTRIE_LOOKUP(&drv_spi_pctrie, drv_spi);
|
|
if (i == NULL)
|
|
return (NULL);
|
|
return (i->sav);
|
|
}
|
|
|
|
static struct ifp_handle_sp *
|
|
ipsec_accel_find_accel_sp(struct secpolicy *sp, if_t ifp)
|
|
{
|
|
struct ifp_handle_sp *i;
|
|
|
|
CK_LIST_FOREACH(i, &sp->accel_ifps, sp_link) {
|
|
if (i->ifp == ifp)
|
|
return (i);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
static bool
|
|
ipsec_accel_is_accel_sp(struct secpolicy *sp, if_t ifp)
|
|
{
|
|
return (ipsec_accel_find_accel_sp(sp, ifp) != NULL);
|
|
}
|
|
|
|
static int
|
|
ipsec_accel_remember_sp(struct secpolicy *sp, if_t ifp,
|
|
struct ifp_handle_sp **ip)
|
|
{
|
|
struct ifp_handle_sp *i;
|
|
|
|
i = malloc(sizeof(*i), M_IPSEC_MISC, M_WAITOK | M_ZERO);
|
|
i->sp = sp;
|
|
i->ifp = ifp;
|
|
if_ref(ifp);
|
|
i->flags = IFP_HP_HANDLED;
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
CK_LIST_INSERT_HEAD(&sp->accel_ifps, i, sp_link);
|
|
CK_LIST_INSERT_HEAD(&ipsec_accel_all_sp_handles, i, sp_allh_link);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
*ip = i;
|
|
return (0);
|
|
}
|
|
|
|
static bool
|
|
ipsec_accel_spdadd_match(if_t ifp, void *arg)
|
|
{
|
|
struct secpolicy *sp;
|
|
|
|
if ((ifp->if_capenable2 & IFCAP2_BIT(IFCAP2_IPSEC_OFFLOAD)) == 0 ||
|
|
ifp->if_ipsec_accel_m->if_spdadd == NULL)
|
|
return (false);
|
|
sp = arg;
|
|
if (sp->accel_ifname != NULL &&
|
|
strcmp(sp->accel_ifname, if_name(ifp)) != 0)
|
|
return (false);
|
|
if (ipsec_accel_is_accel_sp(sp, ifp))
|
|
return (false);
|
|
return (true);
|
|
}
|
|
|
|
static int
|
|
ipsec_accel_spdadd_cb(if_t ifp, void *arg)
|
|
{
|
|
struct secpolicy *sp;
|
|
struct inpcb *inp;
|
|
struct ifp_handle_sp *i;
|
|
int error;
|
|
|
|
sp = arg;
|
|
inp = sp->ipsec_accel_add_sp_inp;
|
|
dprintf("ipsec_accel_spdadd_cb: ifp %s m %p sp %p inp %p\n",
|
|
if_name(ifp), ifp->if_ipsec_accel_m->if_spdadd, sp, inp);
|
|
error = ipsec_accel_remember_sp(sp, ifp, &i);
|
|
if (error != 0) {
|
|
dprintf("ipsec_accel_spdadd: %s if_spdadd %p remember res %d\n",
|
|
if_name(ifp), sp, error);
|
|
return (error);
|
|
}
|
|
error = ifp->if_ipsec_accel_m->if_spdadd(ifp, sp, inp, &i->ifdata);
|
|
if (error != 0) {
|
|
i->flags |= IFP_HP_REJECTED;
|
|
dprintf("ipsec_accel_spdadd: %s if_spdadd %p res %d\n",
|
|
if_name(ifp), sp, error);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_spdadd_act(void *arg, int pending)
|
|
{
|
|
struct secpolicy *sp;
|
|
struct inpcb *inp;
|
|
|
|
sp = arg;
|
|
CURVNET_SET(sp->accel_add_tq.adddel_vnet);
|
|
if_foreach_sleep(ipsec_accel_spdadd_match, arg,
|
|
ipsec_accel_spdadd_cb, arg);
|
|
inp = sp->ipsec_accel_add_sp_inp;
|
|
if (inp != NULL) {
|
|
INP_WLOCK(inp);
|
|
if (!in_pcbrele_wlocked(inp))
|
|
INP_WUNLOCK(inp);
|
|
sp->ipsec_accel_add_sp_inp = NULL;
|
|
}
|
|
CURVNET_RESTORE();
|
|
key_freesp(&sp);
|
|
}
|
|
|
|
void
|
|
ipsec_accel_spdadd_impl(struct secpolicy *sp, struct inpcb *inp)
|
|
{
|
|
struct ipsec_accel_adddel_sp_tq *tq;
|
|
|
|
if (sp == NULL)
|
|
return;
|
|
if (sp->tcount == 0 && inp == NULL)
|
|
return;
|
|
tq = &sp->accel_add_tq;
|
|
if (atomic_cmpset_int(&tq->adddel_scheduled, 0, 1) == 0)
|
|
return;
|
|
tq->adddel_vnet = curthread->td_vnet;
|
|
sp->ipsec_accel_add_sp_inp = inp;
|
|
if (inp != NULL)
|
|
in_pcbref(inp);
|
|
TASK_INIT(&tq->adddel_task, 0, ipsec_accel_spdadd_act, sp);
|
|
key_addref(sp);
|
|
taskqueue_enqueue(ipsec_accel_tq, &tq->adddel_task);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_spddel_act(void *arg, int pending)
|
|
{
|
|
struct ifp_handle_sp *i;
|
|
struct secpolicy *sp;
|
|
int error;
|
|
|
|
sp = arg;
|
|
CURVNET_SET(sp->accel_del_tq.adddel_vnet);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
for (;;) {
|
|
i = CK_LIST_FIRST(&sp->accel_ifps);
|
|
if (i == NULL)
|
|
break;
|
|
CK_LIST_REMOVE(i, sp_link);
|
|
CK_LIST_REMOVE(i, sp_allh_link);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
NET_EPOCH_WAIT();
|
|
if ((i->flags & (IFP_HP_HANDLED | IFP_HP_REJECTED)) ==
|
|
IFP_HP_HANDLED) {
|
|
dprintf("spd deinstall %s %p\n", if_name(i->ifp), sp);
|
|
error = i->ifp->if_ipsec_accel_m->if_spddel(i->ifp,
|
|
sp, i->ifdata);
|
|
if (error != 0) {
|
|
dprintf(
|
|
"ipsec_accel_spddel: %s if_spddel %p res %d\n",
|
|
if_name(i->ifp), sp, error);
|
|
}
|
|
}
|
|
if_rele(i->ifp);
|
|
free(i, M_IPSEC_MISC);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
}
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
key_freesp(&sp);
|
|
CURVNET_RESTORE();
|
|
}
|
|
|
|
void
|
|
ipsec_accel_spddel_impl(struct secpolicy *sp)
|
|
{
|
|
struct ipsec_accel_adddel_sp_tq *tq;
|
|
|
|
if (sp == NULL)
|
|
return;
|
|
|
|
tq = &sp->accel_del_tq;
|
|
if (atomic_cmpset_int(&tq->adddel_scheduled, 0, 1) == 0)
|
|
return;
|
|
tq->adddel_vnet = curthread->td_vnet;
|
|
TASK_INIT(&tq->adddel_task, 0, ipsec_accel_spddel_act, sp);
|
|
key_addref(sp);
|
|
taskqueue_enqueue(ipsec_accel_tq, &tq->adddel_task);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_on_ifdown_sp(struct ifnet *ifp)
|
|
{
|
|
struct ifp_handle_sp *i, *marker;
|
|
struct secpolicy *sp;
|
|
int error;
|
|
|
|
marker = malloc(sizeof(*marker), M_IPSEC_MISC, M_WAITOK | M_ZERO);
|
|
marker->flags = IFP_HS_MARKER;
|
|
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
CK_LIST_INSERT_HEAD(&ipsec_accel_all_sp_handles, marker,
|
|
sp_allh_link);
|
|
for (;;) {
|
|
i = CK_LIST_NEXT(marker, sp_allh_link);
|
|
if (i == NULL)
|
|
break;
|
|
CK_LIST_REMOVE(marker, sp_allh_link);
|
|
CK_LIST_INSERT_AFTER(i, marker, sp_allh_link);
|
|
if (i->ifp != ifp)
|
|
continue;
|
|
|
|
sp = i->sp;
|
|
key_addref(sp);
|
|
CK_LIST_REMOVE(i, sp_link);
|
|
CK_LIST_REMOVE(i, sp_allh_link);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
NET_EPOCH_WAIT();
|
|
if ((i->flags & (IFP_HP_HANDLED | IFP_HP_REJECTED)) ==
|
|
IFP_HP_HANDLED) {
|
|
dprintf("spd deinstall %s %p\n", if_name(ifp), sp);
|
|
error = ifp->if_ipsec_accel_m->if_spddel(ifp,
|
|
sp, i->ifdata);
|
|
}
|
|
if (error != 0) {
|
|
dprintf(
|
|
"ipsec_accel_on_ifdown_sp: %s if_spddel %p res %d\n",
|
|
if_name(ifp), sp, error);
|
|
}
|
|
key_freesp(&sp);
|
|
if_rele(ifp);
|
|
free(i, M_IPSEC_MISC);
|
|
mtx_lock(&ipsec_accel_sav_tmp);
|
|
}
|
|
CK_LIST_REMOVE(marker, sp_allh_link);
|
|
mtx_unlock(&ipsec_accel_sav_tmp);
|
|
free(marker, M_IPSEC_MISC);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_on_ifdown_impl(struct ifnet *ifp)
|
|
{
|
|
ipsec_accel_on_ifdown_sp(ifp);
|
|
ipsec_accel_on_ifdown_sav(ifp);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_ifdetach_event(void *arg __unused, struct ifnet *ifp)
|
|
{
|
|
if ((ifp->if_flags & IFF_RENAMING) != 0)
|
|
return;
|
|
ipsec_accel_on_ifdown_impl(ifp);
|
|
}
|
|
|
|
static bool
|
|
ipsec_accel_output_pad(struct mbuf *m, struct secasvar *sav, int skip, int mtu)
|
|
{
|
|
int alen, blks, hlen, padding, rlen;
|
|
|
|
rlen = m->m_pkthdr.len - skip;
|
|
hlen = ((sav->flags & SADB_X_EXT_OLD) != 0 ? sizeof(struct esp) :
|
|
sizeof(struct newesp)) + sav->ivlen;
|
|
blks = MAX(4, SAV_ISCTR(sav) && VNET(esp_ctr_compatibility) ?
|
|
sav->tdb_encalgxform->native_blocksize :
|
|
sav->tdb_encalgxform->blocksize);
|
|
padding = ((blks - ((rlen + 2) % blks)) % blks) + 2;
|
|
alen = xform_ah_authsize(sav->tdb_authalgxform);
|
|
|
|
return (skip + hlen + rlen + padding + alen <= mtu);
|
|
}
|
|
|
|
static bool
|
|
ipsec_accel_output_tag(struct mbuf *m, u_int drv_spi)
|
|
{
|
|
struct ipsec_accel_out_tag *tag;
|
|
|
|
tag = (struct ipsec_accel_out_tag *)m_tag_get(
|
|
PACKET_TAG_IPSEC_ACCEL_OUT, sizeof(*tag), M_NOWAIT);
|
|
if (tag == NULL)
|
|
return (false);
|
|
tag->drv_spi = drv_spi;
|
|
m_tag_prepend(m, &tag->tag);
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
ipsec_accel_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
|
|
struct secpolicy *sp, struct secasvar *sav, int af, int mtu, int *hwassist)
|
|
{
|
|
struct ifp_handle_sav *i;
|
|
struct ip *ip;
|
|
struct tcpcb *tp;
|
|
u_long ip_len, skip;
|
|
bool res;
|
|
|
|
*hwassist = 0;
|
|
res = false;
|
|
if (ifp == NULL)
|
|
return (res);
|
|
|
|
M_ASSERTPKTHDR(m);
|
|
NET_EPOCH_ASSERT();
|
|
|
|
if (sav == NULL) {
|
|
res = ipsec_accel_output_tag(m, IPSEC_ACCEL_DRV_SPI_BYPASS);
|
|
goto out;
|
|
}
|
|
|
|
i = ipsec_accel_is_accel_sav_ptr(sav, ifp);
|
|
if (i == NULL || (i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) !=
|
|
IFP_HS_HANDLED)
|
|
goto out;
|
|
|
|
if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
|
|
ip_len = m->m_pkthdr.len;
|
|
if (ip_len + i->hdr_ext_size > mtu)
|
|
goto out;
|
|
switch (af) {
|
|
case AF_INET:
|
|
ip = mtod(m, struct ip *);
|
|
skip = ip->ip_hl << 2;
|
|
break;
|
|
case AF_INET6:
|
|
skip = sizeof(struct ip6_hdr);
|
|
break;
|
|
default:
|
|
__unreachable();
|
|
}
|
|
if (!ipsec_accel_output_pad(m, sav, skip, mtu))
|
|
goto out;
|
|
}
|
|
|
|
if (!ipsec_accel_output_tag(m, i->drv_spi))
|
|
goto out;
|
|
|
|
ipsec_accel_sa_recordxfer(sav, m);
|
|
key_freesav(&sav);
|
|
if (sp != NULL)
|
|
key_freesp(&sp);
|
|
|
|
*hwassist = ifp->if_ipsec_accel_m->if_hwassist(ifp, sav,
|
|
i->drv_spi, i->ifdata);
|
|
res = true;
|
|
out:
|
|
if (inp != NULL && inp->inp_pcbinfo == &V_tcbinfo) {
|
|
INP_WLOCK_ASSERT(inp);
|
|
tp = (struct tcpcb *)inp;
|
|
if (res && (*hwassist & (CSUM_TSO | CSUM_IP6_TSO)) != 0) {
|
|
tp->t_flags2 |= TF2_IPSEC_TSO;
|
|
} else {
|
|
tp->t_flags2 &= ~TF2_IPSEC_TSO;
|
|
}
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
struct ipsec_accel_in_tag *
|
|
ipsec_accel_input_tag_lookup(const struct mbuf *m)
|
|
{
|
|
struct ipsec_accel_in_tag *tag;
|
|
struct m_tag *xtag;
|
|
|
|
xtag = m_tag_find(__DECONST(struct mbuf *, m),
|
|
PACKET_TAG_IPSEC_ACCEL_IN, NULL);
|
|
if (xtag == NULL)
|
|
return (NULL);
|
|
tag = __containerof(xtag, struct ipsec_accel_in_tag, tag);
|
|
return (tag);
|
|
}
|
|
|
|
int
|
|
ipsec_accel_input(struct mbuf *m, int offset, int proto)
|
|
{
|
|
struct secasvar *sav;
|
|
struct ipsec_accel_in_tag *tag;
|
|
|
|
tag = ipsec_accel_input_tag_lookup(m);
|
|
if (tag == NULL)
|
|
return (ENXIO);
|
|
|
|
if (tag->drv_spi < IPSEC_ACCEL_DRV_SPI_MIN ||
|
|
tag->drv_spi > IPSEC_ACCEL_DRV_SPI_MAX) {
|
|
dprintf("if %s mbuf %p drv_spi %d invalid, packet dropped\n",
|
|
(m->m_flags & M_PKTHDR) != 0 ? if_name(m->m_pkthdr.rcvif) :
|
|
"<unknwn>", m, tag->drv_spi);
|
|
m_freem(m);
|
|
return (EINPROGRESS);
|
|
}
|
|
|
|
sav = ipsec_accel_drvspi_to_sa(tag->drv_spi);
|
|
if (sav != NULL)
|
|
ipsec_accel_sa_recordxfer(sav, m);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_sa_recordxfer(struct secasvar *sav, struct mbuf *m)
|
|
{
|
|
counter_u64_add(sav->accel_lft_sw, 1);
|
|
counter_u64_add(sav->accel_lft_sw + 1, m->m_pkthdr.len);
|
|
if (sav->accel_firstused == 0)
|
|
sav->accel_firstused = time_second;
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_sa_lifetime_update(struct seclifetime *lft_c,
|
|
const struct seclifetime *lft_l)
|
|
{
|
|
lft_c->allocations += lft_l->allocations;
|
|
lft_c->bytes += lft_l->bytes;
|
|
lft_c->usetime = min(lft_c->usetime, lft_l->usetime);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_drv_sa_lifetime_update_impl(struct secasvar *sav, if_t ifp,
|
|
u_int drv_spi, uint64_t octets, uint64_t allocs)
|
|
{
|
|
struct epoch_tracker et;
|
|
struct ifp_handle_sav *i;
|
|
uint64_t odiff, adiff;
|
|
|
|
NET_EPOCH_ENTER(et);
|
|
mtx_lock(&ipsec_accel_cnt_lock);
|
|
|
|
if (allocs != 0) {
|
|
if (sav->firstused == 0)
|
|
sav->firstused = time_second;
|
|
if (sav->accel_firstused == 0)
|
|
sav->accel_firstused = time_second;
|
|
}
|
|
|
|
CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) {
|
|
if (i->ifp == ifp && i->drv_spi == drv_spi)
|
|
break;
|
|
}
|
|
if (i == NULL)
|
|
goto out;
|
|
|
|
odiff = octets - i->cnt_octets;
|
|
adiff = allocs - i->cnt_allocs;
|
|
|
|
if (sav->lft_c != NULL) {
|
|
counter_u64_add(sav->lft_c_bytes, odiff);
|
|
counter_u64_add(sav->lft_c_allocations, adiff);
|
|
}
|
|
|
|
i->cnt_octets = octets;
|
|
i->cnt_allocs = allocs;
|
|
sav->accel_hw_octets += odiff;
|
|
sav->accel_hw_allocs += adiff;
|
|
|
|
out:
|
|
mtx_unlock(&ipsec_accel_cnt_lock);
|
|
NET_EPOCH_EXIT(et);
|
|
}
|
|
|
|
static int
|
|
ipsec_accel_drv_sa_lifetime_fetch_impl(struct secasvar *sav,
|
|
if_t ifp, u_int drv_spi, uint64_t *octets, uint64_t *allocs)
|
|
{
|
|
struct ifp_handle_sav *i;
|
|
int error;
|
|
|
|
NET_EPOCH_ASSERT();
|
|
error = 0;
|
|
|
|
mtx_lock(&ipsec_accel_cnt_lock);
|
|
CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) {
|
|
if (i->ifp == ifp && i->drv_spi == drv_spi) {
|
|
*octets = i->cnt_octets;
|
|
*allocs = i->cnt_allocs;
|
|
break;
|
|
}
|
|
}
|
|
if (i == NULL)
|
|
error = ENOENT;
|
|
mtx_unlock(&ipsec_accel_cnt_lock);
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_sa_lifetime_hw(struct secasvar *sav, if_t ifp,
|
|
struct seclifetime *lft)
|
|
{
|
|
struct ifp_handle_sav *i;
|
|
if_sa_cnt_fn_t p;
|
|
|
|
IFNET_RLOCK_ASSERT();
|
|
|
|
i = ipsec_accel_is_accel_sav_ptr(sav, ifp);
|
|
if (i != NULL && (i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) ==
|
|
IFP_HS_HANDLED) {
|
|
p = ifp->if_ipsec_accel_m->if_sa_cnt;
|
|
if (p != NULL)
|
|
p(ifp, sav, i->drv_spi, i->ifdata, lft);
|
|
}
|
|
}
|
|
|
|
static int
|
|
ipsec_accel_sa_lifetime_op_impl(struct secasvar *sav,
|
|
struct seclifetime *lft_c, if_t ifp, enum IF_SA_CNT_WHICH op,
|
|
struct rm_priotracker *sahtree_trackerp)
|
|
{
|
|
struct seclifetime lft_l, lft_s;
|
|
struct ifp_handle_sav *i;
|
|
if_t ifp1;
|
|
if_sa_cnt_fn_t p;
|
|
int error;
|
|
|
|
error = 0;
|
|
memset(&lft_l, 0, sizeof(lft_l));
|
|
memset(&lft_s, 0, sizeof(lft_s));
|
|
|
|
switch (op & ~IF_SA_CNT_UPD) {
|
|
case IF_SA_CNT_IFP_HW_VAL:
|
|
ipsec_accel_sa_lifetime_hw(sav, ifp, &lft_l);
|
|
ipsec_accel_sa_lifetime_update(&lft_l, &lft_s);
|
|
break;
|
|
|
|
case IF_SA_CNT_TOTAL_SW_VAL:
|
|
lft_l.allocations = (uint32_t)counter_u64_fetch(
|
|
sav->accel_lft_sw);
|
|
lft_l.bytes = counter_u64_fetch(sav->accel_lft_sw + 1);
|
|
lft_l.usetime = sav->accel_firstused;
|
|
break;
|
|
|
|
case IF_SA_CNT_TOTAL_HW_VAL:
|
|
IFNET_RLOCK_ASSERT();
|
|
CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) {
|
|
if ((i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) !=
|
|
IFP_HS_HANDLED)
|
|
continue;
|
|
ifp1 = i->ifp;
|
|
p = ifp1->if_ipsec_accel_m->if_sa_cnt;
|
|
if (p == NULL)
|
|
continue;
|
|
memset(&lft_s, 0, sizeof(lft_s));
|
|
if (sahtree_trackerp != NULL)
|
|
ipsec_sahtree_runlock(sahtree_trackerp);
|
|
error = p(ifp1, sav, i->drv_spi, i->ifdata, &lft_s);
|
|
if (sahtree_trackerp != NULL)
|
|
ipsec_sahtree_rlock(sahtree_trackerp);
|
|
if (error == 0)
|
|
ipsec_accel_sa_lifetime_update(&lft_l, &lft_s);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (error == 0) {
|
|
if ((op & IF_SA_CNT_UPD) == 0)
|
|
memset(lft_c, 0, sizeof(*lft_c));
|
|
ipsec_accel_sa_lifetime_update(lft_c, &lft_l);
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
ipsec_accel_sync_imp(void)
|
|
{
|
|
taskqueue_drain_all(ipsec_accel_tq);
|
|
}
|
|
|
|
static struct mbuf *
|
|
ipsec_accel_key_setaccelif_impl(struct secasvar *sav)
|
|
{
|
|
struct mbuf *m, *m1;
|
|
struct ifp_handle_sav *i;
|
|
struct epoch_tracker et;
|
|
|
|
if (sav->accel_ifname != NULL)
|
|
return (key_setaccelif(sav->accel_ifname));
|
|
|
|
m = m1 = NULL;
|
|
|
|
NET_EPOCH_ENTER(et);
|
|
CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) {
|
|
if ((i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) ==
|
|
IFP_HS_HANDLED) {
|
|
m1 = key_setaccelif(if_name(i->ifp));
|
|
if (m == NULL)
|
|
m = m1;
|
|
else if (m1 != NULL)
|
|
m_cat(m, m1);
|
|
}
|
|
}
|
|
NET_EPOCH_EXIT(et);
|
|
return (m);
|
|
}
|
|
|
|
#endif /* IPSEC_OFFLOAD */
|