xref: /freebsd/sys/dev/mlx5/mlx5_accel/mlx5_ipsec.c (revision 1fbce7deef51bb7641335f13ddf2543e56f0dafd)
1e23731dbSKonstantin Belousov /*-
2e23731dbSKonstantin Belousov  * Copyright (c) 2023 NVIDIA corporation & affiliates.
3e23731dbSKonstantin Belousov  *
4e23731dbSKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
5e23731dbSKonstantin Belousov  * modification, are permitted provided that the following conditions
6e23731dbSKonstantin Belousov  * are met:
7e23731dbSKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
8e23731dbSKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
9e23731dbSKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
10e23731dbSKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
11e23731dbSKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
12e23731dbSKonstantin Belousov  *
13e23731dbSKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14e23731dbSKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15e23731dbSKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16e23731dbSKonstantin Belousov  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17e23731dbSKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18e23731dbSKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19e23731dbSKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20e23731dbSKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21e23731dbSKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22e23731dbSKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23e23731dbSKonstantin Belousov  * SUCH DAMAGE.
24e23731dbSKonstantin Belousov  *
25e23731dbSKonstantin Belousov  */
26e23731dbSKonstantin Belousov 
27e23731dbSKonstantin Belousov #include "opt_ipsec.h"
28e23731dbSKonstantin Belousov 
29e23731dbSKonstantin Belousov #include <sys/types.h>
30e23731dbSKonstantin Belousov #include <netinet/in.h>
31e23731dbSKonstantin Belousov #include <sys/socket.h>
32e23731dbSKonstantin Belousov #include <sys/param.h>
33e23731dbSKonstantin Belousov #include <sys/systm.h>
34e23731dbSKonstantin Belousov #include <net/if.h>
35e23731dbSKonstantin Belousov #include <net/if_var.h>
36e23731dbSKonstantin Belousov #include <net/pfkeyv2.h>
372851aafeSKonstantin Belousov #include <netipsec/key.h>
38e23731dbSKonstantin Belousov #include <netipsec/key_var.h>
39e23731dbSKonstantin Belousov #include <netipsec/keydb.h>
40e23731dbSKonstantin Belousov #include <netipsec/ipsec.h>
41e23731dbSKonstantin Belousov #include <netipsec/xform.h>
42e23731dbSKonstantin Belousov #include <netipsec/ipsec_offload.h>
43e23731dbSKonstantin Belousov #include <dev/mlx5/fs.h>
44e23731dbSKonstantin Belousov #include <dev/mlx5/mlx5_en/en.h>
45e23731dbSKonstantin Belousov #include <dev/mlx5/mlx5_accel/ipsec.h>
46e23731dbSKonstantin Belousov 
47e23731dbSKonstantin Belousov #define MLX5_IPSEC_RESCHED msecs_to_jiffies(1000)
48e23731dbSKonstantin Belousov 
49828da10bSKonstantin Belousov static void mlx5e_if_sa_deinstall_onekey(struct ifnet *ifp, u_int dev_spi,
50828da10bSKonstantin Belousov     void *priv);
51e23731dbSKonstantin Belousov static int mlx5e_if_sa_deinstall(struct ifnet *ifp, u_int dev_spi, void *priv);
52e23731dbSKonstantin Belousov 
to_ipsec_sa_entry(void * x)53e23731dbSKonstantin Belousov static struct mlx5e_ipsec_sa_entry *to_ipsec_sa_entry(void *x)
54e23731dbSKonstantin Belousov {
55e23731dbSKonstantin Belousov 	return (struct mlx5e_ipsec_sa_entry *)x;
56e23731dbSKonstantin Belousov }
57e23731dbSKonstantin Belousov 
to_ipsec_pol_entry(void * x)58e23731dbSKonstantin Belousov static struct mlx5e_ipsec_pol_entry *to_ipsec_pol_entry(void *x)
59e23731dbSKonstantin Belousov {
60e23731dbSKonstantin Belousov 	return (struct mlx5e_ipsec_pol_entry *)x;
61e23731dbSKonstantin Belousov }
62e23731dbSKonstantin Belousov 
63e23731dbSKonstantin Belousov static void
mlx5e_ipsec_handle_counters_onedir(struct mlx5e_ipsec_sa_entry * sa_entry,u64 * packets,u64 * bytes)64e23731dbSKonstantin Belousov mlx5e_ipsec_handle_counters_onedir(struct mlx5e_ipsec_sa_entry *sa_entry,
65e23731dbSKonstantin Belousov     u64 *packets, u64 *bytes)
66e23731dbSKonstantin Belousov {
67e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
68e23731dbSKonstantin Belousov 	struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
69e23731dbSKonstantin Belousov 
70e23731dbSKonstantin Belousov 	mlx5_fc_query(mdev, ipsec_rule->fc, packets, bytes);
71e23731dbSKonstantin Belousov }
72e23731dbSKonstantin Belousov 
73e23731dbSKonstantin Belousov static struct mlx5e_ipsec_sa_entry *
mlx5e_ipsec_other_sa_entry(struct mlx5e_ipsec_priv_bothdir * pb,struct mlx5e_ipsec_sa_entry * sa_entry)74e23731dbSKonstantin Belousov mlx5e_ipsec_other_sa_entry(struct mlx5e_ipsec_priv_bothdir *pb,
75e23731dbSKonstantin Belousov     struct mlx5e_ipsec_sa_entry *sa_entry)
76e23731dbSKonstantin Belousov {
77e23731dbSKonstantin Belousov 	return (pb->priv_in == sa_entry ? pb->priv_out : pb->priv_in);
78e23731dbSKonstantin Belousov }
79e23731dbSKonstantin Belousov 
80e23731dbSKonstantin Belousov static void
mlx5e_ipsec_handle_counters(struct work_struct * _work)81e23731dbSKonstantin Belousov mlx5e_ipsec_handle_counters(struct work_struct *_work)
82e23731dbSKonstantin Belousov {
83e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_dwork *dwork =
84e23731dbSKonstantin Belousov 	    container_of(_work, struct mlx5e_ipsec_dwork, dwork.work);
85e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_sa_entry *sa_entry = dwork->sa_entry;
86e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_sa_entry *other_sa_entry;
87e23731dbSKonstantin Belousov 	u64 bytes, bytes1, packets1, packets;
88e23731dbSKonstantin Belousov 
89e23731dbSKonstantin Belousov 	if (sa_entry->attrs.drop)
90e23731dbSKonstantin Belousov 		return;
91e23731dbSKonstantin Belousov 	other_sa_entry = mlx5e_ipsec_other_sa_entry(dwork->pb, sa_entry);
92e23731dbSKonstantin Belousov 	if (other_sa_entry == NULL || other_sa_entry->attrs.drop)
93e23731dbSKonstantin Belousov 		return;
94e23731dbSKonstantin Belousov 
95e23731dbSKonstantin Belousov 	mlx5e_ipsec_handle_counters_onedir(sa_entry, &packets, &bytes);
96e23731dbSKonstantin Belousov 	mlx5e_ipsec_handle_counters_onedir(other_sa_entry, &packets1, &bytes1);
97e23731dbSKonstantin Belousov 	packets += packets1;
98e23731dbSKonstantin Belousov 	bytes += bytes1;
99e23731dbSKonstantin Belousov 
100e23731dbSKonstantin Belousov #ifdef IPSEC_OFFLOAD
101205263acSAriel Ehrenberg 	ipsec_accel_drv_sa_lifetime_update(
102205263acSAriel Ehrenberg 	    sa_entry->savp, sa_entry->ifpo, sa_entry->kspi, bytes, packets);
103e23731dbSKonstantin Belousov #endif
104e23731dbSKonstantin Belousov 
105e23731dbSKonstantin Belousov 	queue_delayed_work(sa_entry->ipsec->wq, &dwork->dwork,
106e23731dbSKonstantin Belousov 	    MLX5_IPSEC_RESCHED);
107e23731dbSKonstantin Belousov }
108e23731dbSKonstantin Belousov 
109e23731dbSKonstantin Belousov static int
mlx5e_ipsec_create_dwork(struct mlx5e_ipsec_sa_entry * sa_entry,struct mlx5e_ipsec_priv_bothdir * pb)110e23731dbSKonstantin Belousov mlx5e_ipsec_create_dwork(struct mlx5e_ipsec_sa_entry *sa_entry,
111e23731dbSKonstantin Belousov     struct mlx5e_ipsec_priv_bothdir *pb)
112e23731dbSKonstantin Belousov {
113e23731dbSKonstantin Belousov         struct mlx5e_ipsec_dwork *dwork;
114e23731dbSKonstantin Belousov 
115e23731dbSKonstantin Belousov         dwork = kzalloc(sizeof(*dwork), GFP_KERNEL);
116e23731dbSKonstantin Belousov         if (!dwork)
117e23731dbSKonstantin Belousov 		return (ENOMEM);
118e23731dbSKonstantin Belousov 
119e23731dbSKonstantin Belousov         dwork->sa_entry = sa_entry;
120e23731dbSKonstantin Belousov 	dwork->pb = pb;
121e23731dbSKonstantin Belousov         INIT_DELAYED_WORK(&dwork->dwork, mlx5e_ipsec_handle_counters);
122e23731dbSKonstantin Belousov         sa_entry->dwork = dwork;
123e23731dbSKonstantin Belousov         return 0;
124e23731dbSKonstantin Belousov }
125e23731dbSKonstantin Belousov 
mlx5_xform_ah_authsize(const struct auth_hash * esph)126e23731dbSKonstantin Belousov static int mlx5_xform_ah_authsize(const struct auth_hash *esph)
127e23731dbSKonstantin Belousov {
128e23731dbSKonstantin Belousov         int alen;
129e23731dbSKonstantin Belousov 
130e23731dbSKonstantin Belousov         if (esph == NULL)
131e23731dbSKonstantin Belousov                 return 0;
132e23731dbSKonstantin Belousov 
133e23731dbSKonstantin Belousov         switch (esph->type) {
134e23731dbSKonstantin Belousov         case CRYPTO_SHA2_256_HMAC:
135e23731dbSKonstantin Belousov         case CRYPTO_SHA2_384_HMAC:
136e23731dbSKonstantin Belousov         case CRYPTO_SHA2_512_HMAC:
137e23731dbSKonstantin Belousov                 alen = esph->hashsize / 2;      /* RFC4868 2.3 */
138e23731dbSKonstantin Belousov                 break;
139e23731dbSKonstantin Belousov 
140e23731dbSKonstantin Belousov         case CRYPTO_POLY1305:
141e23731dbSKonstantin Belousov         case CRYPTO_AES_NIST_GMAC:
142e23731dbSKonstantin Belousov                 alen = esph->hashsize;
143e23731dbSKonstantin Belousov                 break;
144e23731dbSKonstantin Belousov 
145e23731dbSKonstantin Belousov         default:
146e23731dbSKonstantin Belousov                 alen = AH_HMAC_HASHLEN;
147e23731dbSKonstantin Belousov                 break;
148e23731dbSKonstantin Belousov         }
149e23731dbSKonstantin Belousov 
150e23731dbSKonstantin Belousov         return alen;
151e23731dbSKonstantin Belousov }
152e23731dbSKonstantin Belousov 
mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry * sa_entry,struct mlx5_accel_esp_xfrm_attrs * attrs,u8 dir)153e23731dbSKonstantin Belousov void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
154e23731dbSKonstantin Belousov 					struct mlx5_accel_esp_xfrm_attrs *attrs,
155e23731dbSKonstantin Belousov 					u8 dir)
156e23731dbSKonstantin Belousov {
157e23731dbSKonstantin Belousov 	struct secasvar *savp = sa_entry->savp;
158e23731dbSKonstantin Belousov 	const struct auth_hash *esph = savp->tdb_authalgxform;
159e23731dbSKonstantin Belousov 	struct aes_gcm_keymat *aes_gcm = &attrs->aes_gcm;
160e23731dbSKonstantin Belousov 	struct secasindex *saidx = &savp->sah->saidx;
161e23731dbSKonstantin Belousov 	struct seckey *key_encap = savp->key_enc;
162e23731dbSKonstantin Belousov 	int key_len;
163e23731dbSKonstantin Belousov 
164e23731dbSKonstantin Belousov 	memset(attrs, 0, sizeof(*attrs));
165e23731dbSKonstantin Belousov 
166e23731dbSKonstantin Belousov 	/* subtract off the salt, RFC4106, 8.1 and RFC3686, 5.1 */
167e23731dbSKonstantin Belousov 	key_len = _KEYLEN(key_encap) - SAV_ISCTRORGCM(savp) * 4 - SAV_ISCHACHA(savp) * 4;
168e23731dbSKonstantin Belousov 
169e23731dbSKonstantin Belousov 	memcpy(aes_gcm->aes_key, key_encap->key_data, key_len);
170e23731dbSKonstantin Belousov 	aes_gcm->key_len = key_len;
171e23731dbSKonstantin Belousov 
172e23731dbSKonstantin Belousov 	/* salt and seq_iv */
173e23731dbSKonstantin Belousov 	aes_gcm->seq_iv = 0;
174e23731dbSKonstantin Belousov 	memcpy(&aes_gcm->salt, key_encap->key_data + key_len,
175e23731dbSKonstantin Belousov 	       sizeof(aes_gcm->salt));
176e23731dbSKonstantin Belousov 
177e23731dbSKonstantin Belousov 	switch (savp->alg_enc) {
178e23731dbSKonstantin Belousov 	case SADB_X_EALG_AESGCM8:
179e23731dbSKonstantin Belousov 		attrs->authsize = 8 / 4; /* in dwords */
180e23731dbSKonstantin Belousov 		break;
181e23731dbSKonstantin Belousov 	case SADB_X_EALG_AESGCM12:
182e23731dbSKonstantin Belousov 		attrs->authsize = 12 / 4; /* in dwords */
183e23731dbSKonstantin Belousov 		break;
184e23731dbSKonstantin Belousov 	case SADB_X_EALG_AESGCM16:
185e23731dbSKonstantin Belousov 		attrs->authsize = 16 / 4; /* in dwords */
186e23731dbSKonstantin Belousov 		break;
187e23731dbSKonstantin Belousov 	default: break;
188e23731dbSKonstantin Belousov 	}
189e23731dbSKonstantin Belousov 
190e23731dbSKonstantin Belousov 	/* iv len */
191e23731dbSKonstantin Belousov 	aes_gcm->icv_len = mlx5_xform_ah_authsize(esph); //TBD: check if value make sense
192e23731dbSKonstantin Belousov 
193e23731dbSKonstantin Belousov 	attrs->dir = dir;
194e23731dbSKonstantin Belousov 	/* spi - host order */
195e23731dbSKonstantin Belousov 	attrs->spi = ntohl(savp->spi);
196e23731dbSKonstantin Belousov 	attrs->family = saidx->dst.sa.sa_family;
197e23731dbSKonstantin Belousov 	attrs->reqid = saidx->reqid;
198e23731dbSKonstantin Belousov 
199e23731dbSKonstantin Belousov 	if (saidx->src.sa.sa_family == AF_INET) {
200e23731dbSKonstantin Belousov 		attrs->saddr.a4 = saidx->src.sin.sin_addr.s_addr;
201e23731dbSKonstantin Belousov 		attrs->daddr.a4 = saidx->dst.sin.sin_addr.s_addr;
202e23731dbSKonstantin Belousov 	} else {
203e23731dbSKonstantin Belousov 		memcpy(&attrs->saddr.a6, &saidx->src.sin6.sin6_addr, 16);
204e23731dbSKonstantin Belousov 		memcpy(&attrs->daddr.a6, &saidx->dst.sin6.sin6_addr, 16);
205e23731dbSKonstantin Belousov 	}
206e23731dbSKonstantin Belousov 
207e23731dbSKonstantin Belousov 	if (savp->natt) {
208e23731dbSKonstantin Belousov 		attrs->encap = true;
209e23731dbSKonstantin Belousov 		attrs->sport = savp->natt->sport;
210e23731dbSKonstantin Belousov 		attrs->dport = savp->natt->dport;
211e23731dbSKonstantin Belousov 	}
212e23731dbSKonstantin Belousov 
213e23731dbSKonstantin Belousov 	if (savp->flags & SADB_X_SAFLAGS_ESN) {
214e23731dbSKonstantin Belousov 		/* We support replay window with ESN only */
215e23731dbSKonstantin Belousov 		attrs->replay_esn.trigger = true;
216e23731dbSKonstantin Belousov 		if (sa_entry->esn_state.esn_msb)
217e23731dbSKonstantin Belousov 			attrs->replay_esn.esn = sa_entry->esn_state.esn;
218e23731dbSKonstantin Belousov 		else
219e23731dbSKonstantin Belousov 			/* According to RFC4303, section "3.3.3. Sequence Number Generation",
220e23731dbSKonstantin Belousov 			 * the first packet sent using a given SA will contain a sequence
221e23731dbSKonstantin Belousov 			 * number of 1.
222e23731dbSKonstantin Belousov 			 */
223e23731dbSKonstantin Belousov 			attrs->replay_esn.esn = max_t(u32, sa_entry->esn_state.esn, 1);
224e23731dbSKonstantin Belousov 		attrs->replay_esn.esn_msb = sa_entry->esn_state.esn_msb;
225e23731dbSKonstantin Belousov 		attrs->replay_esn.overlap = sa_entry->esn_state.overlap;
226e23731dbSKonstantin Belousov 
227e23731dbSKonstantin Belousov 	        if (savp->replay) {
228e23731dbSKonstantin Belousov 			switch (savp->replay->wsize) {
229e23731dbSKonstantin Belousov 			case 4:
230e23731dbSKonstantin Belousov 	                     attrs->replay_esn.replay_window = MLX5_IPSEC_ASO_REPLAY_WIN_32BIT;
231e23731dbSKonstantin Belousov 			     break;
232e23731dbSKonstantin Belousov 			case 8:
233e23731dbSKonstantin Belousov 	                     attrs->replay_esn.replay_window = MLX5_IPSEC_ASO_REPLAY_WIN_64BIT;
234e23731dbSKonstantin Belousov 			     break;
235e23731dbSKonstantin Belousov 			case 16:
236e23731dbSKonstantin Belousov 	                     attrs->replay_esn.replay_window = MLX5_IPSEC_ASO_REPLAY_WIN_128BIT;
237e23731dbSKonstantin Belousov 			     break;
238e23731dbSKonstantin Belousov 			case 32:
239e23731dbSKonstantin Belousov 	                     attrs->replay_esn.replay_window = MLX5_IPSEC_ASO_REPLAY_WIN_256BIT;
240e23731dbSKonstantin Belousov 			     break;
241e23731dbSKonstantin Belousov 			default:
242e23731dbSKonstantin Belousov 			     /* Do nothing */
243e23731dbSKonstantin Belousov 			     break;
244e23731dbSKonstantin Belousov 	                }
245e23731dbSKonstantin Belousov 		}
246e23731dbSKonstantin Belousov         }
247e23731dbSKonstantin Belousov }
248e23731dbSKonstantin Belousov 
mlx5e_xfrm_validate_state(struct mlx5_core_dev * mdev,struct secasvar * savp)249e23731dbSKonstantin Belousov static int mlx5e_xfrm_validate_state(struct mlx5_core_dev *mdev,
250e23731dbSKonstantin Belousov 				     struct secasvar *savp)
251e23731dbSKonstantin Belousov {
252e23731dbSKonstantin Belousov 	struct secasindex *saidx = &savp->sah->saidx;
253e23731dbSKonstantin Belousov 	struct seckey *key_encp = savp->key_enc;
254e23731dbSKonstantin Belousov 	int keylen;
255e23731dbSKonstantin Belousov 
256e23731dbSKonstantin Belousov 	if (!(mlx5_ipsec_device_caps(mdev) &
257e23731dbSKonstantin Belousov 				MLX5_IPSEC_CAP_PACKET_OFFLOAD)) {
258e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "FULL offload is not supported\n");
259*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
260e23731dbSKonstantin Belousov 	}
2612851aafeSKonstantin Belousov 	if (savp->state == SADB_SASTATE_DEAD)
262*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
263e23731dbSKonstantin Belousov 	if (savp->alg_enc == SADB_EALG_NONE) {
264e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Cannot offload authenticated xfrm states\n");
265*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
266e23731dbSKonstantin Belousov 	}
267e23731dbSKonstantin Belousov 	if (savp->alg_enc != SADB_X_EALG_AESGCM16) {
268e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Only IPSec aes-gcm-16 encryption protocol may be offloaded\n");
269*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
270e23731dbSKonstantin Belousov 	}
271e23731dbSKonstantin Belousov 	if (savp->tdb_compalgxform) {
272e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Cannot offload compressed xfrm states\n");
273*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
274e23731dbSKonstantin Belousov 	}
275e23731dbSKonstantin Belousov 	if (savp->alg_auth != SADB_X_AALG_AES128GMAC && savp->alg_auth != SADB_X_AALG_AES256GMAC) {
276e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Cannot offload xfrm states with AEAD key length other than 128/256 bits\n");
277*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
278e23731dbSKonstantin Belousov 	}
279e23731dbSKonstantin Belousov 	if ((saidx->dst.sa.sa_family != AF_INET && saidx->dst.sa.sa_family != AF_INET6) ||
280e23731dbSKonstantin Belousov 	    (saidx->src.sa.sa_family != AF_INET && saidx->src.sa.sa_family != AF_INET6)) {
281e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Only IPv4/6 xfrm states may be offloaded\n");
282*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
283e23731dbSKonstantin Belousov 	}
284e23731dbSKonstantin Belousov 	if (saidx->proto != IPPROTO_ESP) {
285e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Only ESP xfrm state may be offloaded\n");
286*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
287e23731dbSKonstantin Belousov 	}
288e23731dbSKonstantin Belousov 	/* subtract off the salt, RFC4106, 8.1 and RFC3686, 5.1 */
289e23731dbSKonstantin Belousov 	keylen = _KEYLEN(key_encp) - SAV_ISCTRORGCM(savp) * 4 - SAV_ISCHACHA(savp) * 4;
290e23731dbSKonstantin Belousov 	if (keylen != 128/8 && keylen != 256 / 8) {
291e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Cannot offload xfrm states with AEAD key length other than 128/256 bit\n");
292*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
293e23731dbSKonstantin Belousov 	}
294e23731dbSKonstantin Belousov 
295e23731dbSKonstantin Belousov 	if (saidx->mode != IPSEC_MODE_TRANSPORT) {
2964c279534SKonstantin Belousov 		mlx5_core_err(mdev, "Only transport xfrm states may be offloaded in full offload mode\n");
297*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
298e23731dbSKonstantin Belousov 	}
299e23731dbSKonstantin Belousov 
300e23731dbSKonstantin Belousov 	if (savp->natt) {
301e23731dbSKonstantin Belousov 		if (!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_ESPINUDP)) {
302e23731dbSKonstantin Belousov 			mlx5_core_err(mdev, "Encapsulation is not supported\n");
303*1fbce7deSKonstantin Belousov 			return (EOPNOTSUPP);
304e23731dbSKonstantin Belousov 		}
305e23731dbSKonstantin Belousov         }
306e23731dbSKonstantin Belousov 
307e23731dbSKonstantin Belousov         if (savp->replay && savp->replay->wsize != 0 && savp->replay->wsize != 4 &&
308e23731dbSKonstantin Belousov 	    savp->replay->wsize != 8 && savp->replay->wsize != 16 && savp->replay->wsize != 32) {
309e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Unsupported replay window size %d\n", savp->replay->wsize);
310*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
311e23731dbSKonstantin Belousov 	}
312e23731dbSKonstantin Belousov 
313e23731dbSKonstantin Belousov 	if ((savp->flags & SADB_X_SAFLAGS_ESN) != 0) {
314e23731dbSKonstantin Belousov 		if ((mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_ESN) == 0) {
315e23731dbSKonstantin Belousov 			mlx5_core_err(mdev, "ESN is not supported\n");
316*1fbce7deSKonstantin Belousov 			return (EOPNOTSUPP);
317e23731dbSKonstantin Belousov 		}
318e23731dbSKonstantin Belousov 	} else if (savp->replay != NULL && savp->replay->wsize != 0) {
319e23731dbSKonstantin Belousov 		mlx5_core_warn(mdev,
320e23731dbSKonstantin Belousov 		    "non-ESN but replay-protect SA offload is not supported\n");
321*1fbce7deSKonstantin Belousov 		return (EOPNOTSUPP);
322e23731dbSKonstantin Belousov 	}
323e23731dbSKonstantin Belousov         return 0;
324e23731dbSKonstantin Belousov }
325e23731dbSKonstantin Belousov 
326e23731dbSKonstantin Belousov static int
mlx5e_if_sa_newkey_onedir(struct ifnet * ifp,void * sav,int dir,u_int drv_spi,struct mlx5e_ipsec_sa_entry ** privp,struct mlx5e_ipsec_priv_bothdir * pb,struct ifnet * ifpo)327205263acSAriel Ehrenberg mlx5e_if_sa_newkey_onedir(struct ifnet *ifp, void *sav, int dir, u_int drv_spi,
328205263acSAriel Ehrenberg     struct mlx5e_ipsec_sa_entry **privp, struct mlx5e_ipsec_priv_bothdir *pb,
329205263acSAriel Ehrenberg     struct ifnet *ifpo)
330e23731dbSKonstantin Belousov {
3318e5b07ddSKonstantin Belousov #ifdef IPSEC_OFFLOAD
3322851aafeSKonstantin Belousov 	struct rm_priotracker tracker;
3338e5b07ddSKonstantin Belousov #endif
334e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_sa_entry *sa_entry = NULL;
335e23731dbSKonstantin Belousov 	struct mlx5e_priv *priv = if_getsoftc(ifp);
336e23731dbSKonstantin Belousov 	struct mlx5_core_dev *mdev = priv->mdev;
337e23731dbSKonstantin Belousov 	struct mlx5e_ipsec *ipsec = priv->ipsec;
338205263acSAriel Ehrenberg 	u16 vid = VLAN_NONE;
339e23731dbSKonstantin Belousov 	int err;
340e23731dbSKonstantin Belousov 
341e23731dbSKonstantin Belousov 	if (priv->gone != 0 || ipsec == NULL)
342e23731dbSKonstantin Belousov 		return (EOPNOTSUPP);
343e23731dbSKonstantin Belousov 
344205263acSAriel Ehrenberg 	if (if_gettype(ifpo) == IFT_L2VLAN)
345205263acSAriel Ehrenberg 		VLAN_TAG(ifpo, &vid);
346205263acSAriel Ehrenberg 
3478e5b07ddSKonstantin Belousov #ifdef IPSEC_OFFLOAD
3482851aafeSKonstantin Belousov 	ipsec_sahtree_rlock(&tracker);
3498e5b07ddSKonstantin Belousov #endif
350e23731dbSKonstantin Belousov 	err = mlx5e_xfrm_validate_state(mdev, sav);
3518e5b07ddSKonstantin Belousov #ifdef IPSEC_OFFLOAD
3522851aafeSKonstantin Belousov 	ipsec_sahtree_runlock(&tracker);
3538e5b07ddSKonstantin Belousov #endif
354e23731dbSKonstantin Belousov 	if (err)
355e23731dbSKonstantin Belousov 		return err;
356e23731dbSKonstantin Belousov 
357e23731dbSKonstantin Belousov 	sa_entry = kzalloc(sizeof(*sa_entry), GFP_KERNEL);
358e23731dbSKonstantin Belousov 	if (sa_entry == NULL)
359e23731dbSKonstantin Belousov 		return (ENOMEM);
360e23731dbSKonstantin Belousov 
361e23731dbSKonstantin Belousov 	sa_entry->kspi = drv_spi;
362e23731dbSKonstantin Belousov 	sa_entry->savp = sav;
363e23731dbSKonstantin Belousov 	sa_entry->ifp = ifp;
364205263acSAriel Ehrenberg 	sa_entry->ifpo = ifpo;
365e23731dbSKonstantin Belousov 	sa_entry->ipsec = ipsec;
366205263acSAriel Ehrenberg 	sa_entry->vid = vid;
367e23731dbSKonstantin Belousov 
3688e5b07ddSKonstantin Belousov #ifdef IPSEC_OFFLOAD
3692851aafeSKonstantin Belousov 	ipsec_sahtree_rlock(&tracker);
3708e5b07ddSKonstantin Belousov #endif
3712851aafeSKonstantin Belousov 	err = mlx5e_xfrm_validate_state(mdev, sav);
3722851aafeSKonstantin Belousov 	if (err != 0) {
3738e5b07ddSKonstantin Belousov #ifdef IPSEC_OFFLOAD
3742851aafeSKonstantin Belousov 		ipsec_sahtree_runlock(&tracker);
3758e5b07ddSKonstantin Belousov #endif
3762851aafeSKonstantin Belousov 		goto err_xfrm;
3772851aafeSKonstantin Belousov 	}
378e23731dbSKonstantin Belousov 	mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &sa_entry->attrs, dir);
3798e5b07ddSKonstantin Belousov #ifdef IPSEC_OFFLOAD
3802851aafeSKonstantin Belousov 	ipsec_sahtree_runlock(&tracker);
3818e5b07ddSKonstantin Belousov #endif
382e23731dbSKonstantin Belousov 
383e23731dbSKonstantin Belousov 	err = mlx5e_ipsec_create_dwork(sa_entry, pb);
384e23731dbSKonstantin Belousov 	if (err)
385e23731dbSKonstantin Belousov 		goto err_xfrm;
386e23731dbSKonstantin Belousov 
387e23731dbSKonstantin Belousov 	/* create hw context */
388e23731dbSKonstantin Belousov 	err = mlx5_ipsec_create_sa_ctx(sa_entry);
389e23731dbSKonstantin Belousov 	if (err)
390e23731dbSKonstantin Belousov 		goto err_sa_ctx;
391e23731dbSKonstantin Belousov 
392e23731dbSKonstantin Belousov 	err = mlx5e_accel_ipsec_fs_add_rule(sa_entry);
393e23731dbSKonstantin Belousov 	if (err)
394e23731dbSKonstantin Belousov 		goto err_fs;
395e23731dbSKonstantin Belousov 
396e23731dbSKonstantin Belousov 	*privp = sa_entry;
397e23731dbSKonstantin Belousov 	if (sa_entry->dwork)
398e23731dbSKonstantin Belousov 		queue_delayed_work(ipsec->wq, &sa_entry->dwork->dwork, MLX5_IPSEC_RESCHED);
399e23731dbSKonstantin Belousov 
400e23731dbSKonstantin Belousov 	err = xa_insert(&mdev->ipsec_sadb, sa_entry->ipsec_obj_id, sa_entry, GFP_KERNEL);
401e23731dbSKonstantin Belousov 	if (err)
402e23731dbSKonstantin Belousov 		goto err_xa;
403e23731dbSKonstantin Belousov 
404e23731dbSKonstantin Belousov 	return 0;
405e23731dbSKonstantin Belousov 
406e23731dbSKonstantin Belousov err_xa:
407e23731dbSKonstantin Belousov 	if (sa_entry->dwork)
408e23731dbSKonstantin Belousov 		cancel_delayed_work_sync(&sa_entry->dwork->dwork);
409e23731dbSKonstantin Belousov 	mlx5e_accel_ipsec_fs_del_rule(sa_entry);
410e23731dbSKonstantin Belousov err_fs:
411e23731dbSKonstantin Belousov 	mlx5_ipsec_free_sa_ctx(sa_entry);
412e23731dbSKonstantin Belousov err_sa_ctx:
413e23731dbSKonstantin Belousov 	kfree(sa_entry->dwork);
414828da10bSKonstantin Belousov 	sa_entry->dwork = NULL;
415e23731dbSKonstantin Belousov err_xfrm:
416e23731dbSKonstantin Belousov 	kfree(sa_entry);
417e23731dbSKonstantin Belousov 	mlx5_en_err(ifp, "Device failed to offload this state");
418e23731dbSKonstantin Belousov 	return err;
419e23731dbSKonstantin Belousov }
420e23731dbSKonstantin Belousov 
421205263acSAriel Ehrenberg #define GET_TRUNK_IF(vifp, ifp, ept)          \
422205263acSAriel Ehrenberg 	if (if_gettype(vifp) == IFT_L2VLAN) { \
423205263acSAriel Ehrenberg 		NET_EPOCH_ENTER(ept);         \
424205263acSAriel Ehrenberg 		ifp = VLAN_TRUNKDEV(vifp);    \
425205263acSAriel Ehrenberg 		NET_EPOCH_EXIT(ept);          \
426205263acSAriel Ehrenberg 	} else {                              \
427205263acSAriel Ehrenberg 		ifp = vifp;                   \
428205263acSAriel Ehrenberg 	}
429205263acSAriel Ehrenberg 
430e23731dbSKonstantin Belousov static int
mlx5e_if_sa_newkey(struct ifnet * ifpo,void * sav,u_int dev_spi,void ** privp)431205263acSAriel Ehrenberg mlx5e_if_sa_newkey(struct ifnet *ifpo, void *sav, u_int dev_spi, void **privp)
432e23731dbSKonstantin Belousov {
433e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_priv_bothdir *pb;
434205263acSAriel Ehrenberg 	struct epoch_tracker et;
435205263acSAriel Ehrenberg 	struct ifnet *ifp;
436e23731dbSKonstantin Belousov 	int error;
437e23731dbSKonstantin Belousov 
438205263acSAriel Ehrenberg 	GET_TRUNK_IF(ifpo, ifp, et);
439205263acSAriel Ehrenberg 
440e23731dbSKonstantin Belousov 	pb = malloc(sizeof(struct mlx5e_ipsec_priv_bothdir), M_DEVBUF,
441e23731dbSKonstantin Belousov 	    M_WAITOK | M_ZERO);
442205263acSAriel Ehrenberg 	error = mlx5e_if_sa_newkey_onedir(
443205263acSAriel Ehrenberg 	    ifp, sav, IPSEC_DIR_INBOUND, dev_spi, &pb->priv_in, pb, ifpo);
444e23731dbSKonstantin Belousov 	if (error != 0) {
445e23731dbSKonstantin Belousov 		free(pb, M_DEVBUF);
446e23731dbSKonstantin Belousov 		return (error);
447e23731dbSKonstantin Belousov 	}
448205263acSAriel Ehrenberg 	error = mlx5e_if_sa_newkey_onedir(
449205263acSAriel Ehrenberg 	    ifp, sav, IPSEC_DIR_OUTBOUND, dev_spi, &pb->priv_out, pb, ifpo);
450e23731dbSKonstantin Belousov 	if (error == 0) {
451e23731dbSKonstantin Belousov 		*privp = pb;
452e23731dbSKonstantin Belousov 	} else {
453828da10bSKonstantin Belousov 		if (pb->priv_in->dwork != NULL)
454828da10bSKonstantin Belousov 			cancel_delayed_work_sync(&pb->priv_in->dwork->dwork);
455828da10bSKonstantin Belousov 		mlx5e_if_sa_deinstall_onekey(ifp, dev_spi, pb->priv_in);
456e23731dbSKonstantin Belousov 		free(pb, M_DEVBUF);
457e23731dbSKonstantin Belousov 	}
458e23731dbSKonstantin Belousov 	return (error);
459e23731dbSKonstantin Belousov }
460e23731dbSKonstantin Belousov 
461e23731dbSKonstantin Belousov static void
mlx5e_if_sa_deinstall_onekey(struct ifnet * ifp,u_int dev_spi,void * priv)462e23731dbSKonstantin Belousov mlx5e_if_sa_deinstall_onekey(struct ifnet *ifp, u_int dev_spi, void *priv)
463e23731dbSKonstantin Belousov {
464e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(priv);
465e23731dbSKonstantin Belousov 	struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
466e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_sa_entry *old;
467e23731dbSKonstantin Belousov 
468e23731dbSKonstantin Belousov 	old = xa_erase(&mdev->ipsec_sadb, sa_entry->ipsec_obj_id);
469e23731dbSKonstantin Belousov 	WARN_ON(old != sa_entry);
470e23731dbSKonstantin Belousov 
471e23731dbSKonstantin Belousov 	mlx5e_accel_ipsec_fs_del_rule(sa_entry);
472e23731dbSKonstantin Belousov 	mlx5_ipsec_free_sa_ctx(sa_entry);
473e23731dbSKonstantin Belousov 	kfree(sa_entry->dwork);
474e23731dbSKonstantin Belousov 	kfree(sa_entry);
475e23731dbSKonstantin Belousov }
476e23731dbSKonstantin Belousov 
477e23731dbSKonstantin Belousov static int
mlx5e_if_sa_deinstall(struct ifnet * ifpo,u_int dev_spi,void * priv)478205263acSAriel Ehrenberg mlx5e_if_sa_deinstall(struct ifnet *ifpo, u_int dev_spi, void *priv)
479e23731dbSKonstantin Belousov {
480e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_priv_bothdir pb, *pbp;
481205263acSAriel Ehrenberg 	struct epoch_tracker et;
482205263acSAriel Ehrenberg 	struct ifnet *ifp;
483205263acSAriel Ehrenberg 
484205263acSAriel Ehrenberg 	GET_TRUNK_IF(ifpo, ifp, et);
485e23731dbSKonstantin Belousov 
486e23731dbSKonstantin Belousov 	pbp = priv;
487e23731dbSKonstantin Belousov 	pb = *(struct mlx5e_ipsec_priv_bothdir *)priv;
488e23731dbSKonstantin Belousov 	pbp->priv_in = pbp->priv_out = NULL;
489e23731dbSKonstantin Belousov 
490e23731dbSKonstantin Belousov 	if (pb.priv_in->dwork != NULL)
491e23731dbSKonstantin Belousov 		cancel_delayed_work_sync(&pb.priv_in->dwork->dwork);
492e23731dbSKonstantin Belousov 	if (pb.priv_out->dwork != NULL)
493e23731dbSKonstantin Belousov 		cancel_delayed_work_sync(&pb.priv_out->dwork->dwork);
494e23731dbSKonstantin Belousov 
495e23731dbSKonstantin Belousov 	mlx5e_if_sa_deinstall_onekey(ifp, dev_spi, pb.priv_in);
496e23731dbSKonstantin Belousov 	mlx5e_if_sa_deinstall_onekey(ifp, dev_spi, pb.priv_out);
497e23731dbSKonstantin Belousov 	free(pbp, M_DEVBUF);
498e23731dbSKonstantin Belousov 	return (0);
499e23731dbSKonstantin Belousov }
500e23731dbSKonstantin Belousov 
501e23731dbSKonstantin Belousov static void
mlx5e_if_sa_cnt_one(struct ifnet * ifp,void * sa,uint32_t drv_spi,void * priv,u64 * bytes,u64 * packets)502e23731dbSKonstantin Belousov mlx5e_if_sa_cnt_one(struct ifnet *ifp, void *sa, uint32_t drv_spi,
503e23731dbSKonstantin Belousov     void *priv, u64 *bytes, u64 *packets)
504e23731dbSKonstantin Belousov {
505e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(priv);
506e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
507e23731dbSKonstantin Belousov 	struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
508e23731dbSKonstantin Belousov 
509e23731dbSKonstantin Belousov 	mlx5_fc_query(mdev, ipsec_rule->fc, packets, bytes);
510e23731dbSKonstantin Belousov }
511e23731dbSKonstantin Belousov 
512e23731dbSKonstantin Belousov static int
mlx5e_if_sa_cnt(struct ifnet * ifpo,void * sa,uint32_t drv_spi,void * priv,struct seclifetime * lt)513205263acSAriel Ehrenberg mlx5e_if_sa_cnt(struct ifnet *ifpo, void *sa, uint32_t drv_spi, void *priv,
514205263acSAriel Ehrenberg     struct seclifetime *lt)
515e23731dbSKonstantin Belousov {
516e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_priv_bothdir *pb;
517e23731dbSKonstantin Belousov 	u64 packets_in, packets_out;
518e23731dbSKonstantin Belousov 	u64 bytes_in, bytes_out;
519205263acSAriel Ehrenberg 	struct epoch_tracker et;
520205263acSAriel Ehrenberg 	struct ifnet *ifp;
521205263acSAriel Ehrenberg 
522205263acSAriel Ehrenberg 	GET_TRUNK_IF(ifpo, ifp, et);
523e23731dbSKonstantin Belousov 
524e23731dbSKonstantin Belousov 	pb = priv;
525e23731dbSKonstantin Belousov 	mlx5e_if_sa_cnt_one(ifp, sa, drv_spi, pb->priv_in,
526e23731dbSKonstantin Belousov 	    &bytes_in, &packets_in);
527e23731dbSKonstantin Belousov 	mlx5e_if_sa_cnt_one(ifp, sa, drv_spi, pb->priv_out,
528e23731dbSKonstantin Belousov 	    &bytes_out, &packets_out);
529e23731dbSKonstantin Belousov 	/* TODO: remove this casting once Kostia changes allocation type to be u64 */
530e23731dbSKonstantin Belousov 	lt->bytes = bytes_in + bytes_out;
531e23731dbSKonstantin Belousov 	lt->allocations = (uint32_t)(packets_in + packets_out);
532e23731dbSKonstantin Belousov 	return (0);
533e23731dbSKonstantin Belousov }
534e23731dbSKonstantin Belousov 
mlx5e_xfrm_validate_policy(struct mlx5_core_dev * mdev,struct secpolicy * sp,struct inpcb * inp)535e23731dbSKonstantin Belousov static int mlx5e_xfrm_validate_policy(struct mlx5_core_dev *mdev,
536e23731dbSKonstantin Belousov                                       struct secpolicy *sp, struct inpcb *inp)
537e23731dbSKonstantin Belousov {
538e23731dbSKonstantin Belousov 	struct secpolicyindex *spidx = &sp->spidx;
539e23731dbSKonstantin Belousov 
540e23731dbSKonstantin Belousov 	if (!(mlx5_ipsec_device_caps(mdev) &
541e23731dbSKonstantin Belousov 				MLX5_IPSEC_CAP_PACKET_OFFLOAD)) {
542e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "FULL offload is not supported\n");
543e23731dbSKonstantin Belousov 		return (EINVAL);
544e23731dbSKonstantin Belousov 	}
545e23731dbSKonstantin Belousov 
546e23731dbSKonstantin Belousov         if (sp->tcount > 1) {
547e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Can offload exactly one template, "
548e23731dbSKonstantin Belousov 		    "not %d\n", sp->tcount);
549e23731dbSKonstantin Belousov                 return (EINVAL);
550e23731dbSKonstantin Belousov         }
551e23731dbSKonstantin Belousov 
552e23731dbSKonstantin Belousov         if (sp->policy == IPSEC_POLICY_BYPASS &&
553e23731dbSKonstantin Belousov             !(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PRIO)) {
554e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Device does not support policy priority\n");
555e23731dbSKonstantin Belousov 		return (EINVAL);
556e23731dbSKonstantin Belousov 	}
557e23731dbSKonstantin Belousov 
558e23731dbSKonstantin Belousov 	if (sp->tcount > 0 && inp != NULL) {
559e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Not valid input data\n");
560e23731dbSKonstantin Belousov 		return (EINVAL);
561e23731dbSKonstantin Belousov 	}
562e23731dbSKonstantin Belousov 
563e23731dbSKonstantin Belousov 	if (spidx->dir != IPSEC_DIR_INBOUND && spidx->dir != IPSEC_DIR_OUTBOUND) {
564e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Wrong policy direction\n");
565e23731dbSKonstantin Belousov 		return (EINVAL);
566e23731dbSKonstantin Belousov 	}
567e23731dbSKonstantin Belousov 
568e23731dbSKonstantin Belousov 	if (sp->tcount > 0 && sp->req[0]->saidx.mode != IPSEC_MODE_TRANSPORT) {
569e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Device supports transport mode only");
570e23731dbSKonstantin Belousov 		return (EINVAL);
571e23731dbSKonstantin Belousov 	}
572e23731dbSKonstantin Belousov 
573e23731dbSKonstantin Belousov         if (sp->policy != IPSEC_POLICY_DISCARD &&
574e23731dbSKonstantin Belousov             sp->policy != IPSEC_POLICY_IPSEC && sp->policy != IPSEC_POLICY_BYPASS) {
575e23731dbSKonstantin Belousov                 mlx5_core_err(mdev, "Offloaded policy must be specific on its action\n");
576e23731dbSKonstantin Belousov 		return (EINVAL);
577e23731dbSKonstantin Belousov         }
578e23731dbSKonstantin Belousov 
579e23731dbSKonstantin Belousov 	if (sp->policy == IPSEC_POLICY_BYPASS && !inp) {
580e23731dbSKonstantin Belousov 		mlx5_core_err(mdev, "Missing port information for IKE bypass\n");
581e23731dbSKonstantin Belousov 		return (EINVAL);
582e23731dbSKonstantin Belousov 	}
583e23731dbSKonstantin Belousov 
584e23731dbSKonstantin Belousov 	if (inp != NULL) {
585e23731dbSKonstantin Belousov 		INP_RLOCK(inp);
586e23731dbSKonstantin Belousov 		if (inp->inp_socket == NULL || inp->inp_socket->so_proto->
587e23731dbSKonstantin Belousov 		    pr_protocol != IPPROTO_UDP) {
588e23731dbSKonstantin Belousov 			mlx5_core_err(mdev, "Unsupported IKE bypass protocol %d\n",
589e23731dbSKonstantin Belousov 			    inp->inp_socket == NULL ? -1 :
590e23731dbSKonstantin Belousov 			    inp->inp_socket->so_proto->pr_protocol);
591e23731dbSKonstantin Belousov 			INP_RUNLOCK(inp);
592e23731dbSKonstantin Belousov 			return (EINVAL);
593e23731dbSKonstantin Belousov 		}
594e23731dbSKonstantin Belousov 		INP_RUNLOCK(inp);
595e23731dbSKonstantin Belousov 	}
596e23731dbSKonstantin Belousov 
597e23731dbSKonstantin Belousov         /* TODO fill relevant bits */
598e23731dbSKonstantin Belousov 	return 0;
599e23731dbSKonstantin Belousov }
600e23731dbSKonstantin Belousov 
601205263acSAriel Ehrenberg static void
mlx5e_ipsec_build_accel_pol_attrs(struct mlx5e_ipsec_pol_entry * pol_entry,struct mlx5_accel_pol_xfrm_attrs * attrs,struct inpcb * inp,u16 vid)602205263acSAriel Ehrenberg mlx5e_ipsec_build_accel_pol_attrs(struct mlx5e_ipsec_pol_entry *pol_entry,
603205263acSAriel Ehrenberg     struct mlx5_accel_pol_xfrm_attrs *attrs, struct inpcb *inp, u16 vid)
604e23731dbSKonstantin Belousov {
605e23731dbSKonstantin Belousov 	struct secpolicy *sp = pol_entry->sp;
606e23731dbSKonstantin Belousov 	struct secpolicyindex *spidx = &sp->spidx;
607e23731dbSKonstantin Belousov 
608e23731dbSKonstantin Belousov 	memset(attrs, 0, sizeof(*attrs));
609e23731dbSKonstantin Belousov 
610e23731dbSKonstantin Belousov 	if (!inp) {
611e23731dbSKonstantin Belousov 		if (spidx->src.sa.sa_family == AF_INET) {
612e23731dbSKonstantin Belousov 			attrs->saddr.a4 = spidx->src.sin.sin_addr.s_addr;
613e23731dbSKonstantin Belousov 			attrs->daddr.a4 = spidx->dst.sin.sin_addr.s_addr;
614e23731dbSKonstantin Belousov 		} else if (spidx->src.sa.sa_family == AF_INET6) {
615e23731dbSKonstantin Belousov 			memcpy(&attrs->saddr.a6, &spidx->src.sin6.sin6_addr, 16);
616e23731dbSKonstantin Belousov 			memcpy(&attrs->daddr.a6, &spidx->dst.sin6.sin6_addr, 16);
617e23731dbSKonstantin Belousov 		} else {
618e23731dbSKonstantin Belousov 			KASSERT(0, ("unsupported family %d", spidx->src.sa.sa_family));
619e23731dbSKonstantin Belousov 		}
620e23731dbSKonstantin Belousov 		attrs->family = spidx->src.sa.sa_family;
621e23731dbSKonstantin Belousov 		attrs->prio = 0;
622e23731dbSKonstantin Belousov 		attrs->action = sp->policy;
623e23731dbSKonstantin Belousov 		attrs->reqid = sp->req[0]->saidx.reqid;
624e23731dbSKonstantin Belousov 	} else {
625e23731dbSKonstantin Belousov 		INP_RLOCK(inp);
626e23731dbSKonstantin Belousov 		if ((inp->inp_vflag & INP_IPV4) != 0) {
627e23731dbSKonstantin Belousov 			attrs->saddr.a4 = inp->inp_laddr.s_addr;
628e23731dbSKonstantin Belousov 			attrs->daddr.a4 = inp->inp_faddr.s_addr;
629e23731dbSKonstantin Belousov 			attrs->family = AF_INET;
630e23731dbSKonstantin Belousov 		} else if ((inp->inp_vflag & INP_IPV6) != 0) {
631e23731dbSKonstantin Belousov 			memcpy(&attrs->saddr.a6, &inp->in6p_laddr, 16);
632e23731dbSKonstantin Belousov 			memcpy(&attrs->daddr.a6, &inp->in6p_laddr, 16);
633e23731dbSKonstantin Belousov 			attrs->family = AF_INET6;
634e23731dbSKonstantin Belousov 		} else {
635e23731dbSKonstantin Belousov 			KASSERT(0, ("unsupported family %d", inp->inp_vflag));
636e23731dbSKonstantin Belousov 		}
637e23731dbSKonstantin Belousov 		attrs->upspec.dport = inp->inp_fport;
638e23731dbSKonstantin Belousov 		attrs->upspec.sport = inp->inp_lport;
639e23731dbSKonstantin Belousov 		attrs->upspec.proto = inp->inp_ip_p;
640e23731dbSKonstantin Belousov 		INP_RUNLOCK(inp);
641e23731dbSKonstantin Belousov 
642e23731dbSKonstantin Belousov 		/* Give highest priority for PCB policies */
643e23731dbSKonstantin Belousov 		attrs->prio = 1;
644e23731dbSKonstantin Belousov 		attrs->action = IPSEC_POLICY_IPSEC;
645e23731dbSKonstantin Belousov 	}
646e23731dbSKonstantin Belousov 	attrs->dir = spidx->dir;
647205263acSAriel Ehrenberg 	attrs->vid = vid;
648e23731dbSKonstantin Belousov }
649e23731dbSKonstantin Belousov 
650205263acSAriel Ehrenberg static int
mlx5e_if_spd_install(struct ifnet * ifpo,void * sp,void * inp1,void ** ifdatap)651205263acSAriel Ehrenberg mlx5e_if_spd_install(struct ifnet *ifpo, void *sp, void *inp1, void **ifdatap)
652e23731dbSKonstantin Belousov {
653e23731dbSKonstantin Belousov 	struct mlx5e_ipsec_pol_entry *pol_entry;
654e23731dbSKonstantin Belousov 	struct mlx5e_priv *priv;
655205263acSAriel Ehrenberg 	struct epoch_tracker et;
656205263acSAriel Ehrenberg 	u16 vid = VLAN_NONE;
657205263acSAriel Ehrenberg 	struct ifnet *ifp;
658e23731dbSKonstantin Belousov 	int err;
659e23731dbSKonstantin Belousov 
660205263acSAriel Ehrenberg 	GET_TRUNK_IF(ifpo, ifp, et);
661205263acSAriel Ehrenberg 	if (if_gettype(ifpo) == IFT_L2VLAN)
662205263acSAriel Ehrenberg 		VLAN_TAG(ifpo, &vid);
663e23731dbSKonstantin Belousov 	priv = if_getsoftc(ifp);
664e23731dbSKonstantin Belousov 	if (priv->gone || !priv->ipsec)
665e23731dbSKonstantin Belousov 		return (EOPNOTSUPP);
666e23731dbSKonstantin Belousov 
667e23731dbSKonstantin Belousov 	err = mlx5e_xfrm_validate_policy(priv->mdev, sp, inp1);
668e23731dbSKonstantin Belousov 	if (err)
669e23731dbSKonstantin Belousov 		return err;
670e23731dbSKonstantin Belousov 
671e23731dbSKonstantin Belousov 	pol_entry = kzalloc(sizeof(*pol_entry), GFP_KERNEL);
672e23731dbSKonstantin Belousov 	if (!pol_entry)
673e23731dbSKonstantin Belousov 		return (ENOMEM);
674e23731dbSKonstantin Belousov 
675e23731dbSKonstantin Belousov 	pol_entry->sp = sp;
676e23731dbSKonstantin Belousov 	pol_entry->ipsec = priv->ipsec;
677e23731dbSKonstantin Belousov 
678205263acSAriel Ehrenberg 	mlx5e_ipsec_build_accel_pol_attrs(pol_entry, &pol_entry->attrs,
679205263acSAriel Ehrenberg 	    inp1, vid);
680e23731dbSKonstantin Belousov 	err = mlx5e_accel_ipsec_fs_add_pol(pol_entry);
681e23731dbSKonstantin Belousov 	if (err)
682e23731dbSKonstantin Belousov 		goto err_pol;
683e23731dbSKonstantin Belousov 	*ifdatap = pol_entry;
684e23731dbSKonstantin Belousov 
685e23731dbSKonstantin Belousov 	return 0;
686e23731dbSKonstantin Belousov 
687e23731dbSKonstantin Belousov err_pol:
688e23731dbSKonstantin Belousov 	kfree(pol_entry);
689e23731dbSKonstantin Belousov 	mlx5_en_err(ifp, "Device failed to offload this policy");
690e23731dbSKonstantin Belousov 	return err;
691e23731dbSKonstantin Belousov }
692e23731dbSKonstantin Belousov 
693205263acSAriel Ehrenberg static int
mlx5e_if_spd_deinstall(struct ifnet * ifpo,void * sp,void * ifdata)694205263acSAriel Ehrenberg mlx5e_if_spd_deinstall(struct ifnet *ifpo, void *sp, void *ifdata)
695e23731dbSKonstantin Belousov {
696205263acSAriel Ehrenberg 	struct mlx5e_ipsec_pol_entry *pol_entry;
697e23731dbSKonstantin Belousov 
698205263acSAriel Ehrenberg 	pol_entry = to_ipsec_pol_entry(ifdata);
699e23731dbSKonstantin Belousov 	mlx5e_accel_ipsec_fs_del_pol(pol_entry);
700e23731dbSKonstantin Belousov 	kfree(pol_entry);
701e23731dbSKonstantin Belousov 	return 0;
702e23731dbSKonstantin Belousov }
703e23731dbSKonstantin Belousov 
mlx5e_ipsec_cleanup(struct mlx5e_priv * priv)704e23731dbSKonstantin Belousov void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv)
705e23731dbSKonstantin Belousov {
706e23731dbSKonstantin Belousov 	struct mlx5e_ipsec *pipsec = priv->ipsec;
707e23731dbSKonstantin Belousov 	if (!pipsec)
708e23731dbSKonstantin Belousov 		return;
709e23731dbSKonstantin Belousov 
710e23731dbSKonstantin Belousov 	mlx5e_accel_ipsec_fs_cleanup(pipsec);
711e23731dbSKonstantin Belousov 	destroy_workqueue(pipsec->wq);
712e23731dbSKonstantin Belousov 	mlx5e_ipsec_aso_cleanup(pipsec);
713e23731dbSKonstantin Belousov 	kfree(pipsec);
714e23731dbSKonstantin Belousov 	priv->ipsec = NULL;
715e23731dbSKonstantin Belousov }
716e23731dbSKonstantin Belousov 
717e23731dbSKonstantin Belousov static int
mlx5e_if_ipsec_hwassist(if_t ifneto,void * sav __unused,uint32_t drv_spi __unused,void * priv __unused)718205263acSAriel Ehrenberg mlx5e_if_ipsec_hwassist(if_t ifneto, void *sav __unused,
719e23731dbSKonstantin Belousov     uint32_t drv_spi __unused, void *priv __unused)
720e23731dbSKonstantin Belousov {
721205263acSAriel Ehrenberg 	if_t ifnet;
722205263acSAriel Ehrenberg 
723205263acSAriel Ehrenberg 	if (if_gettype(ifneto) == IFT_L2VLAN) {
724205263acSAriel Ehrenberg 		ifnet = VLAN_TRUNKDEV(ifneto);
725205263acSAriel Ehrenberg 	} else {
726205263acSAriel Ehrenberg 		ifnet = ifneto;
727205263acSAriel Ehrenberg 	}
728205263acSAriel Ehrenberg 
729e23731dbSKonstantin Belousov 	return (if_gethwassist(ifnet) & (CSUM_TSO | CSUM_TCP | CSUM_UDP |
730e23731dbSKonstantin Belousov 	    CSUM_IP | CSUM_IP6_TSO | CSUM_IP6_TCP | CSUM_IP6_UDP));
731e23731dbSKonstantin Belousov }
732e23731dbSKonstantin Belousov 
733e23731dbSKonstantin Belousov static const struct if_ipsec_accel_methods  mlx5e_ipsec_funcs = {
734e23731dbSKonstantin Belousov 	.if_sa_newkey = mlx5e_if_sa_newkey,
735e23731dbSKonstantin Belousov 	.if_sa_deinstall = mlx5e_if_sa_deinstall,
736e23731dbSKonstantin Belousov 	.if_spdadd = mlx5e_if_spd_install,
737e23731dbSKonstantin Belousov 	.if_spddel = mlx5e_if_spd_deinstall,
738e23731dbSKonstantin Belousov 	.if_sa_cnt = mlx5e_if_sa_cnt,
739e23731dbSKonstantin Belousov 	.if_hwassist = mlx5e_if_ipsec_hwassist,
740e23731dbSKonstantin Belousov };
741e23731dbSKonstantin Belousov 
mlx5e_ipsec_init(struct mlx5e_priv * priv)742e23731dbSKonstantin Belousov int mlx5e_ipsec_init(struct mlx5e_priv *priv)
743e23731dbSKonstantin Belousov {
744e23731dbSKonstantin Belousov 	struct mlx5_core_dev *mdev = priv->mdev;
745e23731dbSKonstantin Belousov 	struct mlx5e_ipsec *pipsec;
746e23731dbSKonstantin Belousov 	if_t ifp = priv->ifp;
747e23731dbSKonstantin Belousov 	int ret;
748e23731dbSKonstantin Belousov 
749e23731dbSKonstantin Belousov 	mlx5_core_info(mdev, "ipsec "
750e23731dbSKonstantin Belousov 	    "offload %d log_max_dek %d gen_obj_types %d "
751e23731dbSKonstantin Belousov 	    "ipsec_encrypt %d ipsec_decrypt %d "
752e23731dbSKonstantin Belousov 	    "esp_aes_gcm_128_encrypt %d esp_aes_gcm_128_decrypt %d "
753e23731dbSKonstantin Belousov 	    "ipsec_full_offload %d "
754e23731dbSKonstantin Belousov 	    "reformat_add_esp_trasport %d reformat_del_esp_trasport %d "
755e23731dbSKonstantin Belousov 	    "decap %d "
756e23731dbSKonstantin Belousov 	    "ignore_flow_level_tx %d ignore_flow_level_rx %d "
757e23731dbSKonstantin Belousov 	    "reformat_natt_tx %d reformat_natt_rx %d "
758e23731dbSKonstantin Belousov 	    "ipsec_esn %d\n",
759e23731dbSKonstantin Belousov 	    MLX5_CAP_GEN(mdev, ipsec_offload) != 0,
760e23731dbSKonstantin Belousov 	    MLX5_CAP_GEN(mdev, log_max_dek) != 0,
761e23731dbSKonstantin Belousov 	    (MLX5_CAP_GEN_64(mdev, general_obj_types) &
762e23731dbSKonstantin Belousov 		MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_IPSEC) != 0,
763e23731dbSKonstantin Belousov 	    MLX5_CAP_FLOWTABLE_NIC_TX(mdev, ipsec_encrypt) != 0,
764e23731dbSKonstantin Belousov 	    MLX5_CAP_FLOWTABLE_NIC_RX(mdev, ipsec_decrypt) != 0,
765e23731dbSKonstantin Belousov 	    MLX5_CAP_IPSEC(mdev, ipsec_crypto_esp_aes_gcm_128_encrypt) != 0,
766e23731dbSKonstantin Belousov 	    MLX5_CAP_IPSEC(mdev, ipsec_crypto_esp_aes_gcm_128_decrypt) != 0,
767e23731dbSKonstantin Belousov 	    MLX5_CAP_IPSEC(mdev, ipsec_full_offload) != 0,
768e23731dbSKonstantin Belousov             MLX5_CAP_FLOWTABLE_NIC_TX(mdev, reformat_add_esp_trasport) != 0,
769e23731dbSKonstantin Belousov             MLX5_CAP_FLOWTABLE_NIC_RX(mdev, reformat_del_esp_trasport) != 0,
770e23731dbSKonstantin Belousov             MLX5_CAP_FLOWTABLE_NIC_RX(mdev, decap) != 0,
771e23731dbSKonstantin Belousov 	    MLX5_CAP_FLOWTABLE_NIC_TX(mdev, ignore_flow_level) != 0,
772e23731dbSKonstantin Belousov 	    MLX5_CAP_FLOWTABLE_NIC_RX(mdev, ignore_flow_level) != 0,
773e23731dbSKonstantin Belousov 	    MLX5_CAP_FLOWTABLE_NIC_TX(mdev,
774e23731dbSKonstantin Belousov 	        reformat_add_esp_transport_over_udp) != 0,
775e23731dbSKonstantin Belousov 	    MLX5_CAP_FLOWTABLE_NIC_RX(mdev,
776e23731dbSKonstantin Belousov 		reformat_del_esp_transport_over_udp) != 0,
777e23731dbSKonstantin Belousov 	    MLX5_CAP_IPSEC(mdev, ipsec_esn) != 0);
778e23731dbSKonstantin Belousov 
779e23731dbSKonstantin Belousov 	if (!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD)) {
780e23731dbSKonstantin Belousov 		mlx5_core_dbg(mdev, "Not an IPSec offload device\n");
781e23731dbSKonstantin Belousov 		return 0;
782e23731dbSKonstantin Belousov 	}
783e23731dbSKonstantin Belousov 
784e23731dbSKonstantin Belousov 	xa_init_flags(&mdev->ipsec_sadb, XA_FLAGS_ALLOC);
785e23731dbSKonstantin Belousov 
786e23731dbSKonstantin Belousov 	pipsec = kzalloc(sizeof(*pipsec), GFP_KERNEL);
787e23731dbSKonstantin Belousov 	if (pipsec == NULL)
788e23731dbSKonstantin Belousov 		return (ENOMEM);
789e23731dbSKonstantin Belousov 
790e23731dbSKonstantin Belousov 	pipsec->mdev = mdev;
791e23731dbSKonstantin Belousov 	pipsec->pdn = priv->pdn;
792e23731dbSKonstantin Belousov 	pipsec->mkey = priv->mr.key;
793e23731dbSKonstantin Belousov 
794e23731dbSKonstantin Belousov 	ret = mlx5e_ipsec_aso_init(pipsec);
795e23731dbSKonstantin Belousov 	if (ret)
796e23731dbSKonstantin Belousov 		goto err_ipsec_aso;
797e23731dbSKonstantin Belousov 
798e23731dbSKonstantin Belousov 	pipsec->wq = alloc_workqueue("mlx5e_ipsec", WQ_UNBOUND, 0);
799e23731dbSKonstantin Belousov 	if (pipsec->wq == NULL) {
800e23731dbSKonstantin Belousov 		ret = ENOMEM;
801e23731dbSKonstantin Belousov 		goto err_ipsec_wq;
802e23731dbSKonstantin Belousov 	}
803e23731dbSKonstantin Belousov 
804e23731dbSKonstantin Belousov 	ret = mlx5e_accel_ipsec_fs_init(pipsec);
805e23731dbSKonstantin Belousov 	if (ret)
806e23731dbSKonstantin Belousov 		goto err_ipsec_alloc;
807e23731dbSKonstantin Belousov 
808e23731dbSKonstantin Belousov 	if_setipsec_accel_methods(ifp, &mlx5e_ipsec_funcs);
809e23731dbSKonstantin Belousov 	priv->ipsec = pipsec;
810e23731dbSKonstantin Belousov 	mlx5_core_dbg(mdev, "IPSec attached to netdevice\n");
811e23731dbSKonstantin Belousov 	return 0;
812e23731dbSKonstantin Belousov 
813e23731dbSKonstantin Belousov err_ipsec_alloc:
814e23731dbSKonstantin Belousov 	destroy_workqueue(pipsec->wq);
815e23731dbSKonstantin Belousov err_ipsec_wq:
816e23731dbSKonstantin Belousov 	mlx5e_ipsec_aso_cleanup(pipsec);
817e23731dbSKonstantin Belousov err_ipsec_aso:
818e23731dbSKonstantin Belousov 	kfree(pipsec);
819e23731dbSKonstantin Belousov 	mlx5_core_err(priv->mdev, "IPSec initialization failed, %d\n", ret);
820e23731dbSKonstantin Belousov 	return ret;
821e23731dbSKonstantin Belousov }
822