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