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 *
drvspi_sa_trie_alloc(struct pctrie * ptree)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
drvspi_sa_trie_free(struct pctrie * ptree,void * node)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
ipsec_accel_init(void * arg)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
ipsec_accel_fini(void * arg)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
dprintf(const char * fmt,...)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
ipsec_accel_alloc_forget_tq(struct secasvar * sav)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
ipsec_accel_sa_install_match(if_t ifp,void * arg)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
ipsec_accel_sa_newkey_cb(if_t ifp,void * arg)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
ipsec_accel_sa_newkey_act(void * context,int pending)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
ipsec_accel_sa_newkey_impl(struct secasvar * sav)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
ipsec_accel_handle_sav(struct secasvar * sav,struct ifnet * ifp,u_int drv_spi,void * priv,uint32_t flags,struct ifp_handle_sav ** ires)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
ipsec_accel_forget_handle_sav(struct ifp_handle_sav * i,bool freesav)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
ipsec_accel_forget_sav_clear(struct secasvar * sav)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
ipsec_accel_forget_sav_act(void * arg,int pending)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
ipsec_accel_forget_sav_impl(struct secasvar * sav)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
ipsec_accel_on_ifdown_sav(struct ifnet * ifp)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 *
ipsec_accel_is_accel_sav_ptr_raw(struct secasvar * sav,struct ifnet * ifp)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 *
ipsec_accel_is_accel_sav_ptr(struct secasvar * sav,struct ifnet * ifp)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
ipsec_accel_is_accel_sav_impl(struct secasvar * sav)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 *
ipsec_accel_drvspi_to_sa(u_int drv_spi)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 *
ipsec_accel_find_accel_sp(struct secpolicy * sp,if_t ifp)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
ipsec_accel_is_accel_sp(struct secpolicy * sp,if_t ifp)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
ipsec_accel_remember_sp(struct secpolicy * sp,if_t ifp,struct ifp_handle_sp ** ip)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
ipsec_accel_spdadd_match(if_t ifp,void * arg)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
ipsec_accel_spdadd_cb(if_t ifp,void * arg)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
ipsec_accel_spdadd_act(void * arg,int pending)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
ipsec_accel_spdadd_impl(struct secpolicy * sp,struct inpcb * inp)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
ipsec_accel_spddel_act(void * arg,int pending)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
ipsec_accel_spddel_impl(struct secpolicy * sp)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
ipsec_accel_on_ifdown_sp(struct ifnet * ifp)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
ipsec_accel_on_ifdown_impl(struct ifnet * ifp)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
ipsec_accel_ifdetach_event(void * arg __unused,struct ifnet * ifp)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
ipsec_accel_output_pad(struct mbuf * m,struct secasvar * sav,int skip,int mtu)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
ipsec_accel_output_tag(struct mbuf * m,u_int drv_spi)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
ipsec_accel_output(struct ifnet * ifp,struct mbuf * m,struct inpcb * inp,struct secpolicy * sp,struct secasvar * sav,int af,int mtu,int * hwassist)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 *
ipsec_accel_input_tag_lookup(const struct mbuf * m)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
ipsec_accel_input(struct mbuf * m,int offset,int proto)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
ipsec_accel_sa_recordxfer(struct secasvar * sav,struct mbuf * m)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
ipsec_accel_sa_lifetime_update(struct seclifetime * lft_c,const struct seclifetime * lft_l)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
ipsec_accel_drv_sa_lifetime_update_impl(struct secasvar * sav,if_t ifp,u_int drv_spi,uint64_t octets,uint64_t allocs)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
ipsec_accel_drv_sa_lifetime_fetch_impl(struct secasvar * sav,if_t ifp,u_int drv_spi,uint64_t * octets,uint64_t * allocs)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
ipsec_accel_sa_lifetime_hw(struct secasvar * sav,if_t ifp,struct seclifetime * lft)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
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)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
ipsec_accel_sync_imp(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 *
ipsec_accel_key_setaccelif_impl(struct secasvar * sav)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