xref: /openbmc/linux/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
157f273adSHuanhuan Wang // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
257f273adSHuanhuan Wang /* Copyright (C) 2018 Netronome Systems, Inc */
357f273adSHuanhuan Wang /* Copyright (C) 2021 Corigine, Inc */
457f273adSHuanhuan Wang 
557f273adSHuanhuan Wang #include <linux/module.h>
657f273adSHuanhuan Wang #include <linux/kernel.h>
757f273adSHuanhuan Wang #include <linux/init.h>
857f273adSHuanhuan Wang #include <linux/netdevice.h>
957f273adSHuanhuan Wang #include <asm/unaligned.h>
1057f273adSHuanhuan Wang #include <linux/ktime.h>
1157f273adSHuanhuan Wang #include <net/xfrm.h>
1257f273adSHuanhuan Wang 
13436396f2SHuanhuan Wang #include "../nfpcore/nfp_dev.h"
1457f273adSHuanhuan Wang #include "../nfp_net_ctrl.h"
1557f273adSHuanhuan Wang #include "../nfp_net.h"
1657f273adSHuanhuan Wang #include "crypto.h"
1757f273adSHuanhuan Wang 
1857f273adSHuanhuan Wang #define NFP_NET_IPSEC_MAX_SA_CNT  (16 * 1024) /* Firmware support a maximum of 16K SA offload */
1957f273adSHuanhuan Wang 
20859a497fSHuanhuan Wang /* IPsec config message cmd codes */
21859a497fSHuanhuan Wang enum nfp_ipsec_cfg_mssg_cmd_codes {
22859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_ADD_SA,	 /* Add a new SA */
23859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_INV_SA	 /* Invalidate an existing SA */
24859a497fSHuanhuan Wang };
25859a497fSHuanhuan Wang 
26859a497fSHuanhuan Wang /* IPsec config message response codes */
27859a497fSHuanhuan Wang enum nfp_ipsec_cfg_mssg_rsp_codes {
28859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_OK,
29859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_FAILED,
30859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_SA_VALID,
31859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_SA_HASH_ADD_FAILED,
32859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_SA_HASH_DEL_FAILED,
33859a497fSHuanhuan Wang 	NFP_IPSEC_CFG_MSSG_SA_INVALID_CMD
34859a497fSHuanhuan Wang };
35859a497fSHuanhuan Wang 
36859a497fSHuanhuan Wang /* Protocol */
37859a497fSHuanhuan Wang enum nfp_ipsec_sa_prot {
38859a497fSHuanhuan Wang 	NFP_IPSEC_PROTOCOL_AH = 0,
39859a497fSHuanhuan Wang 	NFP_IPSEC_PROTOCOL_ESP = 1
40859a497fSHuanhuan Wang };
41859a497fSHuanhuan Wang 
42859a497fSHuanhuan Wang /* Mode */
43859a497fSHuanhuan Wang enum nfp_ipsec_sa_mode {
44859a497fSHuanhuan Wang 	NFP_IPSEC_PROTMODE_TRANSPORT = 0,
45859a497fSHuanhuan Wang 	NFP_IPSEC_PROTMODE_TUNNEL = 1
46859a497fSHuanhuan Wang };
47859a497fSHuanhuan Wang 
48859a497fSHuanhuan Wang /* Cipher types */
49859a497fSHuanhuan Wang enum nfp_ipsec_sa_cipher {
50859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_NULL,
51859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_3DES,
52859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_AES128,
53859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_AES192,
54859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_AES256,
55859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_AES128_NULL,
56859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_AES192_NULL,
57859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_AES256_NULL,
58859a497fSHuanhuan Wang 	NFP_IPSEC_CIPHER_CHACHA20
59859a497fSHuanhuan Wang };
60859a497fSHuanhuan Wang 
61859a497fSHuanhuan Wang /* Cipher modes */
62859a497fSHuanhuan Wang enum nfp_ipsec_sa_cipher_mode {
63859a497fSHuanhuan Wang 	NFP_IPSEC_CIMODE_ECB,
64859a497fSHuanhuan Wang 	NFP_IPSEC_CIMODE_CBC,
65859a497fSHuanhuan Wang 	NFP_IPSEC_CIMODE_CFB,
66859a497fSHuanhuan Wang 	NFP_IPSEC_CIMODE_OFB,
67859a497fSHuanhuan Wang 	NFP_IPSEC_CIMODE_CTR
68859a497fSHuanhuan Wang };
69859a497fSHuanhuan Wang 
70859a497fSHuanhuan Wang /* Hash types */
71859a497fSHuanhuan Wang enum nfp_ipsec_sa_hash_type {
72859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_NONE,
73859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_MD5_96,
74859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA1_96,
75859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA256_96,
76859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA384_96,
77859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA512_96,
78859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_MD5_128,
79859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA1_80,
80859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA256_128,
81859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA384_192,
82859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_SHA512_256,
83859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_GF128_128,
84859a497fSHuanhuan Wang 	NFP_IPSEC_HASH_POLY1305_128
85859a497fSHuanhuan Wang };
86859a497fSHuanhuan Wang 
87859a497fSHuanhuan Wang /* IPSEC_CFG_MSSG_ADD_SA */
88859a497fSHuanhuan Wang struct nfp_ipsec_cfg_add_sa {
89859a497fSHuanhuan Wang 	u32 ciph_key[8];		  /* Cipher Key */
90859a497fSHuanhuan Wang 	union {
91859a497fSHuanhuan Wang 		u32 auth_key[16];	  /* Authentication Key */
92859a497fSHuanhuan Wang 		struct nfp_ipsec_aesgcm { /* AES-GCM-ESP fields */
93859a497fSHuanhuan Wang 			u32 salt;	  /* Initialized with SA */
94859a497fSHuanhuan Wang 			u32 resv[15];
95859a497fSHuanhuan Wang 		} aesgcm_fields;
96859a497fSHuanhuan Wang 	};
97859a497fSHuanhuan Wang 	struct sa_ctrl_word {
98859a497fSHuanhuan Wang 		uint32_t hash   :4;	  /* From nfp_ipsec_sa_hash_type */
99859a497fSHuanhuan Wang 		uint32_t cimode :4;	  /* From nfp_ipsec_sa_cipher_mode */
100859a497fSHuanhuan Wang 		uint32_t cipher :4;	  /* From nfp_ipsec_sa_cipher */
101859a497fSHuanhuan Wang 		uint32_t mode   :2;	  /* From nfp_ipsec_sa_mode */
102859a497fSHuanhuan Wang 		uint32_t proto  :2;	  /* From nfp_ipsec_sa_prot */
103859a497fSHuanhuan Wang 		uint32_t dir :1;	  /* SA direction */
104859a497fSHuanhuan Wang 		uint32_t resv0 :12;
105859a497fSHuanhuan Wang 		uint32_t encap_dsbl:1;	  /* Encap/Decap disable */
106859a497fSHuanhuan Wang 		uint32_t resv1 :2;	  /* Must be set to 0 */
107859a497fSHuanhuan Wang 	} ctrl_word;
108859a497fSHuanhuan Wang 	u32 spi;			  /* SPI Value */
109859a497fSHuanhuan Wang 	uint32_t pmtu_limit :16;          /* PMTU Limit */
110859a497fSHuanhuan Wang 	uint32_t resv0 :5;
111859a497fSHuanhuan Wang 	uint32_t ipv6       :1;		  /* Outbound IPv6 addr format */
112859a497fSHuanhuan Wang 	uint32_t resv1	 :10;
113859a497fSHuanhuan Wang 	u32 resv2[2];
114859a497fSHuanhuan Wang 	u32 src_ip[4];			  /* Src IP addr */
115859a497fSHuanhuan Wang 	u32 dst_ip[4];			  /* Dst IP addr */
116859a497fSHuanhuan Wang 	u32 resv3[6];
117859a497fSHuanhuan Wang };
118859a497fSHuanhuan Wang 
119859a497fSHuanhuan Wang /* IPSEC_CFG_MSSG */
120859a497fSHuanhuan Wang struct nfp_ipsec_cfg_mssg {
121859a497fSHuanhuan Wang 	union {
122859a497fSHuanhuan Wang 		struct{
123859a497fSHuanhuan Wang 			uint32_t cmd:16;     /* One of nfp_ipsec_cfg_mssg_cmd_codes */
124859a497fSHuanhuan Wang 			uint32_t rsp:16;     /* One of nfp_ipsec_cfg_mssg_rsp_codes */
125859a497fSHuanhuan Wang 			uint32_t sa_idx:16;  /* SA table index */
126859a497fSHuanhuan Wang 			uint32_t spare0:16;
127859a497fSHuanhuan Wang 			struct nfp_ipsec_cfg_add_sa cfg_add_sa;
128859a497fSHuanhuan Wang 		};
129859a497fSHuanhuan Wang 		u32 raw[64];
130859a497fSHuanhuan Wang 	};
131859a497fSHuanhuan Wang };
132859a497fSHuanhuan Wang 
nfp_net_ipsec_cfg(struct nfp_net * nn,struct nfp_mbox_amsg_entry * entry)13371f814cdSYinjun Zhang static int nfp_net_ipsec_cfg(struct nfp_net *nn, struct nfp_mbox_amsg_entry *entry)
134859a497fSHuanhuan Wang {
1357a13a2eeSYinjun Zhang 	unsigned int offset = nn->tlv_caps.mbox_off + NFP_NET_CFG_MBOX_SIMPLE_VAL;
13671f814cdSYinjun Zhang 	struct nfp_ipsec_cfg_mssg *msg = (struct nfp_ipsec_cfg_mssg *)entry->msg;
137859a497fSHuanhuan Wang 	int i, msg_size, ret;
138859a497fSHuanhuan Wang 
1397a13a2eeSYinjun Zhang 	ret = nfp_net_mbox_lock(nn, sizeof(*msg));
1407a13a2eeSYinjun Zhang 	if (ret)
141859a497fSHuanhuan Wang 		return ret;
142859a497fSHuanhuan Wang 
143859a497fSHuanhuan Wang 	msg_size = ARRAY_SIZE(msg->raw);
144859a497fSHuanhuan Wang 	for (i = 0; i < msg_size; i++)
1457a13a2eeSYinjun Zhang 		nn_writel(nn, offset + 4 * i, msg->raw[i]);
146859a497fSHuanhuan Wang 
14771f814cdSYinjun Zhang 	ret = nfp_net_mbox_reconfig(nn, entry->cmd);
1487a13a2eeSYinjun Zhang 	if (ret < 0) {
1497a13a2eeSYinjun Zhang 		nn_ctrl_bar_unlock(nn);
150859a497fSHuanhuan Wang 		return ret;
1517a13a2eeSYinjun Zhang 	}
152859a497fSHuanhuan Wang 
153859a497fSHuanhuan Wang 	/* For now we always read the whole message response back */
154859a497fSHuanhuan Wang 	for (i = 0; i < msg_size; i++)
1557a13a2eeSYinjun Zhang 		msg->raw[i] = nn_readl(nn, offset + 4 * i);
1567a13a2eeSYinjun Zhang 
1577a13a2eeSYinjun Zhang 	nn_ctrl_bar_unlock(nn);
158859a497fSHuanhuan Wang 
159859a497fSHuanhuan Wang 	switch (msg->rsp) {
160859a497fSHuanhuan Wang 	case NFP_IPSEC_CFG_MSSG_OK:
161859a497fSHuanhuan Wang 		return 0;
162859a497fSHuanhuan Wang 	case NFP_IPSEC_CFG_MSSG_SA_INVALID_CMD:
163859a497fSHuanhuan Wang 		return -EINVAL;
164859a497fSHuanhuan Wang 	case NFP_IPSEC_CFG_MSSG_SA_VALID:
165859a497fSHuanhuan Wang 		return -EEXIST;
166859a497fSHuanhuan Wang 	case NFP_IPSEC_CFG_MSSG_FAILED:
167859a497fSHuanhuan Wang 	case NFP_IPSEC_CFG_MSSG_SA_HASH_ADD_FAILED:
168859a497fSHuanhuan Wang 	case NFP_IPSEC_CFG_MSSG_SA_HASH_DEL_FAILED:
169859a497fSHuanhuan Wang 		return -EIO;
170859a497fSHuanhuan Wang 	default:
171859a497fSHuanhuan Wang 		return -EINVAL;
172859a497fSHuanhuan Wang 	}
173859a497fSHuanhuan Wang }
174859a497fSHuanhuan Wang 
set_aes_keylen(struct nfp_ipsec_cfg_add_sa * cfg,int alg,int keylen)175859a497fSHuanhuan Wang static int set_aes_keylen(struct nfp_ipsec_cfg_add_sa *cfg, int alg, int keylen)
176859a497fSHuanhuan Wang {
177859a497fSHuanhuan Wang 	bool aes_gmac = (alg == SADB_X_EALG_NULL_AES_GMAC);
178859a497fSHuanhuan Wang 
179859a497fSHuanhuan Wang 	switch (keylen) {
180859a497fSHuanhuan Wang 	case 128:
181859a497fSHuanhuan Wang 		cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES128_NULL :
182859a497fSHuanhuan Wang 						   NFP_IPSEC_CIPHER_AES128;
183859a497fSHuanhuan Wang 		break;
184859a497fSHuanhuan Wang 	case 192:
185859a497fSHuanhuan Wang 		cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES192_NULL :
186859a497fSHuanhuan Wang 						   NFP_IPSEC_CIPHER_AES192;
187859a497fSHuanhuan Wang 		break;
188859a497fSHuanhuan Wang 	case 256:
189859a497fSHuanhuan Wang 		cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES256_NULL :
190859a497fSHuanhuan Wang 						   NFP_IPSEC_CIPHER_AES256;
191859a497fSHuanhuan Wang 		break;
192859a497fSHuanhuan Wang 	default:
193859a497fSHuanhuan Wang 		return -EINVAL;
194859a497fSHuanhuan Wang 	}
195859a497fSHuanhuan Wang 
196859a497fSHuanhuan Wang 	return 0;
197859a497fSHuanhuan Wang }
198859a497fSHuanhuan Wang 
set_md5hmac(struct nfp_ipsec_cfg_add_sa * cfg,int * trunc_len)199859a497fSHuanhuan Wang static void set_md5hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
200859a497fSHuanhuan Wang {
201859a497fSHuanhuan Wang 	switch (*trunc_len) {
202859a497fSHuanhuan Wang 	case 96:
203859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_MD5_96;
204859a497fSHuanhuan Wang 		break;
205859a497fSHuanhuan Wang 	case 128:
206859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_MD5_128;
207859a497fSHuanhuan Wang 		break;
208859a497fSHuanhuan Wang 	default:
209859a497fSHuanhuan Wang 		*trunc_len = 0;
210859a497fSHuanhuan Wang 	}
211859a497fSHuanhuan Wang }
212859a497fSHuanhuan Wang 
set_sha1hmac(struct nfp_ipsec_cfg_add_sa * cfg,int * trunc_len)213859a497fSHuanhuan Wang static void set_sha1hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
214859a497fSHuanhuan Wang {
215859a497fSHuanhuan Wang 	switch (*trunc_len) {
216859a497fSHuanhuan Wang 	case 96:
217859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA1_96;
218859a497fSHuanhuan Wang 		break;
219859a497fSHuanhuan Wang 	case 80:
220859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA1_80;
221859a497fSHuanhuan Wang 		break;
222859a497fSHuanhuan Wang 	default:
223859a497fSHuanhuan Wang 		*trunc_len = 0;
224859a497fSHuanhuan Wang 	}
225859a497fSHuanhuan Wang }
226859a497fSHuanhuan Wang 
set_sha2_256hmac(struct nfp_ipsec_cfg_add_sa * cfg,int * trunc_len)227859a497fSHuanhuan Wang static void set_sha2_256hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
228859a497fSHuanhuan Wang {
229859a497fSHuanhuan Wang 	switch (*trunc_len) {
230859a497fSHuanhuan Wang 	case 96:
231859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA256_96;
232859a497fSHuanhuan Wang 		break;
233859a497fSHuanhuan Wang 	case 128:
234859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA256_128;
235859a497fSHuanhuan Wang 		break;
236859a497fSHuanhuan Wang 	default:
237859a497fSHuanhuan Wang 		*trunc_len = 0;
238859a497fSHuanhuan Wang 	}
239859a497fSHuanhuan Wang }
240859a497fSHuanhuan Wang 
set_sha2_384hmac(struct nfp_ipsec_cfg_add_sa * cfg,int * trunc_len)241859a497fSHuanhuan Wang static void set_sha2_384hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
242859a497fSHuanhuan Wang {
243859a497fSHuanhuan Wang 	switch (*trunc_len) {
244859a497fSHuanhuan Wang 	case 96:
245859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA384_96;
246859a497fSHuanhuan Wang 		break;
247859a497fSHuanhuan Wang 	case 192:
248859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA384_192;
249859a497fSHuanhuan Wang 		break;
250859a497fSHuanhuan Wang 	default:
251859a497fSHuanhuan Wang 		*trunc_len = 0;
252859a497fSHuanhuan Wang 	}
253859a497fSHuanhuan Wang }
254859a497fSHuanhuan Wang 
set_sha2_512hmac(struct nfp_ipsec_cfg_add_sa * cfg,int * trunc_len)255859a497fSHuanhuan Wang static void set_sha2_512hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
256859a497fSHuanhuan Wang {
257859a497fSHuanhuan Wang 	switch (*trunc_len) {
258859a497fSHuanhuan Wang 	case 96:
259859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA512_96;
260859a497fSHuanhuan Wang 		break;
261859a497fSHuanhuan Wang 	case 256:
262859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA512_256;
263859a497fSHuanhuan Wang 		break;
264859a497fSHuanhuan Wang 	default:
265859a497fSHuanhuan Wang 		*trunc_len = 0;
266859a497fSHuanhuan Wang 	}
267859a497fSHuanhuan Wang }
268859a497fSHuanhuan Wang 
nfp_net_xfrm_add_state(struct xfrm_state * x,struct netlink_ext_ack * extack)2697681a4f5SLeon Romanovsky static int nfp_net_xfrm_add_state(struct xfrm_state *x,
2707681a4f5SLeon Romanovsky 				  struct netlink_ext_ack *extack)
27157f273adSHuanhuan Wang {
272*63cfd210SHuanhuan Wang 	struct net_device *netdev = x->xso.real_dev;
273859a497fSHuanhuan Wang 	struct nfp_ipsec_cfg_mssg msg = {};
274859a497fSHuanhuan Wang 	int i, key_len, trunc_len, err = 0;
275859a497fSHuanhuan Wang 	struct nfp_ipsec_cfg_add_sa *cfg;
276859a497fSHuanhuan Wang 	struct nfp_net *nn;
277859a497fSHuanhuan Wang 	unsigned int saidx;
278859a497fSHuanhuan Wang 
279859a497fSHuanhuan Wang 	nn = netdev_priv(netdev);
280859a497fSHuanhuan Wang 	cfg = &msg.cfg_add_sa;
281859a497fSHuanhuan Wang 
282859a497fSHuanhuan Wang 	/* General */
283859a497fSHuanhuan Wang 	switch (x->props.mode) {
284859a497fSHuanhuan Wang 	case XFRM_MODE_TUNNEL:
285859a497fSHuanhuan Wang 		cfg->ctrl_word.mode = NFP_IPSEC_PROTMODE_TUNNEL;
286859a497fSHuanhuan Wang 		break;
287859a497fSHuanhuan Wang 	case XFRM_MODE_TRANSPORT:
288859a497fSHuanhuan Wang 		cfg->ctrl_word.mode = NFP_IPSEC_PROTMODE_TRANSPORT;
289859a497fSHuanhuan Wang 		break;
290859a497fSHuanhuan Wang 	default:
29105ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported mode for xfrm offload");
292859a497fSHuanhuan Wang 		return -EINVAL;
293859a497fSHuanhuan Wang 	}
294859a497fSHuanhuan Wang 
295859a497fSHuanhuan Wang 	switch (x->id.proto) {
296859a497fSHuanhuan Wang 	case IPPROTO_ESP:
297859a497fSHuanhuan Wang 		cfg->ctrl_word.proto = NFP_IPSEC_PROTOCOL_ESP;
298859a497fSHuanhuan Wang 		break;
299859a497fSHuanhuan Wang 	case IPPROTO_AH:
300859a497fSHuanhuan Wang 		cfg->ctrl_word.proto = NFP_IPSEC_PROTOCOL_AH;
301859a497fSHuanhuan Wang 		break;
302859a497fSHuanhuan Wang 	default:
30305ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol for xfrm offload");
304859a497fSHuanhuan Wang 		return -EINVAL;
305859a497fSHuanhuan Wang 	}
306859a497fSHuanhuan Wang 
307859a497fSHuanhuan Wang 	if (x->props.flags & XFRM_STATE_ESN) {
30805ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported XFRM_REPLAY_MODE_ESN for xfrm offload");
309859a497fSHuanhuan Wang 		return -EINVAL;
310859a497fSHuanhuan Wang 	}
311859a497fSHuanhuan Wang 
31262f6eca5SLeon Romanovsky 	if (x->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) {
31305ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported xfrm offload type");
31462f6eca5SLeon Romanovsky 		return -EINVAL;
31562f6eca5SLeon Romanovsky 	}
31662f6eca5SLeon Romanovsky 
317859a497fSHuanhuan Wang 	cfg->spi = ntohl(x->id.spi);
318859a497fSHuanhuan Wang 
319859a497fSHuanhuan Wang 	/* Hash/Authentication */
320859a497fSHuanhuan Wang 	if (x->aalg)
321859a497fSHuanhuan Wang 		trunc_len = x->aalg->alg_trunc_len;
322859a497fSHuanhuan Wang 	else
323859a497fSHuanhuan Wang 		trunc_len = 0;
324859a497fSHuanhuan Wang 
325859a497fSHuanhuan Wang 	switch (x->props.aalgo) {
326859a497fSHuanhuan Wang 	case SADB_AALG_NONE:
327859a497fSHuanhuan Wang 		if (x->aead) {
328859a497fSHuanhuan Wang 			trunc_len = -1;
329859a497fSHuanhuan Wang 		} else {
33005ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
331859a497fSHuanhuan Wang 			return -EINVAL;
332859a497fSHuanhuan Wang 		}
333859a497fSHuanhuan Wang 		break;
334859a497fSHuanhuan Wang 	case SADB_X_AALG_NULL:
335859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_NONE;
336859a497fSHuanhuan Wang 		trunc_len = -1;
337859a497fSHuanhuan Wang 		break;
338859a497fSHuanhuan Wang 	case SADB_AALG_MD5HMAC:
339436396f2SHuanhuan Wang 		if (nn->pdev->device == PCI_DEVICE_ID_NFP3800) {
340436396f2SHuanhuan Wang 			NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
341436396f2SHuanhuan Wang 			return -EINVAL;
342436396f2SHuanhuan Wang 		}
343859a497fSHuanhuan Wang 		set_md5hmac(cfg, &trunc_len);
344859a497fSHuanhuan Wang 		break;
345859a497fSHuanhuan Wang 	case SADB_AALG_SHA1HMAC:
346859a497fSHuanhuan Wang 		set_sha1hmac(cfg, &trunc_len);
347859a497fSHuanhuan Wang 		break;
348859a497fSHuanhuan Wang 	case SADB_X_AALG_SHA2_256HMAC:
349859a497fSHuanhuan Wang 		set_sha2_256hmac(cfg, &trunc_len);
350859a497fSHuanhuan Wang 		break;
351859a497fSHuanhuan Wang 	case SADB_X_AALG_SHA2_384HMAC:
352859a497fSHuanhuan Wang 		set_sha2_384hmac(cfg, &trunc_len);
353859a497fSHuanhuan Wang 		break;
354859a497fSHuanhuan Wang 	case SADB_X_AALG_SHA2_512HMAC:
355859a497fSHuanhuan Wang 		set_sha2_512hmac(cfg, &trunc_len);
356859a497fSHuanhuan Wang 		break;
357859a497fSHuanhuan Wang 	default:
35805ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
359859a497fSHuanhuan Wang 		return -EINVAL;
360859a497fSHuanhuan Wang 	}
361859a497fSHuanhuan Wang 
362859a497fSHuanhuan Wang 	if (!trunc_len) {
36305ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm trunc length");
364859a497fSHuanhuan Wang 		return -EINVAL;
365859a497fSHuanhuan Wang 	}
366859a497fSHuanhuan Wang 
367859a497fSHuanhuan Wang 	if (x->aalg) {
368859a497fSHuanhuan Wang 		key_len = DIV_ROUND_UP(x->aalg->alg_key_len, BITS_PER_BYTE);
369859a497fSHuanhuan Wang 		if (key_len > sizeof(cfg->auth_key)) {
37005ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "Insufficient space for offloaded auth key");
371859a497fSHuanhuan Wang 			return -EINVAL;
372859a497fSHuanhuan Wang 		}
373859a497fSHuanhuan Wang 		for (i = 0; i < key_len / sizeof(cfg->auth_key[0]) ; i++)
374859a497fSHuanhuan Wang 			cfg->auth_key[i] = get_unaligned_be32(x->aalg->alg_key +
375859a497fSHuanhuan Wang 							      sizeof(cfg->auth_key[0]) * i);
376859a497fSHuanhuan Wang 	}
377859a497fSHuanhuan Wang 
378859a497fSHuanhuan Wang 	/* Encryption */
379859a497fSHuanhuan Wang 	switch (x->props.ealgo) {
380859a497fSHuanhuan Wang 	case SADB_EALG_NONE:
381859a497fSHuanhuan Wang 	case SADB_EALG_NULL:
382859a497fSHuanhuan Wang 		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
383859a497fSHuanhuan Wang 		cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_NULL;
384859a497fSHuanhuan Wang 		break;
385859a497fSHuanhuan Wang 	case SADB_EALG_3DESCBC:
386436396f2SHuanhuan Wang 		if (nn->pdev->device == PCI_DEVICE_ID_NFP3800) {
387436396f2SHuanhuan Wang 			NL_SET_ERR_MSG_MOD(extack, "Unsupported encryption algorithm for offload");
388436396f2SHuanhuan Wang 			return -EINVAL;
389436396f2SHuanhuan Wang 		}
390859a497fSHuanhuan Wang 		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
391859a497fSHuanhuan Wang 		cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_3DES;
392859a497fSHuanhuan Wang 		break;
393859a497fSHuanhuan Wang 	case SADB_X_EALG_AES_GCM_ICV16:
394859a497fSHuanhuan Wang 	case SADB_X_EALG_NULL_AES_GMAC:
395859a497fSHuanhuan Wang 		if (!x->aead) {
39605ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "Invalid AES key data");
397859a497fSHuanhuan Wang 			return -EINVAL;
398859a497fSHuanhuan Wang 		}
399859a497fSHuanhuan Wang 
400859a497fSHuanhuan Wang 		if (x->aead->alg_icv_len != 128) {
40105ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "ICV must be 128bit with SADB_X_EALG_AES_GCM_ICV16");
402859a497fSHuanhuan Wang 			return -EINVAL;
403859a497fSHuanhuan Wang 		}
404859a497fSHuanhuan Wang 		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CTR;
405859a497fSHuanhuan Wang 		cfg->ctrl_word.hash = NFP_IPSEC_HASH_GF128_128;
406859a497fSHuanhuan Wang 
407859a497fSHuanhuan Wang 		/* Aead->alg_key_len includes 32-bit salt */
408859a497fSHuanhuan Wang 		if (set_aes_keylen(cfg, x->props.ealgo, x->aead->alg_key_len - 32)) {
40905ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "Unsupported AES key length");
410859a497fSHuanhuan Wang 			return -EINVAL;
411859a497fSHuanhuan Wang 		}
412859a497fSHuanhuan Wang 		break;
413859a497fSHuanhuan Wang 	case SADB_X_EALG_AESCBC:
414859a497fSHuanhuan Wang 		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
415859a497fSHuanhuan Wang 		if (!x->ealg) {
41605ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "Invalid AES key data");
417859a497fSHuanhuan Wang 			return -EINVAL;
418859a497fSHuanhuan Wang 		}
419859a497fSHuanhuan Wang 		if (set_aes_keylen(cfg, x->props.ealgo, x->ealg->alg_key_len) < 0) {
42005ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "Unsupported AES key length");
421859a497fSHuanhuan Wang 			return -EINVAL;
422859a497fSHuanhuan Wang 		}
423859a497fSHuanhuan Wang 		break;
424859a497fSHuanhuan Wang 	default:
42505ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported encryption algorithm for offload");
426859a497fSHuanhuan Wang 		return -EINVAL;
427859a497fSHuanhuan Wang 	}
428859a497fSHuanhuan Wang 
429859a497fSHuanhuan Wang 	if (x->aead) {
430859a497fSHuanhuan Wang 		int salt_len = 4;
431859a497fSHuanhuan Wang 
432859a497fSHuanhuan Wang 		key_len = DIV_ROUND_UP(x->aead->alg_key_len, BITS_PER_BYTE);
433859a497fSHuanhuan Wang 		key_len -= salt_len;
434859a497fSHuanhuan Wang 
435859a497fSHuanhuan Wang 		if (key_len > sizeof(cfg->ciph_key)) {
43605ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "aead: Insufficient space for offloaded key");
437859a497fSHuanhuan Wang 			return -EINVAL;
438859a497fSHuanhuan Wang 		}
439859a497fSHuanhuan Wang 
440859a497fSHuanhuan Wang 		for (i = 0; i < key_len / sizeof(cfg->ciph_key[0]) ; i++)
441859a497fSHuanhuan Wang 			cfg->ciph_key[i] = get_unaligned_be32(x->aead->alg_key +
442859a497fSHuanhuan Wang 							      sizeof(cfg->ciph_key[0]) * i);
443859a497fSHuanhuan Wang 
444859a497fSHuanhuan Wang 		/* Load up the salt */
445859a497fSHuanhuan Wang 		cfg->aesgcm_fields.salt = get_unaligned_be32(x->aead->alg_key + key_len);
446859a497fSHuanhuan Wang 	}
447859a497fSHuanhuan Wang 
448859a497fSHuanhuan Wang 	if (x->ealg) {
449859a497fSHuanhuan Wang 		key_len = DIV_ROUND_UP(x->ealg->alg_key_len, BITS_PER_BYTE);
450859a497fSHuanhuan Wang 
451859a497fSHuanhuan Wang 		if (key_len > sizeof(cfg->ciph_key)) {
45205ddf5f8SLeon Romanovsky 			NL_SET_ERR_MSG_MOD(extack, "ealg: Insufficient space for offloaded key");
453859a497fSHuanhuan Wang 			return -EINVAL;
454859a497fSHuanhuan Wang 		}
455859a497fSHuanhuan Wang 		for (i = 0; i < key_len / sizeof(cfg->ciph_key[0]) ; i++)
456859a497fSHuanhuan Wang 			cfg->ciph_key[i] = get_unaligned_be32(x->ealg->alg_key +
457859a497fSHuanhuan Wang 							      sizeof(cfg->ciph_key[0]) * i);
458859a497fSHuanhuan Wang 	}
459859a497fSHuanhuan Wang 
460859a497fSHuanhuan Wang 	/* IP related info */
461859a497fSHuanhuan Wang 	switch (x->props.family) {
462859a497fSHuanhuan Wang 	case AF_INET:
463859a497fSHuanhuan Wang 		cfg->ipv6 = 0;
464859a497fSHuanhuan Wang 		cfg->src_ip[0] = ntohl(x->props.saddr.a4);
465859a497fSHuanhuan Wang 		cfg->dst_ip[0] = ntohl(x->id.daddr.a4);
466859a497fSHuanhuan Wang 		break;
467859a497fSHuanhuan Wang 	case AF_INET6:
468859a497fSHuanhuan Wang 		cfg->ipv6 = 1;
469859a497fSHuanhuan Wang 		for (i = 0; i < 4; i++) {
470859a497fSHuanhuan Wang 			cfg->src_ip[i] = ntohl(x->props.saddr.a6[i]);
471859a497fSHuanhuan Wang 			cfg->dst_ip[i] = ntohl(x->id.daddr.a6[i]);
472859a497fSHuanhuan Wang 		}
473859a497fSHuanhuan Wang 		break;
474859a497fSHuanhuan Wang 	default:
47505ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unsupported address family");
476859a497fSHuanhuan Wang 		return -EINVAL;
477859a497fSHuanhuan Wang 	}
478859a497fSHuanhuan Wang 
479859a497fSHuanhuan Wang 	/* Maximum nic IPsec code could handle. Other limits may apply. */
480859a497fSHuanhuan Wang 	cfg->pmtu_limit = 0xffff;
481859a497fSHuanhuan Wang 	cfg->ctrl_word.encap_dsbl = 1;
482859a497fSHuanhuan Wang 
483859a497fSHuanhuan Wang 	/* SA direction */
484859a497fSHuanhuan Wang 	cfg->ctrl_word.dir = x->xso.dir;
485859a497fSHuanhuan Wang 
486859a497fSHuanhuan Wang 	/* Find unused SA data*/
487859a497fSHuanhuan Wang 	err = xa_alloc(&nn->xa_ipsec, &saidx, x,
488859a497fSHuanhuan Wang 		       XA_LIMIT(0, NFP_NET_IPSEC_MAX_SA_CNT - 1), GFP_KERNEL);
489859a497fSHuanhuan Wang 	if (err < 0) {
49005ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Unable to get sa_data number for IPsec");
491859a497fSHuanhuan Wang 		return err;
492859a497fSHuanhuan Wang 	}
493859a497fSHuanhuan Wang 
494859a497fSHuanhuan Wang 	/* Allocate saidx and commit the SA */
49571f814cdSYinjun Zhang 	msg.cmd = NFP_IPSEC_CFG_MSSG_ADD_SA;
49671f814cdSYinjun Zhang 	msg.sa_idx = saidx;
49771f814cdSYinjun Zhang 	err = nfp_net_sched_mbox_amsg_work(nn, NFP_NET_CFG_MBOX_CMD_IPSEC, &msg,
49871f814cdSYinjun Zhang 					   sizeof(msg), nfp_net_ipsec_cfg);
499859a497fSHuanhuan Wang 	if (err) {
500859a497fSHuanhuan Wang 		xa_erase(&nn->xa_ipsec, saidx);
50105ddf5f8SLeon Romanovsky 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue IPsec command");
502859a497fSHuanhuan Wang 		return err;
503859a497fSHuanhuan Wang 	}
504859a497fSHuanhuan Wang 
505859a497fSHuanhuan Wang 	/* 0 is invalid offload_handle for kernel */
506859a497fSHuanhuan Wang 	x->xso.offload_handle = saidx + 1;
507859a497fSHuanhuan Wang 	return 0;
50857f273adSHuanhuan Wang }
50957f273adSHuanhuan Wang 
nfp_net_xfrm_del_state(struct xfrm_state * x)51057f273adSHuanhuan Wang static void nfp_net_xfrm_del_state(struct xfrm_state *x)
51157f273adSHuanhuan Wang {
51271f814cdSYinjun Zhang 	struct nfp_ipsec_cfg_mssg msg = {
51371f814cdSYinjun Zhang 		.cmd = NFP_IPSEC_CFG_MSSG_INV_SA,
51471f814cdSYinjun Zhang 		.sa_idx = x->xso.offload_handle - 1,
51571f814cdSYinjun Zhang 	};
516*63cfd210SHuanhuan Wang 	struct net_device *netdev = x->xso.real_dev;
517859a497fSHuanhuan Wang 	struct nfp_net *nn;
518859a497fSHuanhuan Wang 	int err;
519859a497fSHuanhuan Wang 
520859a497fSHuanhuan Wang 	nn = netdev_priv(netdev);
52171f814cdSYinjun Zhang 	err = nfp_net_sched_mbox_amsg_work(nn, NFP_NET_CFG_MBOX_CMD_IPSEC, &msg,
52271f814cdSYinjun Zhang 					   sizeof(msg), nfp_net_ipsec_cfg);
523859a497fSHuanhuan Wang 	if (err)
524859a497fSHuanhuan Wang 		nn_warn(nn, "Failed to invalidate SA in hardware\n");
525859a497fSHuanhuan Wang 
526859a497fSHuanhuan Wang 	xa_erase(&nn->xa_ipsec, x->xso.offload_handle - 1);
52757f273adSHuanhuan Wang }
52857f273adSHuanhuan Wang 
nfp_net_ipsec_offload_ok(struct sk_buff * skb,struct xfrm_state * x)52957f273adSHuanhuan Wang static bool nfp_net_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
53057f273adSHuanhuan Wang {
531859a497fSHuanhuan Wang 	if (x->props.family == AF_INET)
532859a497fSHuanhuan Wang 		/* Offload with IPv4 options is not supported yet */
533859a497fSHuanhuan Wang 		return ip_hdr(skb)->ihl == 5;
534859a497fSHuanhuan Wang 
535859a497fSHuanhuan Wang 	/* Offload with IPv6 extension headers is not support yet */
536859a497fSHuanhuan Wang 	return !(ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr));
53757f273adSHuanhuan Wang }
53857f273adSHuanhuan Wang 
53957f273adSHuanhuan Wang static const struct xfrmdev_ops nfp_net_ipsec_xfrmdev_ops = {
54057f273adSHuanhuan Wang 	.xdo_dev_state_add = nfp_net_xfrm_add_state,
54157f273adSHuanhuan Wang 	.xdo_dev_state_delete = nfp_net_xfrm_del_state,
54257f273adSHuanhuan Wang 	.xdo_dev_offload_ok = nfp_net_ipsec_offload_ok,
54357f273adSHuanhuan Wang };
54457f273adSHuanhuan Wang 
nfp_net_ipsec_init(struct nfp_net * nn)54557f273adSHuanhuan Wang void nfp_net_ipsec_init(struct nfp_net *nn)
54657f273adSHuanhuan Wang {
54757f273adSHuanhuan Wang 	if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC))
54857f273adSHuanhuan Wang 		return;
54957f273adSHuanhuan Wang 
55057f273adSHuanhuan Wang 	xa_init_flags(&nn->xa_ipsec, XA_FLAGS_ALLOC);
55157f273adSHuanhuan Wang 	nn->dp.netdev->xfrmdev_ops = &nfp_net_ipsec_xfrmdev_ops;
55257f273adSHuanhuan Wang }
55357f273adSHuanhuan Wang 
nfp_net_ipsec_clean(struct nfp_net * nn)55457f273adSHuanhuan Wang void nfp_net_ipsec_clean(struct nfp_net *nn)
55557f273adSHuanhuan Wang {
55657f273adSHuanhuan Wang 	if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC))
55757f273adSHuanhuan Wang 		return;
55857f273adSHuanhuan Wang 
55957f273adSHuanhuan Wang 	WARN_ON(!xa_empty(&nn->xa_ipsec));
56057f273adSHuanhuan Wang 	xa_destroy(&nn->xa_ipsec);
56157f273adSHuanhuan Wang }
56257f273adSHuanhuan Wang 
nfp_net_ipsec_tx_prep(struct nfp_net_dp * dp,struct sk_buff * skb,struct nfp_ipsec_offload * offload_info)56357f273adSHuanhuan Wang bool nfp_net_ipsec_tx_prep(struct nfp_net_dp *dp, struct sk_buff *skb,
56457f273adSHuanhuan Wang 			   struct nfp_ipsec_offload *offload_info)
56557f273adSHuanhuan Wang {
56657f273adSHuanhuan Wang 	struct xfrm_offload *xo = xfrm_offload(skb);
56757f273adSHuanhuan Wang 	struct xfrm_state *x;
56857f273adSHuanhuan Wang 
56957f273adSHuanhuan Wang 	x = xfrm_input_state(skb);
57057f273adSHuanhuan Wang 	if (!x)
57157f273adSHuanhuan Wang 		return false;
57257f273adSHuanhuan Wang 
57357f273adSHuanhuan Wang 	offload_info->seq_hi = xo->seq.hi;
57457f273adSHuanhuan Wang 	offload_info->seq_low = xo->seq.low;
57557f273adSHuanhuan Wang 	offload_info->handle = x->xso.offload_handle;
57657f273adSHuanhuan Wang 
57757f273adSHuanhuan Wang 	return true;
57857f273adSHuanhuan Wang }
57957f273adSHuanhuan Wang 
nfp_net_ipsec_rx(struct nfp_meta_parsed * meta,struct sk_buff * skb)58057f273adSHuanhuan Wang int nfp_net_ipsec_rx(struct nfp_meta_parsed *meta, struct sk_buff *skb)
58157f273adSHuanhuan Wang {
58257f273adSHuanhuan Wang 	struct net_device *netdev = skb->dev;
58357f273adSHuanhuan Wang 	struct xfrm_offload *xo;
58457f273adSHuanhuan Wang 	struct xfrm_state *x;
58557f273adSHuanhuan Wang 	struct sec_path *sp;
58657f273adSHuanhuan Wang 	struct nfp_net *nn;
58757f273adSHuanhuan Wang 	u32 saidx;
58857f273adSHuanhuan Wang 
58957f273adSHuanhuan Wang 	nn = netdev_priv(netdev);
59057f273adSHuanhuan Wang 
59157f273adSHuanhuan Wang 	saidx = meta->ipsec_saidx - 1;
59257f273adSHuanhuan Wang 	if (saidx >= NFP_NET_IPSEC_MAX_SA_CNT)
59357f273adSHuanhuan Wang 		return -EINVAL;
59457f273adSHuanhuan Wang 
59557f273adSHuanhuan Wang 	sp = secpath_set(skb);
59657f273adSHuanhuan Wang 	if (unlikely(!sp))
59757f273adSHuanhuan Wang 		return -ENOMEM;
59857f273adSHuanhuan Wang 
59957f273adSHuanhuan Wang 	xa_lock(&nn->xa_ipsec);
60057f273adSHuanhuan Wang 	x = xa_load(&nn->xa_ipsec, saidx);
60157f273adSHuanhuan Wang 	xa_unlock(&nn->xa_ipsec);
60257f273adSHuanhuan Wang 	if (!x)
60357f273adSHuanhuan Wang 		return -EINVAL;
60457f273adSHuanhuan Wang 
60557f273adSHuanhuan Wang 	xfrm_state_hold(x);
60657f273adSHuanhuan Wang 	sp->xvec[sp->len++] = x;
60757f273adSHuanhuan Wang 	sp->olen++;
60857f273adSHuanhuan Wang 	xo = xfrm_offload(skb);
60957f273adSHuanhuan Wang 	xo->flags = CRYPTO_DONE;
61057f273adSHuanhuan Wang 	xo->status = CRYPTO_SUCCESS;
61157f273adSHuanhuan Wang 
61257f273adSHuanhuan Wang 	return 0;
61357f273adSHuanhuan Wang }
614