1ef2a572bSKonstantin Belousov /*- 2ef2a572bSKonstantin Belousov * Copyright (c) 2021,2022 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. 3ef2a572bSKonstantin Belousov * 4ef2a572bSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 5ef2a572bSKonstantin Belousov * modification, are permitted provided that the following conditions 6ef2a572bSKonstantin Belousov * are met: 7ef2a572bSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 8ef2a572bSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 9ef2a572bSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 10ef2a572bSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 11ef2a572bSKonstantin Belousov * documentation and/or other materials provided with the distribution. 12ef2a572bSKonstantin Belousov * 13ef2a572bSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14ef2a572bSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15ef2a572bSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16ef2a572bSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17ef2a572bSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18ef2a572bSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19ef2a572bSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20ef2a572bSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21ef2a572bSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22ef2a572bSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23ef2a572bSKonstantin Belousov * SUCH DAMAGE. 24ef2a572bSKonstantin Belousov */ 25ef2a572bSKonstantin Belousov 26ef2a572bSKonstantin Belousov #include "opt_inet.h" 27ef2a572bSKonstantin Belousov #include "opt_inet6.h" 28ef2a572bSKonstantin Belousov #include "opt_ipsec.h" 29ef2a572bSKonstantin Belousov 30ef2a572bSKonstantin Belousov #include <sys/param.h> 31ef2a572bSKonstantin Belousov #include <sys/systm.h> 32ef2a572bSKonstantin Belousov #include <sys/ck.h> 33c4a0ee9bSKonstantin Belousov #include <sys/eventhandler.h> 34ef2a572bSKonstantin Belousov #include <sys/kernel.h> 35ef2a572bSKonstantin Belousov #include <sys/mbuf.h> 36ef2a572bSKonstantin Belousov #include <sys/pctrie.h> 37ef2a572bSKonstantin Belousov #include <sys/proc.h> 38ef2a572bSKonstantin Belousov #include <sys/socket.h> 399a7096ffSKonstantin Belousov #include <sys/sysctl.h> 40ef2a572bSKonstantin Belousov #include <sys/protosw.h> 41ef2a572bSKonstantin Belousov #include <sys/taskqueue.h> 42ef2a572bSKonstantin Belousov 439a7096ffSKonstantin Belousov #include <machine/stdarg.h> 449a7096ffSKonstantin Belousov 45ef2a572bSKonstantin Belousov #include <net/if.h> 46ef2a572bSKonstantin Belousov #include <net/if_var.h> 47*4d0c9538SJustin Hibbits #include <net/if_private.h> 48ef2a572bSKonstantin Belousov #include <net/vnet.h> 49ef2a572bSKonstantin Belousov #include <netinet/in.h> 50ef2a572bSKonstantin Belousov #include <netinet/ip.h> 51ef2a572bSKonstantin Belousov #include <netinet/ip_var.h> 52ef2a572bSKonstantin Belousov #include <netinet/ip6.h> 53ef2a572bSKonstantin Belousov #include <netinet6/ip6_var.h> 54ef2a572bSKonstantin Belousov #include <netinet/in_pcb.h> 55e6e2c0a5SKonstantin Belousov #include <netinet/tcp_var.h> 56ef2a572bSKonstantin Belousov 57ef2a572bSKonstantin Belousov #include <netipsec/key.h> 58ef2a572bSKonstantin Belousov #include <netipsec/keydb.h> 59ef2a572bSKonstantin Belousov #include <netipsec/key_debug.h> 60ef2a572bSKonstantin Belousov #include <netipsec/xform.h> 61ef2a572bSKonstantin Belousov #include <netipsec/ipsec.h> 62ef2a572bSKonstantin Belousov #include <netipsec/ipsec_offload.h> 63ef2a572bSKonstantin Belousov #include <netipsec/ah_var.h> 64ef2a572bSKonstantin Belousov #include <netipsec/esp.h> 65ef2a572bSKonstantin Belousov #include <netipsec/esp_var.h> 66ef2a572bSKonstantin Belousov #include <netipsec/ipcomp_var.h> 67ef2a572bSKonstantin Belousov 68ef2a572bSKonstantin Belousov #ifdef IPSEC_OFFLOAD 69ef2a572bSKonstantin Belousov 70ef2a572bSKonstantin Belousov static struct mtx ipsec_accel_sav_tmp; 71ef2a572bSKonstantin Belousov static struct unrhdr *drv_spi_unr; 72ef2a572bSKonstantin Belousov static struct mtx ipsec_accel_cnt_lock; 73f76826b8SKonstantin Belousov static struct taskqueue *ipsec_accel_tq; 74ef2a572bSKonstantin Belousov 75ef2a572bSKonstantin Belousov struct ipsec_accel_install_newkey_tq { 76ef2a572bSKonstantin Belousov struct secasvar *sav; 77ef2a572bSKonstantin Belousov struct vnet *install_vnet; 78ef2a572bSKonstantin Belousov struct task install_task; 79ef2a572bSKonstantin Belousov }; 80ef2a572bSKonstantin Belousov 81ef2a572bSKonstantin Belousov struct ipsec_accel_forget_tq { 82ef2a572bSKonstantin Belousov struct vnet *forget_vnet; 83ef2a572bSKonstantin Belousov struct task forget_task; 84ef2a572bSKonstantin Belousov struct secasvar *sav; 85ef2a572bSKonstantin Belousov }; 86ef2a572bSKonstantin Belousov 87ef2a572bSKonstantin Belousov struct ifp_handle_sav { 88ef2a572bSKonstantin Belousov CK_LIST_ENTRY(ifp_handle_sav) sav_link; 89ef2a572bSKonstantin Belousov CK_LIST_ENTRY(ifp_handle_sav) sav_allh_link; 90ef2a572bSKonstantin Belousov struct secasvar *sav; 91ef2a572bSKonstantin Belousov struct ifnet *ifp; 92ef2a572bSKonstantin Belousov void *ifdata; 93ef2a572bSKonstantin Belousov uint64_t drv_spi; 94ef2a572bSKonstantin Belousov uint32_t flags; 95ef2a572bSKonstantin Belousov size_t hdr_ext_size; 96ef2a572bSKonstantin Belousov uint64_t cnt_octets; 97ef2a572bSKonstantin Belousov uint64_t cnt_allocs; 98ef2a572bSKonstantin Belousov }; 99ef2a572bSKonstantin Belousov 100ef2a572bSKonstantin Belousov #define IFP_HS_HANDLED 0x00000001 101ef2a572bSKonstantin Belousov #define IFP_HS_REJECTED 0x00000002 102ef2a572bSKonstantin Belousov #define IFP_HS_MARKER 0x00000010 103ef2a572bSKonstantin Belousov 104ef2a572bSKonstantin Belousov static CK_LIST_HEAD(, ifp_handle_sav) ipsec_accel_all_sav_handles; 105ef2a572bSKonstantin Belousov 106ef2a572bSKonstantin Belousov struct ifp_handle_sp { 107ef2a572bSKonstantin Belousov CK_LIST_ENTRY(ifp_handle_sp) sp_link; 108ef2a572bSKonstantin Belousov CK_LIST_ENTRY(ifp_handle_sp) sp_allh_link; 109ef2a572bSKonstantin Belousov struct secpolicy *sp; 110ef2a572bSKonstantin Belousov struct ifnet *ifp; 111ef2a572bSKonstantin Belousov void *ifdata; 112ef2a572bSKonstantin Belousov uint32_t flags; 113ef2a572bSKonstantin Belousov }; 114ef2a572bSKonstantin Belousov 115ef2a572bSKonstantin Belousov #define IFP_HP_HANDLED 0x00000001 116ef2a572bSKonstantin Belousov #define IFP_HP_REJECTED 0x00000002 117ef2a572bSKonstantin Belousov #define IFP_HP_MARKER 0x00000004 118ef2a572bSKonstantin Belousov 119ef2a572bSKonstantin Belousov static CK_LIST_HEAD(, ifp_handle_sp) ipsec_accel_all_sp_handles; 120ef2a572bSKonstantin Belousov 121ef2a572bSKonstantin Belousov static void * 122ef2a572bSKonstantin Belousov drvspi_sa_trie_alloc(struct pctrie *ptree) 123ef2a572bSKonstantin Belousov { 124ef2a572bSKonstantin Belousov void *res; 125ef2a572bSKonstantin Belousov 126ef2a572bSKonstantin Belousov res = malloc(pctrie_node_size(), M_IPSEC_MISC, M_ZERO | M_NOWAIT); 127ef2a572bSKonstantin Belousov if (res != NULL) 128ef2a572bSKonstantin Belousov pctrie_zone_init(res, 0, 0); 129ef2a572bSKonstantin Belousov return (res); 130ef2a572bSKonstantin Belousov } 131ef2a572bSKonstantin Belousov 132ef2a572bSKonstantin Belousov static void 133ef2a572bSKonstantin Belousov drvspi_sa_trie_free(struct pctrie *ptree, void *node) 134ef2a572bSKonstantin Belousov { 135ef2a572bSKonstantin Belousov free(node, M_IPSEC_MISC); 136ef2a572bSKonstantin Belousov } 137ef2a572bSKonstantin Belousov 138ef2a572bSKonstantin Belousov PCTRIE_DEFINE(DRVSPI_SA, ifp_handle_sav, drv_spi, 139ef2a572bSKonstantin Belousov drvspi_sa_trie_alloc, drvspi_sa_trie_free); 140ef2a572bSKonstantin Belousov static struct pctrie drv_spi_pctrie; 141ef2a572bSKonstantin Belousov 142c4a0ee9bSKonstantin Belousov static eventhandler_tag ipsec_accel_ifdetach_event_tag; 143c4a0ee9bSKonstantin Belousov 144ef2a572bSKonstantin Belousov static void ipsec_accel_sa_newkey_impl(struct secasvar *sav); 145ef2a572bSKonstantin Belousov static int ipsec_accel_handle_sav(struct secasvar *sav, struct ifnet *ifp, 146ef2a572bSKonstantin Belousov u_int drv_spi, void *priv, uint32_t flags, struct ifp_handle_sav **ires); 147ef2a572bSKonstantin Belousov static void ipsec_accel_forget_sav_clear(struct secasvar *sav); 148ef2a572bSKonstantin Belousov static struct ifp_handle_sav *ipsec_accel_is_accel_sav_ptr(struct secasvar *sav, 149ef2a572bSKonstantin Belousov struct ifnet *ifp); 150ef2a572bSKonstantin Belousov static int ipsec_accel_sa_lifetime_op_impl(struct secasvar *sav, 151ef2a572bSKonstantin Belousov struct seclifetime *lft_c, if_t ifp, enum IF_SA_CNT_WHICH op, 152ef2a572bSKonstantin Belousov struct rm_priotracker *sahtree_trackerp); 153ef2a572bSKonstantin Belousov static void ipsec_accel_sa_recordxfer(struct secasvar *sav, struct mbuf *m); 154ef2a572bSKonstantin Belousov static void ipsec_accel_sync_imp(void); 155ef2a572bSKonstantin Belousov static bool ipsec_accel_is_accel_sav_impl(struct secasvar *sav); 156ef2a572bSKonstantin Belousov static struct mbuf *ipsec_accel_key_setaccelif_impl(struct secasvar *sav); 15765f264dcSKonstantin Belousov static void ipsec_accel_on_ifdown_impl(struct ifnet *ifp); 15865f264dcSKonstantin Belousov static void ipsec_accel_drv_sa_lifetime_update_impl(struct secasvar *sav, 15965f264dcSKonstantin Belousov if_t ifp, u_int drv_spi, uint64_t octets, uint64_t allocs); 16066f0e201SKonstantin Belousov static int ipsec_accel_drv_sa_lifetime_fetch_impl(struct secasvar *sav, 16166f0e201SKonstantin Belousov if_t ifp, u_int drv_spi, uint64_t *octets, uint64_t *allocs); 162c4a0ee9bSKonstantin Belousov static void ipsec_accel_ifdetach_event(void *arg, struct ifnet *ifp); 163ef2a572bSKonstantin Belousov 164ef2a572bSKonstantin Belousov static void 165ef2a572bSKonstantin Belousov ipsec_accel_init(void *arg) 166ef2a572bSKonstantin Belousov { 167ef2a572bSKonstantin Belousov mtx_init(&ipsec_accel_sav_tmp, "ipasat", MTX_DEF, 0); 168ef2a572bSKonstantin Belousov mtx_init(&ipsec_accel_cnt_lock, "ipascn", MTX_DEF, 0); 169ef2a572bSKonstantin Belousov drv_spi_unr = new_unrhdr(IPSEC_ACCEL_DRV_SPI_MIN, 170ef2a572bSKonstantin Belousov IPSEC_ACCEL_DRV_SPI_MAX, &ipsec_accel_sav_tmp); 171f76826b8SKonstantin Belousov ipsec_accel_tq = taskqueue_create("ipsec_offload", M_WAITOK, 172f76826b8SKonstantin Belousov taskqueue_thread_enqueue, &ipsec_accel_tq); 173f76826b8SKonstantin Belousov (void)taskqueue_start_threads(&ipsec_accel_tq, 174f76826b8SKonstantin Belousov 1 /* Must be single-threaded */, PWAIT, 175f76826b8SKonstantin Belousov "ipsec_offload"); 176ef2a572bSKonstantin Belousov ipsec_accel_sa_newkey_p = ipsec_accel_sa_newkey_impl; 177ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_p = ipsec_accel_forget_sav_impl; 178ef2a572bSKonstantin Belousov ipsec_accel_spdadd_p = ipsec_accel_spdadd_impl; 179ef2a572bSKonstantin Belousov ipsec_accel_spddel_p = ipsec_accel_spddel_impl; 180ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_op_p = ipsec_accel_sa_lifetime_op_impl; 181ef2a572bSKonstantin Belousov ipsec_accel_sync_p = ipsec_accel_sync_imp; 182ef2a572bSKonstantin Belousov ipsec_accel_is_accel_sav_p = ipsec_accel_is_accel_sav_impl; 183ef2a572bSKonstantin Belousov ipsec_accel_key_setaccelif_p = ipsec_accel_key_setaccelif_impl; 18465f264dcSKonstantin Belousov ipsec_accel_on_ifdown_p = ipsec_accel_on_ifdown_impl; 18565f264dcSKonstantin Belousov ipsec_accel_drv_sa_lifetime_update_p = 18665f264dcSKonstantin Belousov ipsec_accel_drv_sa_lifetime_update_impl; 18766f0e201SKonstantin Belousov ipsec_accel_drv_sa_lifetime_fetch_p = 18866f0e201SKonstantin Belousov ipsec_accel_drv_sa_lifetime_fetch_impl; 189ef2a572bSKonstantin Belousov pctrie_init(&drv_spi_pctrie); 190c4a0ee9bSKonstantin Belousov ipsec_accel_ifdetach_event_tag = EVENTHANDLER_REGISTER( 191c4a0ee9bSKonstantin Belousov ifnet_departure_event, ipsec_accel_ifdetach_event, NULL, 192c4a0ee9bSKonstantin Belousov EVENTHANDLER_PRI_ANY); 193ef2a572bSKonstantin Belousov } 194ef2a572bSKonstantin Belousov SYSINIT(ipsec_accel_init, SI_SUB_VNET_DONE, SI_ORDER_ANY, 195ef2a572bSKonstantin Belousov ipsec_accel_init, NULL); 196ef2a572bSKonstantin Belousov 197ef2a572bSKonstantin Belousov static void 198ef2a572bSKonstantin Belousov ipsec_accel_fini(void *arg) 199ef2a572bSKonstantin Belousov { 200c4a0ee9bSKonstantin Belousov EVENTHANDLER_DEREGISTER(ifnet_departure_event, 201c4a0ee9bSKonstantin Belousov ipsec_accel_ifdetach_event_tag); 202ef2a572bSKonstantin Belousov ipsec_accel_sa_newkey_p = NULL; 203ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_p = NULL; 204ef2a572bSKonstantin Belousov ipsec_accel_spdadd_p = NULL; 205ef2a572bSKonstantin Belousov ipsec_accel_spddel_p = NULL; 206ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_op_p = NULL; 207ef2a572bSKonstantin Belousov ipsec_accel_sync_p = NULL; 208ef2a572bSKonstantin Belousov ipsec_accel_is_accel_sav_p = NULL; 209ef2a572bSKonstantin Belousov ipsec_accel_key_setaccelif_p = NULL; 21065f264dcSKonstantin Belousov ipsec_accel_on_ifdown_p = NULL; 21165f264dcSKonstantin Belousov ipsec_accel_drv_sa_lifetime_update_p = NULL; 21266f0e201SKonstantin Belousov ipsec_accel_drv_sa_lifetime_fetch_p = NULL; 213ef2a572bSKonstantin Belousov ipsec_accel_sync_imp(); 214ef2a572bSKonstantin Belousov clean_unrhdr(drv_spi_unr); /* avoid panic, should go later */ 215ef2a572bSKonstantin Belousov clear_unrhdr(drv_spi_unr); 216ef2a572bSKonstantin Belousov delete_unrhdr(drv_spi_unr); 217f76826b8SKonstantin Belousov taskqueue_drain_all(ipsec_accel_tq); 218f76826b8SKonstantin Belousov taskqueue_free(ipsec_accel_tq); 219ef2a572bSKonstantin Belousov mtx_destroy(&ipsec_accel_sav_tmp); 220ef2a572bSKonstantin Belousov mtx_destroy(&ipsec_accel_cnt_lock); 221ef2a572bSKonstantin Belousov } 222ef2a572bSKonstantin Belousov SYSUNINIT(ipsec_accel_fini, SI_SUB_VNET_DONE, SI_ORDER_ANY, 223ef2a572bSKonstantin Belousov ipsec_accel_fini, NULL); 224ef2a572bSKonstantin Belousov 2259a7096ffSKonstantin Belousov SYSCTL_NODE(_net_inet_ipsec, OID_AUTO, offload, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 2269a7096ffSKonstantin Belousov ""); 2279a7096ffSKonstantin Belousov 2289a7096ffSKonstantin Belousov static bool ipsec_offload_verbose = false; 2299a7096ffSKonstantin Belousov SYSCTL_BOOL(_net_inet_ipsec_offload, OID_AUTO, verbose, CTLFLAG_RW, 2309a7096ffSKonstantin Belousov &ipsec_offload_verbose, 0, 2319a7096ffSKonstantin Belousov "Verbose SA/SP offload install and deinstall"); 2329a7096ffSKonstantin Belousov 2339a7096ffSKonstantin Belousov static void 2349a7096ffSKonstantin Belousov dprintf(const char *fmt, ...) 2359a7096ffSKonstantin Belousov { 2369a7096ffSKonstantin Belousov va_list ap; 2379a7096ffSKonstantin Belousov 2389a7096ffSKonstantin Belousov if (!ipsec_offload_verbose) 2399a7096ffSKonstantin Belousov return; 2409a7096ffSKonstantin Belousov 2419a7096ffSKonstantin Belousov va_start(ap, fmt); 2429a7096ffSKonstantin Belousov vprintf(fmt, ap); 2439a7096ffSKonstantin Belousov va_end(ap); 2449a7096ffSKonstantin Belousov } 2459a7096ffSKonstantin Belousov 246ef2a572bSKonstantin Belousov static void 247ef2a572bSKonstantin Belousov ipsec_accel_alloc_forget_tq(struct secasvar *sav) 248ef2a572bSKonstantin Belousov { 249ef2a572bSKonstantin Belousov void *ftq; 250ef2a572bSKonstantin Belousov 251ef2a572bSKonstantin Belousov if (sav->accel_forget_tq != 0) 252ef2a572bSKonstantin Belousov return; 253ef2a572bSKonstantin Belousov 254ef2a572bSKonstantin Belousov ftq = malloc(sizeof(struct ipsec_accel_forget_tq), M_TEMP, M_WAITOK); 255ef2a572bSKonstantin Belousov if (!atomic_cmpset_ptr(&sav->accel_forget_tq, 0, (uintptr_t)ftq)) 256ef2a572bSKonstantin Belousov free(ftq, M_TEMP); 257ef2a572bSKonstantin Belousov } 258ef2a572bSKonstantin Belousov 259ef2a572bSKonstantin Belousov static bool 260ef2a572bSKonstantin Belousov ipsec_accel_sa_install_match(if_t ifp, void *arg) 261ef2a572bSKonstantin Belousov { 262ef2a572bSKonstantin Belousov if ((ifp->if_capenable2 & IFCAP2_BIT(IFCAP2_IPSEC_OFFLOAD)) == 0) 263ef2a572bSKonstantin Belousov return (false); 264ef2a572bSKonstantin Belousov if (ifp->if_ipsec_accel_m->if_sa_newkey == NULL) { 2659a7096ffSKonstantin Belousov dprintf("driver bug ifp %s if_sa_newkey NULL\n", 266ef2a572bSKonstantin Belousov if_name(ifp)); 267ef2a572bSKonstantin Belousov return (false); 268ef2a572bSKonstantin Belousov } 269ef2a572bSKonstantin Belousov return (true); 270ef2a572bSKonstantin Belousov } 271ef2a572bSKonstantin Belousov 272ef2a572bSKonstantin Belousov static int 273ef2a572bSKonstantin Belousov ipsec_accel_sa_newkey_cb(if_t ifp, void *arg) 274ef2a572bSKonstantin Belousov { 275ef2a572bSKonstantin Belousov struct ipsec_accel_install_newkey_tq *tq; 276ef2a572bSKonstantin Belousov void *priv; 277ef2a572bSKonstantin Belousov u_int drv_spi; 278ef2a572bSKonstantin Belousov int error; 279ef2a572bSKonstantin Belousov 280ef2a572bSKonstantin Belousov tq = arg; 281ef2a572bSKonstantin Belousov 2829a7096ffSKonstantin Belousov dprintf("ipsec_accel_sa_newkey_act: ifp %s h %p spi %#x " 283ef2a572bSKonstantin Belousov "flags %#x seq %d\n", 284ef2a572bSKonstantin Belousov if_name(ifp), ifp->if_ipsec_accel_m->if_sa_newkey, 285ef2a572bSKonstantin Belousov be32toh(tq->sav->spi), tq->sav->flags, tq->sav->seq); 286ef2a572bSKonstantin Belousov priv = NULL; 287ef2a572bSKonstantin Belousov drv_spi = alloc_unr(drv_spi_unr); 288ef2a572bSKonstantin Belousov if (tq->sav->accel_ifname != NULL && 289ef2a572bSKonstantin Belousov strcmp(tq->sav->accel_ifname, if_name(ifp)) != 0) { 290ef2a572bSKonstantin Belousov error = ipsec_accel_handle_sav(tq->sav, 291ef2a572bSKonstantin Belousov ifp, drv_spi, priv, IFP_HS_REJECTED, NULL); 292ef2a572bSKonstantin Belousov goto out; 293ef2a572bSKonstantin Belousov } 294ef2a572bSKonstantin Belousov if (drv_spi == -1) { 295ef2a572bSKonstantin Belousov /* XXXKIB */ 2969a7096ffSKonstantin Belousov dprintf("ipsec_accel_sa_install_newkey: cannot alloc " 297ef2a572bSKonstantin Belousov "drv_spi if %s spi %#x\n", if_name(ifp), 298ef2a572bSKonstantin Belousov be32toh(tq->sav->spi)); 299ef2a572bSKonstantin Belousov return (ENOMEM); 300ef2a572bSKonstantin Belousov } 301ef2a572bSKonstantin Belousov error = ifp->if_ipsec_accel_m->if_sa_newkey(ifp, tq->sav, 302ef2a572bSKonstantin Belousov drv_spi, &priv); 303ef2a572bSKonstantin Belousov if (error != 0) { 304ef2a572bSKonstantin Belousov if (error == EOPNOTSUPP) { 3059a7096ffSKonstantin Belousov dprintf("ipsec_accel_sa_newkey: driver " 306ef2a572bSKonstantin Belousov "refused sa if %s spi %#x\n", 307ef2a572bSKonstantin Belousov if_name(ifp), be32toh(tq->sav->spi)); 308ef2a572bSKonstantin Belousov error = ipsec_accel_handle_sav(tq->sav, 309ef2a572bSKonstantin Belousov ifp, drv_spi, priv, IFP_HS_REJECTED, NULL); 310ef2a572bSKonstantin Belousov /* XXXKIB */ 311ef2a572bSKonstantin Belousov } else { 3129a7096ffSKonstantin Belousov dprintf("ipsec_accel_sa_newkey: driver " 313ef2a572bSKonstantin Belousov "error %d if %s spi %#x\n", 314ef2a572bSKonstantin Belousov error, if_name(ifp), be32toh(tq->sav->spi)); 315ef2a572bSKonstantin Belousov /* XXXKIB */ 316ef2a572bSKonstantin Belousov } 317ef2a572bSKonstantin Belousov } else { 318ef2a572bSKonstantin Belousov error = ipsec_accel_handle_sav(tq->sav, ifp, 319ef2a572bSKonstantin Belousov drv_spi, priv, IFP_HS_HANDLED, NULL); 320ef2a572bSKonstantin Belousov if (error != 0) { 321ef2a572bSKonstantin Belousov /* XXXKIB */ 3229a7096ffSKonstantin Belousov dprintf("ipsec_accel_sa_newkey: handle_sav " 323ef2a572bSKonstantin Belousov "err %d if %s spi %#x\n", error, 324ef2a572bSKonstantin Belousov if_name(ifp), be32toh(tq->sav->spi)); 325ef2a572bSKonstantin Belousov } 326ef2a572bSKonstantin Belousov } 327ef2a572bSKonstantin Belousov out: 328ef2a572bSKonstantin Belousov return (error); 329ef2a572bSKonstantin Belousov } 330ef2a572bSKonstantin Belousov 331ef2a572bSKonstantin Belousov static void 332ef2a572bSKonstantin Belousov ipsec_accel_sa_newkey_act(void *context, int pending) 333ef2a572bSKonstantin Belousov { 334ef2a572bSKonstantin Belousov struct ipsec_accel_install_newkey_tq *tq; 335ef2a572bSKonstantin Belousov void *tqf; 336ef2a572bSKonstantin Belousov struct secasvar *sav; 337ef2a572bSKonstantin Belousov 338ef2a572bSKonstantin Belousov tq = context; 339ef2a572bSKonstantin Belousov tqf = NULL; 340ef2a572bSKonstantin Belousov sav = tq->sav; 341ef2a572bSKonstantin Belousov CURVNET_SET(tq->install_vnet); 342ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 343ef2a572bSKonstantin Belousov if ((sav->accel_flags & (SADB_KEY_ACCEL_INST | 344ef2a572bSKonstantin Belousov SADB_KEY_ACCEL_DEINST)) == 0 && 345ef2a572bSKonstantin Belousov sav->state == SADB_SASTATE_MATURE) { 346ef2a572bSKonstantin Belousov sav->accel_flags |= SADB_KEY_ACCEL_INST; 347ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 348ef2a572bSKonstantin Belousov if_foreach_sleep(ipsec_accel_sa_install_match, context, 349ef2a572bSKonstantin Belousov ipsec_accel_sa_newkey_cb, context); 350ef2a572bSKonstantin Belousov ipsec_accel_alloc_forget_tq(sav); 351ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 352ef2a572bSKonstantin Belousov 353ef2a572bSKonstantin Belousov /* 354ef2a572bSKonstantin Belousov * If ipsec_accel_forget_sav() raced with us and set 355ef2a572bSKonstantin Belousov * the flag, do its work. Its task cannot execute in 356f76826b8SKonstantin Belousov * parallel since ipsec_accel taskqueue is single-threaded. 357ef2a572bSKonstantin Belousov */ 358ef2a572bSKonstantin Belousov if ((sav->accel_flags & SADB_KEY_ACCEL_DEINST) != 0) { 359ef2a572bSKonstantin Belousov tqf = (void *)sav->accel_forget_tq; 360ef2a572bSKonstantin Belousov sav->accel_forget_tq = 0; 361ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_clear(sav); 362ef2a572bSKonstantin Belousov } 363ef2a572bSKonstantin Belousov } 364ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 365ef2a572bSKonstantin Belousov key_freesav(&tq->sav); 366ef2a572bSKonstantin Belousov CURVNET_RESTORE(); 367ef2a572bSKonstantin Belousov free(tq, M_TEMP); 368ef2a572bSKonstantin Belousov free(tqf, M_TEMP); 369ef2a572bSKonstantin Belousov } 370ef2a572bSKonstantin Belousov 371ef2a572bSKonstantin Belousov static void 372ef2a572bSKonstantin Belousov ipsec_accel_sa_newkey_impl(struct secasvar *sav) 373ef2a572bSKonstantin Belousov { 374ef2a572bSKonstantin Belousov struct ipsec_accel_install_newkey_tq *tq; 375ef2a572bSKonstantin Belousov 376ef2a572bSKonstantin Belousov if ((sav->accel_flags & (SADB_KEY_ACCEL_INST | 377ef2a572bSKonstantin Belousov SADB_KEY_ACCEL_DEINST)) != 0) 378ef2a572bSKonstantin Belousov return; 379ef2a572bSKonstantin Belousov 3809a7096ffSKonstantin Belousov dprintf( 381ef2a572bSKonstantin Belousov "ipsec_accel_sa_install_newkey: spi %#x flags %#x seq %d\n", 382ef2a572bSKonstantin Belousov be32toh(sav->spi), sav->flags, sav->seq); 383ef2a572bSKonstantin Belousov 384ef2a572bSKonstantin Belousov tq = malloc(sizeof(*tq), M_TEMP, M_NOWAIT); 385ef2a572bSKonstantin Belousov if (tq == NULL) { 3869a7096ffSKonstantin Belousov dprintf("ipsec_accel_sa_install_newkey: no memory for tq, " 387ef2a572bSKonstantin Belousov "spi %#x\n", be32toh(sav->spi)); 388ef2a572bSKonstantin Belousov /* XXXKIB */ 389ef2a572bSKonstantin Belousov return; 390ef2a572bSKonstantin Belousov } 391ef2a572bSKonstantin Belousov 392ef2a572bSKonstantin Belousov refcount_acquire(&sav->refcnt); 393ef2a572bSKonstantin Belousov 394ef2a572bSKonstantin Belousov TASK_INIT(&tq->install_task, 0, ipsec_accel_sa_newkey_act, tq); 395ef2a572bSKonstantin Belousov tq->sav = sav; 39601f43479SMark Johnston tq->install_vnet = curthread->td_vnet; 397f76826b8SKonstantin Belousov taskqueue_enqueue(ipsec_accel_tq, &tq->install_task); 398ef2a572bSKonstantin Belousov } 399ef2a572bSKonstantin Belousov 400ef2a572bSKonstantin Belousov static int 401ef2a572bSKonstantin Belousov ipsec_accel_handle_sav(struct secasvar *sav, struct ifnet *ifp, 402ef2a572bSKonstantin Belousov u_int drv_spi, void *priv, uint32_t flags, struct ifp_handle_sav **ires) 403ef2a572bSKonstantin Belousov { 404ef2a572bSKonstantin Belousov struct ifp_handle_sav *ihs, *i; 405ef2a572bSKonstantin Belousov int error; 406ef2a572bSKonstantin Belousov 407ef2a572bSKonstantin Belousov MPASS(__bitcount(flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) == 1); 408ef2a572bSKonstantin Belousov 409ef2a572bSKonstantin Belousov ihs = malloc(sizeof(*ihs), M_IPSEC_MISC, M_WAITOK | M_ZERO); 410ef2a572bSKonstantin Belousov ihs->ifp = ifp; 411ef2a572bSKonstantin Belousov ihs->sav = sav; 412ef2a572bSKonstantin Belousov ihs->drv_spi = drv_spi; 413ef2a572bSKonstantin Belousov ihs->ifdata = priv; 414ef2a572bSKonstantin Belousov ihs->flags = flags; 415ef2a572bSKonstantin Belousov ihs->hdr_ext_size = esp_hdrsiz(sav); 416ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 417ef2a572bSKonstantin Belousov CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) { 418ef2a572bSKonstantin Belousov if (i->ifp == ifp) { 419ef2a572bSKonstantin Belousov error = EALREADY; 420ef2a572bSKonstantin Belousov goto errout; 421ef2a572bSKonstantin Belousov } 422ef2a572bSKonstantin Belousov } 423ef2a572bSKonstantin Belousov error = DRVSPI_SA_PCTRIE_INSERT(&drv_spi_pctrie, ihs); 424ef2a572bSKonstantin Belousov if (error != 0) 425ef2a572bSKonstantin Belousov goto errout; 426ef2a572bSKonstantin Belousov if_ref(ihs->ifp); 427ef2a572bSKonstantin Belousov CK_LIST_INSERT_HEAD(&sav->accel_ifps, ihs, sav_link); 428ef2a572bSKonstantin Belousov CK_LIST_INSERT_HEAD(&ipsec_accel_all_sav_handles, ihs, sav_allh_link); 429ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 430ef2a572bSKonstantin Belousov if (ires != NULL) 431ef2a572bSKonstantin Belousov *ires = ihs; 432ef2a572bSKonstantin Belousov return (0); 433ef2a572bSKonstantin Belousov errout: 434ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 435ef2a572bSKonstantin Belousov free(ihs, M_IPSEC_MISC); 436ef2a572bSKonstantin Belousov if (ires != NULL) 437ef2a572bSKonstantin Belousov *ires = NULL; 438ef2a572bSKonstantin Belousov return (error); 439ef2a572bSKonstantin Belousov } 440ef2a572bSKonstantin Belousov 441ef2a572bSKonstantin Belousov static void 442ef2a572bSKonstantin Belousov ipsec_accel_forget_handle_sav(struct ifp_handle_sav *i, bool freesav) 443ef2a572bSKonstantin Belousov { 444ef2a572bSKonstantin Belousov struct ifnet *ifp; 445ef2a572bSKonstantin Belousov struct secasvar *sav; 446ef2a572bSKonstantin Belousov 447ef2a572bSKonstantin Belousov mtx_assert(&ipsec_accel_sav_tmp, MA_OWNED); 448ef2a572bSKonstantin Belousov 449ef2a572bSKonstantin Belousov CK_LIST_REMOVE(i, sav_link); 450ef2a572bSKonstantin Belousov CK_LIST_REMOVE(i, sav_allh_link); 451ef2a572bSKonstantin Belousov DRVSPI_SA_PCTRIE_REMOVE(&drv_spi_pctrie, i->drv_spi); 452ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 453ef2a572bSKonstantin Belousov NET_EPOCH_WAIT(); 454ef2a572bSKonstantin Belousov ifp = i->ifp; 455ef2a572bSKonstantin Belousov sav = i->sav; 456ef2a572bSKonstantin Belousov if ((i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) == 457ef2a572bSKonstantin Belousov IFP_HS_HANDLED) { 4589a7096ffSKonstantin Belousov dprintf("sa deinstall %s %p spi %#x ifl %#x\n", 459ef2a572bSKonstantin Belousov if_name(ifp), sav, be32toh(sav->spi), i->flags); 460ef2a572bSKonstantin Belousov ifp->if_ipsec_accel_m->if_sa_deinstall(ifp, 461ef2a572bSKonstantin Belousov i->drv_spi, i->ifdata); 462ef2a572bSKonstantin Belousov } 463ef2a572bSKonstantin Belousov if_rele(ifp); 464ef2a572bSKonstantin Belousov free_unr(drv_spi_unr, i->drv_spi); 465ef2a572bSKonstantin Belousov free(i, M_IPSEC_MISC); 466ef2a572bSKonstantin Belousov if (freesav) 467ef2a572bSKonstantin Belousov key_freesav(&sav); 468ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 469ef2a572bSKonstantin Belousov } 470ef2a572bSKonstantin Belousov 471ef2a572bSKonstantin Belousov static void 472ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_clear(struct secasvar *sav) 473ef2a572bSKonstantin Belousov { 474ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 475ef2a572bSKonstantin Belousov 476ef2a572bSKonstantin Belousov for (;;) { 477ef2a572bSKonstantin Belousov i = CK_LIST_FIRST(&sav->accel_ifps); 478ef2a572bSKonstantin Belousov if (i == NULL) 479ef2a572bSKonstantin Belousov break; 480ef2a572bSKonstantin Belousov ipsec_accel_forget_handle_sav(i, false); 481ef2a572bSKonstantin Belousov } 482ef2a572bSKonstantin Belousov } 483ef2a572bSKonstantin Belousov 484ef2a572bSKonstantin Belousov static void 485ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_act(void *arg, int pending) 486ef2a572bSKonstantin Belousov { 487ef2a572bSKonstantin Belousov struct ipsec_accel_forget_tq *tq; 488ef2a572bSKonstantin Belousov struct secasvar *sav; 489ef2a572bSKonstantin Belousov 490ef2a572bSKonstantin Belousov tq = arg; 491ef2a572bSKonstantin Belousov sav = tq->sav; 492ef2a572bSKonstantin Belousov CURVNET_SET(tq->forget_vnet); 493ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 494ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_clear(sav); 495ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 496ef2a572bSKonstantin Belousov key_freesav(&sav); 497ef2a572bSKonstantin Belousov CURVNET_RESTORE(); 498ef2a572bSKonstantin Belousov free(tq, M_TEMP); 499ef2a572bSKonstantin Belousov } 500ef2a572bSKonstantin Belousov 501ef2a572bSKonstantin Belousov void 502ef2a572bSKonstantin Belousov ipsec_accel_forget_sav_impl(struct secasvar *sav) 503ef2a572bSKonstantin Belousov { 504ef2a572bSKonstantin Belousov struct ipsec_accel_forget_tq *tq; 505ef2a572bSKonstantin Belousov 506ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 507ef2a572bSKonstantin Belousov sav->accel_flags |= SADB_KEY_ACCEL_DEINST; 508ef2a572bSKonstantin Belousov tq = (void *)atomic_load_ptr(&sav->accel_forget_tq); 509ef2a572bSKonstantin Belousov if (tq == NULL || !atomic_cmpset_ptr(&sav->accel_forget_tq, 510ef2a572bSKonstantin Belousov (uintptr_t)tq, 0)) { 511ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 512ef2a572bSKonstantin Belousov return; 513ef2a572bSKonstantin Belousov } 514ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 515ef2a572bSKonstantin Belousov 516ef2a572bSKonstantin Belousov refcount_acquire(&sav->refcnt); 517ef2a572bSKonstantin Belousov TASK_INIT(&tq->forget_task, 0, ipsec_accel_forget_sav_act, tq); 518ef2a572bSKonstantin Belousov tq->forget_vnet = curthread->td_vnet; 519ef2a572bSKonstantin Belousov tq->sav = sav; 520f76826b8SKonstantin Belousov taskqueue_enqueue(ipsec_accel_tq, &tq->forget_task); 521ef2a572bSKonstantin Belousov } 522ef2a572bSKonstantin Belousov 523ef2a572bSKonstantin Belousov static void 524ef2a572bSKonstantin Belousov ipsec_accel_on_ifdown_sav(struct ifnet *ifp) 525ef2a572bSKonstantin Belousov { 526ef2a572bSKonstantin Belousov struct ifp_handle_sav *i, *marker; 527ef2a572bSKonstantin Belousov 528ef2a572bSKonstantin Belousov marker = malloc(sizeof(*marker), M_IPSEC_MISC, M_WAITOK | M_ZERO); 529ef2a572bSKonstantin Belousov marker->flags = IFP_HS_MARKER; 530ef2a572bSKonstantin Belousov 531ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 532ef2a572bSKonstantin Belousov CK_LIST_INSERT_HEAD(&ipsec_accel_all_sav_handles, marker, 533ef2a572bSKonstantin Belousov sav_allh_link); 534ef2a572bSKonstantin Belousov for (;;) { 535ef2a572bSKonstantin Belousov i = CK_LIST_NEXT(marker, sav_allh_link); 536ef2a572bSKonstantin Belousov if (i == NULL) 537ef2a572bSKonstantin Belousov break; 538ef2a572bSKonstantin Belousov CK_LIST_REMOVE(marker, sav_allh_link); 539ef2a572bSKonstantin Belousov CK_LIST_INSERT_AFTER(i, marker, sav_allh_link); 540ef2a572bSKonstantin Belousov if (i->ifp == ifp) { 541ef2a572bSKonstantin Belousov refcount_acquire(&i->sav->refcnt); /* XXXKIB wrap ? */ 542ef2a572bSKonstantin Belousov ipsec_accel_forget_handle_sav(i, true); 543ef2a572bSKonstantin Belousov } 544ef2a572bSKonstantin Belousov } 545ef2a572bSKonstantin Belousov CK_LIST_REMOVE(marker, sav_allh_link); 546ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 547ef2a572bSKonstantin Belousov free(marker, M_IPSEC_MISC); 548ef2a572bSKonstantin Belousov } 549ef2a572bSKonstantin Belousov 550ef2a572bSKonstantin Belousov static struct ifp_handle_sav * 551ef2a572bSKonstantin Belousov ipsec_accel_is_accel_sav_ptr_raw(struct secasvar *sav, struct ifnet *ifp) 552ef2a572bSKonstantin Belousov { 553ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 554ef2a572bSKonstantin Belousov 555ef2a572bSKonstantin Belousov if ((ifp->if_capenable2 & IFCAP2_BIT(IFCAP2_IPSEC_OFFLOAD)) == 0) 556ef2a572bSKonstantin Belousov return (NULL); 557ef2a572bSKonstantin Belousov CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) { 558ef2a572bSKonstantin Belousov if (i->ifp == ifp) 559ef2a572bSKonstantin Belousov return (i); 560ef2a572bSKonstantin Belousov } 561ef2a572bSKonstantin Belousov return (NULL); 562ef2a572bSKonstantin Belousov } 563ef2a572bSKonstantin Belousov 564ef2a572bSKonstantin Belousov static struct ifp_handle_sav * 565ef2a572bSKonstantin Belousov ipsec_accel_is_accel_sav_ptr(struct secasvar *sav, struct ifnet *ifp) 566ef2a572bSKonstantin Belousov { 567ef2a572bSKonstantin Belousov NET_EPOCH_ASSERT(); 568ef2a572bSKonstantin Belousov return (ipsec_accel_is_accel_sav_ptr_raw(sav, ifp)); 569ef2a572bSKonstantin Belousov } 570ef2a572bSKonstantin Belousov 571ef2a572bSKonstantin Belousov static bool 572ef2a572bSKonstantin Belousov ipsec_accel_is_accel_sav_impl(struct secasvar *sav) 573ef2a572bSKonstantin Belousov { 574ef2a572bSKonstantin Belousov return (!CK_LIST_EMPTY(&sav->accel_ifps)); 575ef2a572bSKonstantin Belousov } 576ef2a572bSKonstantin Belousov 577ef2a572bSKonstantin Belousov static struct secasvar * 578ef2a572bSKonstantin Belousov ipsec_accel_drvspi_to_sa(u_int drv_spi) 579ef2a572bSKonstantin Belousov { 580ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 581ef2a572bSKonstantin Belousov 582ef2a572bSKonstantin Belousov i = DRVSPI_SA_PCTRIE_LOOKUP(&drv_spi_pctrie, drv_spi); 583ef2a572bSKonstantin Belousov if (i == NULL) 584ef2a572bSKonstantin Belousov return (NULL); 585ef2a572bSKonstantin Belousov return (i->sav); 586ef2a572bSKonstantin Belousov } 587ef2a572bSKonstantin Belousov 588ef2a572bSKonstantin Belousov static struct ifp_handle_sp * 589ef2a572bSKonstantin Belousov ipsec_accel_find_accel_sp(struct secpolicy *sp, if_t ifp) 590ef2a572bSKonstantin Belousov { 591ef2a572bSKonstantin Belousov struct ifp_handle_sp *i; 592ef2a572bSKonstantin Belousov 593ef2a572bSKonstantin Belousov CK_LIST_FOREACH(i, &sp->accel_ifps, sp_link) { 594ef2a572bSKonstantin Belousov if (i->ifp == ifp) 595ef2a572bSKonstantin Belousov return (i); 596ef2a572bSKonstantin Belousov } 597ef2a572bSKonstantin Belousov return (NULL); 598ef2a572bSKonstantin Belousov } 599ef2a572bSKonstantin Belousov 600ef2a572bSKonstantin Belousov static bool 601ef2a572bSKonstantin Belousov ipsec_accel_is_accel_sp(struct secpolicy *sp, if_t ifp) 602ef2a572bSKonstantin Belousov { 603ef2a572bSKonstantin Belousov return (ipsec_accel_find_accel_sp(sp, ifp) != NULL); 604ef2a572bSKonstantin Belousov } 605ef2a572bSKonstantin Belousov 606ef2a572bSKonstantin Belousov static int 607ef2a572bSKonstantin Belousov ipsec_accel_remember_sp(struct secpolicy *sp, if_t ifp, 608ef2a572bSKonstantin Belousov struct ifp_handle_sp **ip) 609ef2a572bSKonstantin Belousov { 610ef2a572bSKonstantin Belousov struct ifp_handle_sp *i; 611ef2a572bSKonstantin Belousov 612ef2a572bSKonstantin Belousov i = malloc(sizeof(*i), M_IPSEC_MISC, M_WAITOK | M_ZERO); 613ef2a572bSKonstantin Belousov i->sp = sp; 614ef2a572bSKonstantin Belousov i->ifp = ifp; 615ef2a572bSKonstantin Belousov if_ref(ifp); 616ef2a572bSKonstantin Belousov i->flags = IFP_HP_HANDLED; 617ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 618ef2a572bSKonstantin Belousov CK_LIST_INSERT_HEAD(&sp->accel_ifps, i, sp_link); 619ef2a572bSKonstantin Belousov CK_LIST_INSERT_HEAD(&ipsec_accel_all_sp_handles, i, sp_allh_link); 620ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 621ef2a572bSKonstantin Belousov *ip = i; 622ef2a572bSKonstantin Belousov return (0); 623ef2a572bSKonstantin Belousov } 624ef2a572bSKonstantin Belousov 625ef2a572bSKonstantin Belousov static bool 626ef2a572bSKonstantin Belousov ipsec_accel_spdadd_match(if_t ifp, void *arg) 627ef2a572bSKonstantin Belousov { 628ef2a572bSKonstantin Belousov struct secpolicy *sp; 629ef2a572bSKonstantin Belousov 630ef2a572bSKonstantin Belousov if ((ifp->if_capenable2 & IFCAP2_BIT(IFCAP2_IPSEC_OFFLOAD)) == 0 || 631ef2a572bSKonstantin Belousov ifp->if_ipsec_accel_m->if_spdadd == NULL) 632ef2a572bSKonstantin Belousov return (false); 633ef2a572bSKonstantin Belousov sp = arg; 634ef2a572bSKonstantin Belousov if (sp->accel_ifname != NULL && 635ef2a572bSKonstantin Belousov strcmp(sp->accel_ifname, if_name(ifp)) != 0) 636ef2a572bSKonstantin Belousov return (false); 637ef2a572bSKonstantin Belousov if (ipsec_accel_is_accel_sp(sp, ifp)) 638ef2a572bSKonstantin Belousov return (false); 639ef2a572bSKonstantin Belousov return (true); 640ef2a572bSKonstantin Belousov } 641ef2a572bSKonstantin Belousov 642ef2a572bSKonstantin Belousov static int 643ef2a572bSKonstantin Belousov ipsec_accel_spdadd_cb(if_t ifp, void *arg) 644ef2a572bSKonstantin Belousov { 645ef2a572bSKonstantin Belousov struct secpolicy *sp; 646ef2a572bSKonstantin Belousov struct inpcb *inp; 647ef2a572bSKonstantin Belousov struct ifp_handle_sp *i; 648ef2a572bSKonstantin Belousov int error; 649ef2a572bSKonstantin Belousov 650ef2a572bSKonstantin Belousov sp = arg; 651ef2a572bSKonstantin Belousov inp = sp->ipsec_accel_add_sp_inp; 6529a7096ffSKonstantin Belousov dprintf("ipsec_accel_spdadd_cb: ifp %s m %p sp %p inp %p\n", 653ef2a572bSKonstantin Belousov if_name(ifp), ifp->if_ipsec_accel_m->if_spdadd, sp, inp); 654ef2a572bSKonstantin Belousov error = ipsec_accel_remember_sp(sp, ifp, &i); 655ef2a572bSKonstantin Belousov if (error != 0) { 6569a7096ffSKonstantin Belousov dprintf("ipsec_accel_spdadd: %s if_spdadd %p remember res %d\n", 657ef2a572bSKonstantin Belousov if_name(ifp), sp, error); 658ef2a572bSKonstantin Belousov return (error); 659ef2a572bSKonstantin Belousov } 660ef2a572bSKonstantin Belousov error = ifp->if_ipsec_accel_m->if_spdadd(ifp, sp, inp, &i->ifdata); 661ef2a572bSKonstantin Belousov if (error != 0) { 662ef2a572bSKonstantin Belousov i->flags |= IFP_HP_REJECTED; 6639a7096ffSKonstantin Belousov dprintf("ipsec_accel_spdadd: %s if_spdadd %p res %d\n", 664ef2a572bSKonstantin Belousov if_name(ifp), sp, error); 665ef2a572bSKonstantin Belousov } 666ef2a572bSKonstantin Belousov return (error); 667ef2a572bSKonstantin Belousov } 668ef2a572bSKonstantin Belousov 669ef2a572bSKonstantin Belousov static void 670ef2a572bSKonstantin Belousov ipsec_accel_spdadd_act(void *arg, int pending) 671ef2a572bSKonstantin Belousov { 672ef2a572bSKonstantin Belousov struct secpolicy *sp; 673ef2a572bSKonstantin Belousov struct inpcb *inp; 674ef2a572bSKonstantin Belousov 675ef2a572bSKonstantin Belousov sp = arg; 676ef2a572bSKonstantin Belousov CURVNET_SET(sp->accel_add_tq.adddel_vnet); 677ef2a572bSKonstantin Belousov if_foreach_sleep(ipsec_accel_spdadd_match, arg, 678ef2a572bSKonstantin Belousov ipsec_accel_spdadd_cb, arg); 679ef2a572bSKonstantin Belousov inp = sp->ipsec_accel_add_sp_inp; 680ef2a572bSKonstantin Belousov if (inp != NULL) { 681ef2a572bSKonstantin Belousov INP_WLOCK(inp); 682ef2a572bSKonstantin Belousov if (!in_pcbrele_wlocked(inp)) 683ef2a572bSKonstantin Belousov INP_WUNLOCK(inp); 684ef2a572bSKonstantin Belousov sp->ipsec_accel_add_sp_inp = NULL; 685ef2a572bSKonstantin Belousov } 686ef2a572bSKonstantin Belousov CURVNET_RESTORE(); 687ef2a572bSKonstantin Belousov key_freesp(&sp); 688ef2a572bSKonstantin Belousov } 689ef2a572bSKonstantin Belousov 690ef2a572bSKonstantin Belousov void 691ef2a572bSKonstantin Belousov ipsec_accel_spdadd_impl(struct secpolicy *sp, struct inpcb *inp) 692ef2a572bSKonstantin Belousov { 693ef2a572bSKonstantin Belousov struct ipsec_accel_adddel_sp_tq *tq; 694ef2a572bSKonstantin Belousov 695ef2a572bSKonstantin Belousov if (sp == NULL) 696ef2a572bSKonstantin Belousov return; 697ef2a572bSKonstantin Belousov if (sp->tcount == 0 && inp == NULL) 698ef2a572bSKonstantin Belousov return; 699ef2a572bSKonstantin Belousov tq = &sp->accel_add_tq; 700ef2a572bSKonstantin Belousov if (atomic_cmpset_int(&tq->adddel_scheduled, 0, 1) == 0) 701ef2a572bSKonstantin Belousov return; 702ef2a572bSKonstantin Belousov tq->adddel_vnet = curthread->td_vnet; 703ef2a572bSKonstantin Belousov sp->ipsec_accel_add_sp_inp = inp; 704ef2a572bSKonstantin Belousov if (inp != NULL) 705ef2a572bSKonstantin Belousov in_pcbref(inp); 706ef2a572bSKonstantin Belousov TASK_INIT(&tq->adddel_task, 0, ipsec_accel_spdadd_act, sp); 707ef2a572bSKonstantin Belousov key_addref(sp); 708f76826b8SKonstantin Belousov taskqueue_enqueue(ipsec_accel_tq, &tq->adddel_task); 709ef2a572bSKonstantin Belousov } 710ef2a572bSKonstantin Belousov 711ef2a572bSKonstantin Belousov static void 712ef2a572bSKonstantin Belousov ipsec_accel_spddel_act(void *arg, int pending) 713ef2a572bSKonstantin Belousov { 714ef2a572bSKonstantin Belousov struct ifp_handle_sp *i; 715ef2a572bSKonstantin Belousov struct secpolicy *sp; 716ef2a572bSKonstantin Belousov int error; 717ef2a572bSKonstantin Belousov 718ef2a572bSKonstantin Belousov sp = arg; 719ef2a572bSKonstantin Belousov CURVNET_SET(sp->accel_del_tq.adddel_vnet); 720ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 721ef2a572bSKonstantin Belousov for (;;) { 722ef2a572bSKonstantin Belousov i = CK_LIST_FIRST(&sp->accel_ifps); 723ef2a572bSKonstantin Belousov if (i == NULL) 724ef2a572bSKonstantin Belousov break; 725ef2a572bSKonstantin Belousov CK_LIST_REMOVE(i, sp_link); 726ef2a572bSKonstantin Belousov CK_LIST_REMOVE(i, sp_allh_link); 727ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 728ef2a572bSKonstantin Belousov NET_EPOCH_WAIT(); 729ef2a572bSKonstantin Belousov if ((i->flags & (IFP_HP_HANDLED | IFP_HP_REJECTED)) == 730ef2a572bSKonstantin Belousov IFP_HP_HANDLED) { 7319a7096ffSKonstantin Belousov dprintf("spd deinstall %s %p\n", if_name(i->ifp), sp); 732ef2a572bSKonstantin Belousov error = i->ifp->if_ipsec_accel_m->if_spddel(i->ifp, 733ef2a572bSKonstantin Belousov sp, i->ifdata); 734ef2a572bSKonstantin Belousov if (error != 0) { 7359a7096ffSKonstantin Belousov dprintf( 736ef2a572bSKonstantin Belousov "ipsec_accel_spddel: %s if_spddel %p res %d\n", 737ef2a572bSKonstantin Belousov if_name(i->ifp), sp, error); 738ef2a572bSKonstantin Belousov } 739ef2a572bSKonstantin Belousov } 740ef2a572bSKonstantin Belousov if_rele(i->ifp); 741ef2a572bSKonstantin Belousov free(i, M_IPSEC_MISC); 742ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 743ef2a572bSKonstantin Belousov } 744ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 745ef2a572bSKonstantin Belousov key_freesp(&sp); 746ef2a572bSKonstantin Belousov CURVNET_RESTORE(); 747ef2a572bSKonstantin Belousov } 748ef2a572bSKonstantin Belousov 749ef2a572bSKonstantin Belousov void 750ef2a572bSKonstantin Belousov ipsec_accel_spddel_impl(struct secpolicy *sp) 751ef2a572bSKonstantin Belousov { 752ef2a572bSKonstantin Belousov struct ipsec_accel_adddel_sp_tq *tq; 753ef2a572bSKonstantin Belousov 754ef2a572bSKonstantin Belousov if (sp == NULL) 755ef2a572bSKonstantin Belousov return; 756ef2a572bSKonstantin Belousov 757ef2a572bSKonstantin Belousov tq = &sp->accel_del_tq; 758ef2a572bSKonstantin Belousov if (atomic_cmpset_int(&tq->adddel_scheduled, 0, 1) == 0) 759ef2a572bSKonstantin Belousov return; 760ef2a572bSKonstantin Belousov tq->adddel_vnet = curthread->td_vnet; 761ef2a572bSKonstantin Belousov TASK_INIT(&tq->adddel_task, 0, ipsec_accel_spddel_act, sp); 762ef2a572bSKonstantin Belousov key_addref(sp); 763f76826b8SKonstantin Belousov taskqueue_enqueue(ipsec_accel_tq, &tq->adddel_task); 764ef2a572bSKonstantin Belousov } 765ef2a572bSKonstantin Belousov 766ef2a572bSKonstantin Belousov static void 767ef2a572bSKonstantin Belousov ipsec_accel_on_ifdown_sp(struct ifnet *ifp) 768ef2a572bSKonstantin Belousov { 769ef2a572bSKonstantin Belousov struct ifp_handle_sp *i, *marker; 770ef2a572bSKonstantin Belousov struct secpolicy *sp; 771ef2a572bSKonstantin Belousov int error; 772ef2a572bSKonstantin Belousov 773ef2a572bSKonstantin Belousov marker = malloc(sizeof(*marker), M_IPSEC_MISC, M_WAITOK | M_ZERO); 774ef2a572bSKonstantin Belousov marker->flags = IFP_HS_MARKER; 775ef2a572bSKonstantin Belousov 776ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 777ef2a572bSKonstantin Belousov CK_LIST_INSERT_HEAD(&ipsec_accel_all_sp_handles, marker, 778ef2a572bSKonstantin Belousov sp_allh_link); 779ef2a572bSKonstantin Belousov for (;;) { 780ef2a572bSKonstantin Belousov i = CK_LIST_NEXT(marker, sp_allh_link); 781ef2a572bSKonstantin Belousov if (i == NULL) 782ef2a572bSKonstantin Belousov break; 783ef2a572bSKonstantin Belousov CK_LIST_REMOVE(marker, sp_allh_link); 784ef2a572bSKonstantin Belousov CK_LIST_INSERT_AFTER(i, marker, sp_allh_link); 785ef2a572bSKonstantin Belousov if (i->ifp != ifp) 786ef2a572bSKonstantin Belousov continue; 787ef2a572bSKonstantin Belousov 788ef2a572bSKonstantin Belousov sp = i->sp; 789ef2a572bSKonstantin Belousov key_addref(sp); 790ef2a572bSKonstantin Belousov CK_LIST_REMOVE(i, sp_link); 791ef2a572bSKonstantin Belousov CK_LIST_REMOVE(i, sp_allh_link); 792ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 793ef2a572bSKonstantin Belousov NET_EPOCH_WAIT(); 794ef2a572bSKonstantin Belousov if ((i->flags & (IFP_HP_HANDLED | IFP_HP_REJECTED)) == 795ef2a572bSKonstantin Belousov IFP_HP_HANDLED) { 7969a7096ffSKonstantin Belousov dprintf("spd deinstall %s %p\n", if_name(ifp), sp); 797ef2a572bSKonstantin Belousov error = ifp->if_ipsec_accel_m->if_spddel(ifp, 798ef2a572bSKonstantin Belousov sp, i->ifdata); 799ef2a572bSKonstantin Belousov } 800ef2a572bSKonstantin Belousov if (error != 0) { 8019a7096ffSKonstantin Belousov dprintf( 802ef2a572bSKonstantin Belousov "ipsec_accel_on_ifdown_sp: %s if_spddel %p res %d\n", 803ef2a572bSKonstantin Belousov if_name(ifp), sp, error); 804ef2a572bSKonstantin Belousov } 805ef2a572bSKonstantin Belousov key_freesp(&sp); 806ef2a572bSKonstantin Belousov if_rele(ifp); 807ef2a572bSKonstantin Belousov free(i, M_IPSEC_MISC); 808ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_sav_tmp); 809ef2a572bSKonstantin Belousov } 810ef2a572bSKonstantin Belousov CK_LIST_REMOVE(marker, sp_allh_link); 811ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_sav_tmp); 812ef2a572bSKonstantin Belousov free(marker, M_IPSEC_MISC); 813ef2a572bSKonstantin Belousov } 814ef2a572bSKonstantin Belousov 81565f264dcSKonstantin Belousov static void 81665f264dcSKonstantin Belousov ipsec_accel_on_ifdown_impl(struct ifnet *ifp) 817ef2a572bSKonstantin Belousov { 818ef2a572bSKonstantin Belousov ipsec_accel_on_ifdown_sp(ifp); 819ef2a572bSKonstantin Belousov ipsec_accel_on_ifdown_sav(ifp); 820ef2a572bSKonstantin Belousov } 821ef2a572bSKonstantin Belousov 822c4a0ee9bSKonstantin Belousov static void 823c4a0ee9bSKonstantin Belousov ipsec_accel_ifdetach_event(void *arg __unused, struct ifnet *ifp) 824c4a0ee9bSKonstantin Belousov { 825c4a0ee9bSKonstantin Belousov if ((ifp->if_flags & IFF_RENAMING) != 0) 826c4a0ee9bSKonstantin Belousov return; 827c4a0ee9bSKonstantin Belousov ipsec_accel_on_ifdown_impl(ifp); 828c4a0ee9bSKonstantin Belousov } 829c4a0ee9bSKonstantin Belousov 830ef2a572bSKonstantin Belousov static bool 831ef2a572bSKonstantin Belousov ipsec_accel_output_pad(struct mbuf *m, struct secasvar *sav, int skip, int mtu) 832ef2a572bSKonstantin Belousov { 833ef2a572bSKonstantin Belousov int alen, blks, hlen, padding, rlen; 834ef2a572bSKonstantin Belousov 835ef2a572bSKonstantin Belousov rlen = m->m_pkthdr.len - skip; 836ef2a572bSKonstantin Belousov hlen = ((sav->flags & SADB_X_EXT_OLD) != 0 ? sizeof(struct esp) : 837ef2a572bSKonstantin Belousov sizeof(struct newesp)) + sav->ivlen; 838ef2a572bSKonstantin Belousov blks = MAX(4, SAV_ISCTR(sav) && VNET(esp_ctr_compatibility) ? 839ef2a572bSKonstantin Belousov sav->tdb_encalgxform->native_blocksize : 840ef2a572bSKonstantin Belousov sav->tdb_encalgxform->blocksize); 841ef2a572bSKonstantin Belousov padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; 842ef2a572bSKonstantin Belousov alen = xform_ah_authsize(sav->tdb_authalgxform); 843ef2a572bSKonstantin Belousov 844ef2a572bSKonstantin Belousov return (skip + hlen + rlen + padding + alen <= mtu); 845ef2a572bSKonstantin Belousov } 846ef2a572bSKonstantin Belousov 847ef2a572bSKonstantin Belousov static bool 848ef2a572bSKonstantin Belousov ipsec_accel_output_tag(struct mbuf *m, u_int drv_spi) 849ef2a572bSKonstantin Belousov { 850ef2a572bSKonstantin Belousov struct ipsec_accel_out_tag *tag; 851ef2a572bSKonstantin Belousov 852ef2a572bSKonstantin Belousov tag = (struct ipsec_accel_out_tag *)m_tag_get( 853ef2a572bSKonstantin Belousov PACKET_TAG_IPSEC_ACCEL_OUT, sizeof(*tag), M_NOWAIT); 854ef2a572bSKonstantin Belousov if (tag == NULL) 855ef2a572bSKonstantin Belousov return (false); 856ef2a572bSKonstantin Belousov tag->drv_spi = drv_spi; 857ef2a572bSKonstantin Belousov m_tag_prepend(m, &tag->tag); 858ef2a572bSKonstantin Belousov return (true); 859ef2a572bSKonstantin Belousov } 860ef2a572bSKonstantin Belousov 861ef2a572bSKonstantin Belousov bool 862ef2a572bSKonstantin Belousov ipsec_accel_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp, 863240b7bfeSKonstantin Belousov struct secpolicy *sp, struct secasvar *sav, int af, int mtu, int *hwassist) 864ef2a572bSKonstantin Belousov { 865ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 866ef2a572bSKonstantin Belousov struct ip *ip; 867e6e2c0a5SKonstantin Belousov struct tcpcb *tp; 868ef2a572bSKonstantin Belousov u_long ip_len, skip; 869e6e2c0a5SKonstantin Belousov bool res; 870ef2a572bSKonstantin Belousov 871240b7bfeSKonstantin Belousov *hwassist = 0; 872e6e2c0a5SKonstantin Belousov res = false; 873ef2a572bSKonstantin Belousov if (ifp == NULL) 874e6e2c0a5SKonstantin Belousov return (res); 875ef2a572bSKonstantin Belousov 876ef2a572bSKonstantin Belousov M_ASSERTPKTHDR(m); 877ef2a572bSKonstantin Belousov NET_EPOCH_ASSERT(); 878ef2a572bSKonstantin Belousov 879e6e2c0a5SKonstantin Belousov if (sav == NULL) { 880e6e2c0a5SKonstantin Belousov res = ipsec_accel_output_tag(m, IPSEC_ACCEL_DRV_SPI_BYPASS); 881e6e2c0a5SKonstantin Belousov goto out; 882e6e2c0a5SKonstantin Belousov } 883ef2a572bSKonstantin Belousov 884ef2a572bSKonstantin Belousov i = ipsec_accel_is_accel_sav_ptr(sav, ifp); 885d02e1a3fSKonstantin Belousov if (i == NULL || (i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) != 886d02e1a3fSKonstantin Belousov IFP_HS_HANDLED) 887e6e2c0a5SKonstantin Belousov goto out; 888ef2a572bSKonstantin Belousov 889ef2a572bSKonstantin Belousov if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0) { 890ef2a572bSKonstantin Belousov ip_len = m->m_pkthdr.len; 891ef2a572bSKonstantin Belousov if (ip_len + i->hdr_ext_size > mtu) 892e6e2c0a5SKonstantin Belousov goto out; 893ef2a572bSKonstantin Belousov switch (af) { 894ef2a572bSKonstantin Belousov case AF_INET: 895ef2a572bSKonstantin Belousov ip = mtod(m, struct ip *); 896ef2a572bSKonstantin Belousov skip = ip->ip_hl << 2; 897ef2a572bSKonstantin Belousov break; 898ef2a572bSKonstantin Belousov case AF_INET6: 899ef2a572bSKonstantin Belousov skip = sizeof(struct ip6_hdr); 900ef2a572bSKonstantin Belousov break; 901ef2a572bSKonstantin Belousov default: 902ef2a572bSKonstantin Belousov __unreachable(); 903ef2a572bSKonstantin Belousov } 904ef2a572bSKonstantin Belousov if (!ipsec_accel_output_pad(m, sav, skip, mtu)) 905e6e2c0a5SKonstantin Belousov goto out; 906ef2a572bSKonstantin Belousov } 907ef2a572bSKonstantin Belousov 908ef2a572bSKonstantin Belousov if (!ipsec_accel_output_tag(m, i->drv_spi)) 909e6e2c0a5SKonstantin Belousov goto out; 910ef2a572bSKonstantin Belousov 911ef2a572bSKonstantin Belousov ipsec_accel_sa_recordxfer(sav, m); 912ef2a572bSKonstantin Belousov key_freesav(&sav); 913ef2a572bSKonstantin Belousov if (sp != NULL) 914ef2a572bSKonstantin Belousov key_freesp(&sp); 915ef2a572bSKonstantin Belousov 916240b7bfeSKonstantin Belousov *hwassist = ifp->if_ipsec_accel_m->if_hwassist(ifp, sav, 917240b7bfeSKonstantin Belousov i->drv_spi, i->ifdata); 918e6e2c0a5SKonstantin Belousov res = true; 919e6e2c0a5SKonstantin Belousov out: 920e6e2c0a5SKonstantin Belousov if (inp != NULL && inp->inp_pcbinfo == &V_tcbinfo) { 921e6e2c0a5SKonstantin Belousov INP_WLOCK_ASSERT(inp); 922e6e2c0a5SKonstantin Belousov tp = (struct tcpcb *)inp; 923e6e2c0a5SKonstantin Belousov if (res && (*hwassist & (CSUM_TSO | CSUM_IP6_TSO)) != 0) { 924e6e2c0a5SKonstantin Belousov tp->t_flags2 |= TF2_IPSEC_TSO; 925e6e2c0a5SKonstantin Belousov } else { 926e6e2c0a5SKonstantin Belousov tp->t_flags2 &= ~TF2_IPSEC_TSO; 927e6e2c0a5SKonstantin Belousov } 928e6e2c0a5SKonstantin Belousov } 929e6e2c0a5SKonstantin Belousov return (res); 930ef2a572bSKonstantin Belousov } 931ef2a572bSKonstantin Belousov 932ef2a572bSKonstantin Belousov struct ipsec_accel_in_tag * 933ef2a572bSKonstantin Belousov ipsec_accel_input_tag_lookup(const struct mbuf *m) 934ef2a572bSKonstantin Belousov { 935ef2a572bSKonstantin Belousov struct ipsec_accel_in_tag *tag; 936ef2a572bSKonstantin Belousov struct m_tag *xtag; 937ef2a572bSKonstantin Belousov 938ef2a572bSKonstantin Belousov xtag = m_tag_find(__DECONST(struct mbuf *, m), 939ef2a572bSKonstantin Belousov PACKET_TAG_IPSEC_ACCEL_IN, NULL); 940ef2a572bSKonstantin Belousov if (xtag == NULL) 941ef2a572bSKonstantin Belousov return (NULL); 942ef2a572bSKonstantin Belousov tag = __containerof(xtag, struct ipsec_accel_in_tag, tag); 943ef2a572bSKonstantin Belousov return (tag); 944ef2a572bSKonstantin Belousov } 945ef2a572bSKonstantin Belousov 946ef2a572bSKonstantin Belousov int 947ef2a572bSKonstantin Belousov ipsec_accel_input(struct mbuf *m, int offset, int proto) 948ef2a572bSKonstantin Belousov { 949ef2a572bSKonstantin Belousov struct secasvar *sav; 950ef2a572bSKonstantin Belousov struct ipsec_accel_in_tag *tag; 951ef2a572bSKonstantin Belousov 952ef2a572bSKonstantin Belousov tag = ipsec_accel_input_tag_lookup(m); 953ef2a572bSKonstantin Belousov if (tag == NULL) 954ef2a572bSKonstantin Belousov return (ENXIO); 955ef2a572bSKonstantin Belousov 956ef2a572bSKonstantin Belousov if (tag->drv_spi < IPSEC_ACCEL_DRV_SPI_MIN || 957ef2a572bSKonstantin Belousov tag->drv_spi > IPSEC_ACCEL_DRV_SPI_MAX) { 9589a7096ffSKonstantin Belousov dprintf("if %s mbuf %p drv_spi %d invalid, packet dropped\n", 959ef2a572bSKonstantin Belousov (m->m_flags & M_PKTHDR) != 0 ? if_name(m->m_pkthdr.rcvif) : 960ef2a572bSKonstantin Belousov "<unknwn>", m, tag->drv_spi); 961ef2a572bSKonstantin Belousov m_freem(m); 962ef2a572bSKonstantin Belousov return (EINPROGRESS); 963ef2a572bSKonstantin Belousov } 964ef2a572bSKonstantin Belousov 965ef2a572bSKonstantin Belousov sav = ipsec_accel_drvspi_to_sa(tag->drv_spi); 966ef2a572bSKonstantin Belousov if (sav != NULL) 967ef2a572bSKonstantin Belousov ipsec_accel_sa_recordxfer(sav, m); 968ef2a572bSKonstantin Belousov return (0); 969ef2a572bSKonstantin Belousov } 970ef2a572bSKonstantin Belousov 971ef2a572bSKonstantin Belousov static void 972ef2a572bSKonstantin Belousov ipsec_accel_sa_recordxfer(struct secasvar *sav, struct mbuf *m) 973ef2a572bSKonstantin Belousov { 974ef2a572bSKonstantin Belousov counter_u64_add(sav->accel_lft_sw, 1); 975ef2a572bSKonstantin Belousov counter_u64_add(sav->accel_lft_sw + 1, m->m_pkthdr.len); 976ef2a572bSKonstantin Belousov if (sav->accel_firstused == 0) 977ef2a572bSKonstantin Belousov sav->accel_firstused = time_second; 978ef2a572bSKonstantin Belousov } 979ef2a572bSKonstantin Belousov 980ef2a572bSKonstantin Belousov static void 981ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_update(struct seclifetime *lft_c, 982ef2a572bSKonstantin Belousov const struct seclifetime *lft_l) 983ef2a572bSKonstantin Belousov { 984ef2a572bSKonstantin Belousov lft_c->allocations += lft_l->allocations; 985ef2a572bSKonstantin Belousov lft_c->bytes += lft_l->bytes; 986ef2a572bSKonstantin Belousov lft_c->usetime = min(lft_c->usetime, lft_l->usetime); 987ef2a572bSKonstantin Belousov } 988ef2a572bSKonstantin Belousov 98965f264dcSKonstantin Belousov static void 99065f264dcSKonstantin Belousov ipsec_accel_drv_sa_lifetime_update_impl(struct secasvar *sav, if_t ifp, 991ef2a572bSKonstantin Belousov u_int drv_spi, uint64_t octets, uint64_t allocs) 992ef2a572bSKonstantin Belousov { 993ef2a572bSKonstantin Belousov struct epoch_tracker et; 994ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 995ef2a572bSKonstantin Belousov uint64_t odiff, adiff; 996ef2a572bSKonstantin Belousov 997ef2a572bSKonstantin Belousov NET_EPOCH_ENTER(et); 998ef2a572bSKonstantin Belousov mtx_lock(&ipsec_accel_cnt_lock); 999ef2a572bSKonstantin Belousov 1000ef2a572bSKonstantin Belousov if (allocs != 0) { 1001ef2a572bSKonstantin Belousov if (sav->firstused == 0) 1002ef2a572bSKonstantin Belousov sav->firstused = time_second; 1003ef2a572bSKonstantin Belousov if (sav->accel_firstused == 0) 1004ef2a572bSKonstantin Belousov sav->accel_firstused = time_second; 1005ef2a572bSKonstantin Belousov } 1006ef2a572bSKonstantin Belousov 1007ef2a572bSKonstantin Belousov CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) { 1008ef2a572bSKonstantin Belousov if (i->ifp == ifp && i->drv_spi == drv_spi) 1009ef2a572bSKonstantin Belousov break; 1010ef2a572bSKonstantin Belousov } 1011ef2a572bSKonstantin Belousov if (i == NULL) 1012ef2a572bSKonstantin Belousov goto out; 1013ef2a572bSKonstantin Belousov 1014ef2a572bSKonstantin Belousov odiff = octets - i->cnt_octets; 1015ef2a572bSKonstantin Belousov adiff = allocs - i->cnt_allocs; 1016ef2a572bSKonstantin Belousov 1017ef2a572bSKonstantin Belousov if (sav->lft_c != NULL) { 1018ef2a572bSKonstantin Belousov counter_u64_add(sav->lft_c_bytes, odiff); 1019ef2a572bSKonstantin Belousov counter_u64_add(sav->lft_c_allocations, adiff); 1020ef2a572bSKonstantin Belousov } 1021ef2a572bSKonstantin Belousov 1022ef2a572bSKonstantin Belousov i->cnt_octets = octets; 1023ef2a572bSKonstantin Belousov i->cnt_allocs = allocs; 1024ef2a572bSKonstantin Belousov sav->accel_hw_octets += odiff; 1025ef2a572bSKonstantin Belousov sav->accel_hw_allocs += adiff; 1026ef2a572bSKonstantin Belousov 1027ef2a572bSKonstantin Belousov out: 1028ef2a572bSKonstantin Belousov mtx_unlock(&ipsec_accel_cnt_lock); 1029ef2a572bSKonstantin Belousov NET_EPOCH_EXIT(et); 1030ef2a572bSKonstantin Belousov } 1031ef2a572bSKonstantin Belousov 103266f0e201SKonstantin Belousov static int 103366f0e201SKonstantin Belousov ipsec_accel_drv_sa_lifetime_fetch_impl(struct secasvar *sav, 103466f0e201SKonstantin Belousov if_t ifp, u_int drv_spi, uint64_t *octets, uint64_t *allocs) 103566f0e201SKonstantin Belousov { 103666f0e201SKonstantin Belousov struct ifp_handle_sav *i; 103766f0e201SKonstantin Belousov int error; 103866f0e201SKonstantin Belousov 103966f0e201SKonstantin Belousov NET_EPOCH_ASSERT(); 104066f0e201SKonstantin Belousov error = 0; 104166f0e201SKonstantin Belousov 104266f0e201SKonstantin Belousov mtx_lock(&ipsec_accel_cnt_lock); 104366f0e201SKonstantin Belousov CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) { 104466f0e201SKonstantin Belousov if (i->ifp == ifp && i->drv_spi == drv_spi) { 104566f0e201SKonstantin Belousov *octets = i->cnt_octets; 104666f0e201SKonstantin Belousov *allocs = i->cnt_allocs; 104766f0e201SKonstantin Belousov break; 104866f0e201SKonstantin Belousov } 104966f0e201SKonstantin Belousov } 105066f0e201SKonstantin Belousov if (i == NULL) 105166f0e201SKonstantin Belousov error = ENOENT; 105266f0e201SKonstantin Belousov mtx_unlock(&ipsec_accel_cnt_lock); 105366f0e201SKonstantin Belousov return (error); 105466f0e201SKonstantin Belousov } 105566f0e201SKonstantin Belousov 1056ef2a572bSKonstantin Belousov static void 1057ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_hw(struct secasvar *sav, if_t ifp, 1058ef2a572bSKonstantin Belousov struct seclifetime *lft) 1059ef2a572bSKonstantin Belousov { 1060ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 1061ef2a572bSKonstantin Belousov if_sa_cnt_fn_t p; 1062ef2a572bSKonstantin Belousov 1063ef2a572bSKonstantin Belousov IFNET_RLOCK_ASSERT(); 1064ef2a572bSKonstantin Belousov 1065ef2a572bSKonstantin Belousov i = ipsec_accel_is_accel_sav_ptr(sav, ifp); 1066ef2a572bSKonstantin Belousov if (i != NULL && (i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) == 1067ef2a572bSKonstantin Belousov IFP_HS_HANDLED) { 1068ef2a572bSKonstantin Belousov p = ifp->if_ipsec_accel_m->if_sa_cnt; 1069ef2a572bSKonstantin Belousov if (p != NULL) 1070ef2a572bSKonstantin Belousov p(ifp, sav, i->drv_spi, i->ifdata, lft); 1071ef2a572bSKonstantin Belousov } 1072ef2a572bSKonstantin Belousov } 1073ef2a572bSKonstantin Belousov 1074ef2a572bSKonstantin Belousov static int 1075ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_op_impl(struct secasvar *sav, 1076ef2a572bSKonstantin Belousov struct seclifetime *lft_c, if_t ifp, enum IF_SA_CNT_WHICH op, 1077ef2a572bSKonstantin Belousov struct rm_priotracker *sahtree_trackerp) 1078ef2a572bSKonstantin Belousov { 1079ef2a572bSKonstantin Belousov struct seclifetime lft_l, lft_s; 1080ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 1081ef2a572bSKonstantin Belousov if_t ifp1; 1082ef2a572bSKonstantin Belousov if_sa_cnt_fn_t p; 1083ef2a572bSKonstantin Belousov int error; 1084ef2a572bSKonstantin Belousov 1085ef2a572bSKonstantin Belousov error = 0; 1086ef2a572bSKonstantin Belousov memset(&lft_l, 0, sizeof(lft_l)); 1087ef2a572bSKonstantin Belousov memset(&lft_s, 0, sizeof(lft_s)); 1088ef2a572bSKonstantin Belousov 1089ef2a572bSKonstantin Belousov switch (op & ~IF_SA_CNT_UPD) { 1090ef2a572bSKonstantin Belousov case IF_SA_CNT_IFP_HW_VAL: 1091ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_hw(sav, ifp, &lft_l); 1092ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_update(&lft_l, &lft_s); 1093ef2a572bSKonstantin Belousov break; 1094ef2a572bSKonstantin Belousov 1095ef2a572bSKonstantin Belousov case IF_SA_CNT_TOTAL_SW_VAL: 1096ef2a572bSKonstantin Belousov lft_l.allocations = (uint32_t)counter_u64_fetch( 1097ef2a572bSKonstantin Belousov sav->accel_lft_sw); 1098ef2a572bSKonstantin Belousov lft_l.bytes = counter_u64_fetch(sav->accel_lft_sw + 1); 1099ef2a572bSKonstantin Belousov lft_l.usetime = sav->accel_firstused; 1100ef2a572bSKonstantin Belousov break; 1101ef2a572bSKonstantin Belousov 1102ef2a572bSKonstantin Belousov case IF_SA_CNT_TOTAL_HW_VAL: 1103ef2a572bSKonstantin Belousov IFNET_RLOCK_ASSERT(); 1104ef2a572bSKonstantin Belousov CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) { 1105ef2a572bSKonstantin Belousov if ((i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) != 1106ef2a572bSKonstantin Belousov IFP_HS_HANDLED) 1107ef2a572bSKonstantin Belousov continue; 1108ef2a572bSKonstantin Belousov ifp1 = i->ifp; 1109ef2a572bSKonstantin Belousov p = ifp1->if_ipsec_accel_m->if_sa_cnt; 1110ef2a572bSKonstantin Belousov if (p == NULL) 1111ef2a572bSKonstantin Belousov continue; 1112ef2a572bSKonstantin Belousov memset(&lft_s, 0, sizeof(lft_s)); 1113ef2a572bSKonstantin Belousov if (sahtree_trackerp != NULL) 1114ef2a572bSKonstantin Belousov ipsec_sahtree_runlock(sahtree_trackerp); 1115ef2a572bSKonstantin Belousov error = p(ifp1, sav, i->drv_spi, i->ifdata, &lft_s); 1116ef2a572bSKonstantin Belousov if (sahtree_trackerp != NULL) 1117ef2a572bSKonstantin Belousov ipsec_sahtree_rlock(sahtree_trackerp); 1118ef2a572bSKonstantin Belousov if (error == 0) 1119ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_update(&lft_l, &lft_s); 1120ef2a572bSKonstantin Belousov } 1121ef2a572bSKonstantin Belousov break; 1122ef2a572bSKonstantin Belousov } 1123ef2a572bSKonstantin Belousov 1124ef2a572bSKonstantin Belousov if (error == 0) { 1125ef2a572bSKonstantin Belousov if ((op & IF_SA_CNT_UPD) == 0) 1126ef2a572bSKonstantin Belousov memset(lft_c, 0, sizeof(*lft_c)); 1127ef2a572bSKonstantin Belousov ipsec_accel_sa_lifetime_update(lft_c, &lft_l); 1128ef2a572bSKonstantin Belousov } 1129ef2a572bSKonstantin Belousov 1130ef2a572bSKonstantin Belousov return (error); 1131ef2a572bSKonstantin Belousov } 1132ef2a572bSKonstantin Belousov 1133ef2a572bSKonstantin Belousov static void 1134ef2a572bSKonstantin Belousov ipsec_accel_sync_imp(void) 1135ef2a572bSKonstantin Belousov { 1136f76826b8SKonstantin Belousov taskqueue_drain_all(ipsec_accel_tq); 1137ef2a572bSKonstantin Belousov } 1138ef2a572bSKonstantin Belousov 1139ef2a572bSKonstantin Belousov static struct mbuf * 1140ef2a572bSKonstantin Belousov ipsec_accel_key_setaccelif_impl(struct secasvar *sav) 1141ef2a572bSKonstantin Belousov { 1142ef2a572bSKonstantin Belousov struct mbuf *m, *m1; 1143ef2a572bSKonstantin Belousov struct ifp_handle_sav *i; 1144ef2a572bSKonstantin Belousov struct epoch_tracker et; 1145ef2a572bSKonstantin Belousov 1146ef2a572bSKonstantin Belousov if (sav->accel_ifname != NULL) 1147ef2a572bSKonstantin Belousov return (key_setaccelif(sav->accel_ifname)); 1148ef2a572bSKonstantin Belousov 1149ef2a572bSKonstantin Belousov m = m1 = NULL; 1150ef2a572bSKonstantin Belousov 1151ef2a572bSKonstantin Belousov NET_EPOCH_ENTER(et); 1152ef2a572bSKonstantin Belousov CK_LIST_FOREACH(i, &sav->accel_ifps, sav_link) { 1153ef2a572bSKonstantin Belousov if ((i->flags & (IFP_HS_HANDLED | IFP_HS_REJECTED)) == 1154ef2a572bSKonstantin Belousov IFP_HS_HANDLED) { 1155ef2a572bSKonstantin Belousov m1 = key_setaccelif(if_name(i->ifp)); 1156ef2a572bSKonstantin Belousov if (m == NULL) 1157ef2a572bSKonstantin Belousov m = m1; 1158ef2a572bSKonstantin Belousov else if (m1 != NULL) 1159ef2a572bSKonstantin Belousov m_cat(m, m1); 1160ef2a572bSKonstantin Belousov } 1161ef2a572bSKonstantin Belousov } 1162ef2a572bSKonstantin Belousov NET_EPOCH_EXIT(et); 1163ef2a572bSKonstantin Belousov return (m); 1164ef2a572bSKonstantin Belousov } 1165ef2a572bSKonstantin Belousov 1166ef2a572bSKonstantin Belousov #endif /* IPSEC_OFFLOAD */ 1167