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