162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2018 Netronome Systems, Inc */
362306a36Sopenharmony_ci/* Copyright (C) 2021 Corigine, Inc */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/netdevice.h>
962306a36Sopenharmony_ci#include <asm/unaligned.h>
1062306a36Sopenharmony_ci#include <linux/ktime.h>
1162306a36Sopenharmony_ci#include <net/xfrm.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "../nfpcore/nfp_dev.h"
1462306a36Sopenharmony_ci#include "../nfp_net_ctrl.h"
1562306a36Sopenharmony_ci#include "../nfp_net.h"
1662306a36Sopenharmony_ci#include "crypto.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define NFP_NET_IPSEC_MAX_SA_CNT  (16 * 1024) /* Firmware support a maximum of 16K SA offload */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* IPsec config message cmd codes */
2162306a36Sopenharmony_cienum nfp_ipsec_cfg_mssg_cmd_codes {
2262306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_ADD_SA,	 /* Add a new SA */
2362306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_INV_SA	 /* Invalidate an existing SA */
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* IPsec config message response codes */
2762306a36Sopenharmony_cienum nfp_ipsec_cfg_mssg_rsp_codes {
2862306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_OK,
2962306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_FAILED,
3062306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_SA_VALID,
3162306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_SA_HASH_ADD_FAILED,
3262306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_SA_HASH_DEL_FAILED,
3362306a36Sopenharmony_ci	NFP_IPSEC_CFG_MSSG_SA_INVALID_CMD
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Protocol */
3762306a36Sopenharmony_cienum nfp_ipsec_sa_prot {
3862306a36Sopenharmony_ci	NFP_IPSEC_PROTOCOL_AH = 0,
3962306a36Sopenharmony_ci	NFP_IPSEC_PROTOCOL_ESP = 1
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Mode */
4362306a36Sopenharmony_cienum nfp_ipsec_sa_mode {
4462306a36Sopenharmony_ci	NFP_IPSEC_PROTMODE_TRANSPORT = 0,
4562306a36Sopenharmony_ci	NFP_IPSEC_PROTMODE_TUNNEL = 1
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Cipher types */
4962306a36Sopenharmony_cienum nfp_ipsec_sa_cipher {
5062306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_NULL,
5162306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_3DES,
5262306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_AES128,
5362306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_AES192,
5462306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_AES256,
5562306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_AES128_NULL,
5662306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_AES192_NULL,
5762306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_AES256_NULL,
5862306a36Sopenharmony_ci	NFP_IPSEC_CIPHER_CHACHA20
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Cipher modes */
6262306a36Sopenharmony_cienum nfp_ipsec_sa_cipher_mode {
6362306a36Sopenharmony_ci	NFP_IPSEC_CIMODE_ECB,
6462306a36Sopenharmony_ci	NFP_IPSEC_CIMODE_CBC,
6562306a36Sopenharmony_ci	NFP_IPSEC_CIMODE_CFB,
6662306a36Sopenharmony_ci	NFP_IPSEC_CIMODE_OFB,
6762306a36Sopenharmony_ci	NFP_IPSEC_CIMODE_CTR
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Hash types */
7162306a36Sopenharmony_cienum nfp_ipsec_sa_hash_type {
7262306a36Sopenharmony_ci	NFP_IPSEC_HASH_NONE,
7362306a36Sopenharmony_ci	NFP_IPSEC_HASH_MD5_96,
7462306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA1_96,
7562306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA256_96,
7662306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA384_96,
7762306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA512_96,
7862306a36Sopenharmony_ci	NFP_IPSEC_HASH_MD5_128,
7962306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA1_80,
8062306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA256_128,
8162306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA384_192,
8262306a36Sopenharmony_ci	NFP_IPSEC_HASH_SHA512_256,
8362306a36Sopenharmony_ci	NFP_IPSEC_HASH_GF128_128,
8462306a36Sopenharmony_ci	NFP_IPSEC_HASH_POLY1305_128
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* IPSEC_CFG_MSSG_ADD_SA */
8862306a36Sopenharmony_cistruct nfp_ipsec_cfg_add_sa {
8962306a36Sopenharmony_ci	u32 ciph_key[8];		  /* Cipher Key */
9062306a36Sopenharmony_ci	union {
9162306a36Sopenharmony_ci		u32 auth_key[16];	  /* Authentication Key */
9262306a36Sopenharmony_ci		struct nfp_ipsec_aesgcm { /* AES-GCM-ESP fields */
9362306a36Sopenharmony_ci			u32 salt;	  /* Initialized with SA */
9462306a36Sopenharmony_ci			u32 resv[15];
9562306a36Sopenharmony_ci		} aesgcm_fields;
9662306a36Sopenharmony_ci	};
9762306a36Sopenharmony_ci	struct sa_ctrl_word {
9862306a36Sopenharmony_ci		uint32_t hash   :4;	  /* From nfp_ipsec_sa_hash_type */
9962306a36Sopenharmony_ci		uint32_t cimode :4;	  /* From nfp_ipsec_sa_cipher_mode */
10062306a36Sopenharmony_ci		uint32_t cipher :4;	  /* From nfp_ipsec_sa_cipher */
10162306a36Sopenharmony_ci		uint32_t mode   :2;	  /* From nfp_ipsec_sa_mode */
10262306a36Sopenharmony_ci		uint32_t proto  :2;	  /* From nfp_ipsec_sa_prot */
10362306a36Sopenharmony_ci		uint32_t dir :1;	  /* SA direction */
10462306a36Sopenharmony_ci		uint32_t resv0 :12;
10562306a36Sopenharmony_ci		uint32_t encap_dsbl:1;	  /* Encap/Decap disable */
10662306a36Sopenharmony_ci		uint32_t resv1 :2;	  /* Must be set to 0 */
10762306a36Sopenharmony_ci	} ctrl_word;
10862306a36Sopenharmony_ci	u32 spi;			  /* SPI Value */
10962306a36Sopenharmony_ci	uint32_t pmtu_limit :16;          /* PMTU Limit */
11062306a36Sopenharmony_ci	uint32_t resv0 :5;
11162306a36Sopenharmony_ci	uint32_t ipv6       :1;		  /* Outbound IPv6 addr format */
11262306a36Sopenharmony_ci	uint32_t resv1	 :10;
11362306a36Sopenharmony_ci	u32 resv2[2];
11462306a36Sopenharmony_ci	u32 src_ip[4];			  /* Src IP addr */
11562306a36Sopenharmony_ci	u32 dst_ip[4];			  /* Dst IP addr */
11662306a36Sopenharmony_ci	u32 resv3[6];
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* IPSEC_CFG_MSSG */
12062306a36Sopenharmony_cistruct nfp_ipsec_cfg_mssg {
12162306a36Sopenharmony_ci	union {
12262306a36Sopenharmony_ci		struct{
12362306a36Sopenharmony_ci			uint32_t cmd:16;     /* One of nfp_ipsec_cfg_mssg_cmd_codes */
12462306a36Sopenharmony_ci			uint32_t rsp:16;     /* One of nfp_ipsec_cfg_mssg_rsp_codes */
12562306a36Sopenharmony_ci			uint32_t sa_idx:16;  /* SA table index */
12662306a36Sopenharmony_ci			uint32_t spare0:16;
12762306a36Sopenharmony_ci			struct nfp_ipsec_cfg_add_sa cfg_add_sa;
12862306a36Sopenharmony_ci		};
12962306a36Sopenharmony_ci		u32 raw[64];
13062306a36Sopenharmony_ci	};
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int nfp_net_ipsec_cfg(struct nfp_net *nn, struct nfp_mbox_amsg_entry *entry)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	unsigned int offset = nn->tlv_caps.mbox_off + NFP_NET_CFG_MBOX_SIMPLE_VAL;
13662306a36Sopenharmony_ci	struct nfp_ipsec_cfg_mssg *msg = (struct nfp_ipsec_cfg_mssg *)entry->msg;
13762306a36Sopenharmony_ci	int i, msg_size, ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ret = nfp_net_mbox_lock(nn, sizeof(*msg));
14062306a36Sopenharmony_ci	if (ret)
14162306a36Sopenharmony_ci		return ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	msg_size = ARRAY_SIZE(msg->raw);
14462306a36Sopenharmony_ci	for (i = 0; i < msg_size; i++)
14562306a36Sopenharmony_ci		nn_writel(nn, offset + 4 * i, msg->raw[i]);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ret = nfp_net_mbox_reconfig(nn, entry->cmd);
14862306a36Sopenharmony_ci	if (ret < 0) {
14962306a36Sopenharmony_ci		nn_ctrl_bar_unlock(nn);
15062306a36Sopenharmony_ci		return ret;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* For now we always read the whole message response back */
15462306a36Sopenharmony_ci	for (i = 0; i < msg_size; i++)
15562306a36Sopenharmony_ci		msg->raw[i] = nn_readl(nn, offset + 4 * i);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	nn_ctrl_bar_unlock(nn);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	switch (msg->rsp) {
16062306a36Sopenharmony_ci	case NFP_IPSEC_CFG_MSSG_OK:
16162306a36Sopenharmony_ci		return 0;
16262306a36Sopenharmony_ci	case NFP_IPSEC_CFG_MSSG_SA_INVALID_CMD:
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	case NFP_IPSEC_CFG_MSSG_SA_VALID:
16562306a36Sopenharmony_ci		return -EEXIST;
16662306a36Sopenharmony_ci	case NFP_IPSEC_CFG_MSSG_FAILED:
16762306a36Sopenharmony_ci	case NFP_IPSEC_CFG_MSSG_SA_HASH_ADD_FAILED:
16862306a36Sopenharmony_ci	case NFP_IPSEC_CFG_MSSG_SA_HASH_DEL_FAILED:
16962306a36Sopenharmony_ci		return -EIO;
17062306a36Sopenharmony_ci	default:
17162306a36Sopenharmony_ci		return -EINVAL;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int set_aes_keylen(struct nfp_ipsec_cfg_add_sa *cfg, int alg, int keylen)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	bool aes_gmac = (alg == SADB_X_EALG_NULL_AES_GMAC);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	switch (keylen) {
18062306a36Sopenharmony_ci	case 128:
18162306a36Sopenharmony_ci		cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES128_NULL :
18262306a36Sopenharmony_ci						   NFP_IPSEC_CIPHER_AES128;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case 192:
18562306a36Sopenharmony_ci		cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES192_NULL :
18662306a36Sopenharmony_ci						   NFP_IPSEC_CIPHER_AES192;
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	case 256:
18962306a36Sopenharmony_ci		cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES256_NULL :
19062306a36Sopenharmony_ci						   NFP_IPSEC_CIPHER_AES256;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		return -EINVAL;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void set_md5hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	switch (*trunc_len) {
20262306a36Sopenharmony_ci	case 96:
20362306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_MD5_96;
20462306a36Sopenharmony_ci		break;
20562306a36Sopenharmony_ci	case 128:
20662306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_MD5_128;
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	default:
20962306a36Sopenharmony_ci		*trunc_len = 0;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void set_sha1hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	switch (*trunc_len) {
21662306a36Sopenharmony_ci	case 96:
21762306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA1_96;
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci	case 80:
22062306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA1_80;
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	default:
22362306a36Sopenharmony_ci		*trunc_len = 0;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void set_sha2_256hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	switch (*trunc_len) {
23062306a36Sopenharmony_ci	case 96:
23162306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA256_96;
23262306a36Sopenharmony_ci		break;
23362306a36Sopenharmony_ci	case 128:
23462306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA256_128;
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci	default:
23762306a36Sopenharmony_ci		*trunc_len = 0;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void set_sha2_384hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	switch (*trunc_len) {
24462306a36Sopenharmony_ci	case 96:
24562306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA384_96;
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci	case 192:
24862306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA384_192;
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	default:
25162306a36Sopenharmony_ci		*trunc_len = 0;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic void set_sha2_512hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	switch (*trunc_len) {
25862306a36Sopenharmony_ci	case 96:
25962306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA512_96;
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci	case 256:
26262306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA512_256;
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	default:
26562306a36Sopenharmony_ci		*trunc_len = 0;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int nfp_net_xfrm_add_state(struct xfrm_state *x,
27062306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct net_device *netdev = x->xso.real_dev;
27362306a36Sopenharmony_ci	struct nfp_ipsec_cfg_mssg msg = {};
27462306a36Sopenharmony_ci	int i, key_len, trunc_len, err = 0;
27562306a36Sopenharmony_ci	struct nfp_ipsec_cfg_add_sa *cfg;
27662306a36Sopenharmony_ci	struct nfp_net *nn;
27762306a36Sopenharmony_ci	unsigned int saidx;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	nn = netdev_priv(netdev);
28062306a36Sopenharmony_ci	cfg = &msg.cfg_add_sa;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* General */
28362306a36Sopenharmony_ci	switch (x->props.mode) {
28462306a36Sopenharmony_ci	case XFRM_MODE_TUNNEL:
28562306a36Sopenharmony_ci		cfg->ctrl_word.mode = NFP_IPSEC_PROTMODE_TUNNEL;
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	case XFRM_MODE_TRANSPORT:
28862306a36Sopenharmony_ci		cfg->ctrl_word.mode = NFP_IPSEC_PROTMODE_TRANSPORT;
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci	default:
29162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported mode for xfrm offload");
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	switch (x->id.proto) {
29662306a36Sopenharmony_ci	case IPPROTO_ESP:
29762306a36Sopenharmony_ci		cfg->ctrl_word.proto = NFP_IPSEC_PROTOCOL_ESP;
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci	case IPPROTO_AH:
30062306a36Sopenharmony_ci		cfg->ctrl_word.proto = NFP_IPSEC_PROTOCOL_AH;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	default:
30362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol for xfrm offload");
30462306a36Sopenharmony_ci		return -EINVAL;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (x->props.flags & XFRM_STATE_ESN) {
30862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported XFRM_REPLAY_MODE_ESN for xfrm offload");
30962306a36Sopenharmony_ci		return -EINVAL;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (x->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) {
31362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported xfrm offload type");
31462306a36Sopenharmony_ci		return -EINVAL;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	cfg->spi = ntohl(x->id.spi);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Hash/Authentication */
32062306a36Sopenharmony_ci	if (x->aalg)
32162306a36Sopenharmony_ci		trunc_len = x->aalg->alg_trunc_len;
32262306a36Sopenharmony_ci	else
32362306a36Sopenharmony_ci		trunc_len = 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	switch (x->props.aalgo) {
32662306a36Sopenharmony_ci	case SADB_AALG_NONE:
32762306a36Sopenharmony_ci		if (x->aead) {
32862306a36Sopenharmony_ci			trunc_len = -1;
32962306a36Sopenharmony_ci		} else {
33062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
33162306a36Sopenharmony_ci			return -EINVAL;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case SADB_X_AALG_NULL:
33562306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_NONE;
33662306a36Sopenharmony_ci		trunc_len = -1;
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	case SADB_AALG_MD5HMAC:
33962306a36Sopenharmony_ci		if (nn->pdev->device == PCI_DEVICE_ID_NFP3800) {
34062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
34162306a36Sopenharmony_ci			return -EINVAL;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci		set_md5hmac(cfg, &trunc_len);
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	case SADB_AALG_SHA1HMAC:
34662306a36Sopenharmony_ci		set_sha1hmac(cfg, &trunc_len);
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci	case SADB_X_AALG_SHA2_256HMAC:
34962306a36Sopenharmony_ci		set_sha2_256hmac(cfg, &trunc_len);
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	case SADB_X_AALG_SHA2_384HMAC:
35262306a36Sopenharmony_ci		set_sha2_384hmac(cfg, &trunc_len);
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	case SADB_X_AALG_SHA2_512HMAC:
35562306a36Sopenharmony_ci		set_sha2_512hmac(cfg, &trunc_len);
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	default:
35862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
35962306a36Sopenharmony_ci		return -EINVAL;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!trunc_len) {
36362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm trunc length");
36462306a36Sopenharmony_ci		return -EINVAL;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (x->aalg) {
36862306a36Sopenharmony_ci		key_len = DIV_ROUND_UP(x->aalg->alg_key_len, BITS_PER_BYTE);
36962306a36Sopenharmony_ci		if (key_len > sizeof(cfg->auth_key)) {
37062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Insufficient space for offloaded auth key");
37162306a36Sopenharmony_ci			return -EINVAL;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci		for (i = 0; i < key_len / sizeof(cfg->auth_key[0]) ; i++)
37462306a36Sopenharmony_ci			cfg->auth_key[i] = get_unaligned_be32(x->aalg->alg_key +
37562306a36Sopenharmony_ci							      sizeof(cfg->auth_key[0]) * i);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* Encryption */
37962306a36Sopenharmony_ci	switch (x->props.ealgo) {
38062306a36Sopenharmony_ci	case SADB_EALG_NONE:
38162306a36Sopenharmony_ci	case SADB_EALG_NULL:
38262306a36Sopenharmony_ci		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
38362306a36Sopenharmony_ci		cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_NULL;
38462306a36Sopenharmony_ci		break;
38562306a36Sopenharmony_ci	case SADB_EALG_3DESCBC:
38662306a36Sopenharmony_ci		if (nn->pdev->device == PCI_DEVICE_ID_NFP3800) {
38762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported encryption algorithm for offload");
38862306a36Sopenharmony_ci			return -EINVAL;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
39162306a36Sopenharmony_ci		cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_3DES;
39262306a36Sopenharmony_ci		break;
39362306a36Sopenharmony_ci	case SADB_X_EALG_AES_GCM_ICV16:
39462306a36Sopenharmony_ci	case SADB_X_EALG_NULL_AES_GMAC:
39562306a36Sopenharmony_ci		if (!x->aead) {
39662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid AES key data");
39762306a36Sopenharmony_ci			return -EINVAL;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		if (x->aead->alg_icv_len != 128) {
40162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "ICV must be 128bit with SADB_X_EALG_AES_GCM_ICV16");
40262306a36Sopenharmony_ci			return -EINVAL;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CTR;
40562306a36Sopenharmony_ci		cfg->ctrl_word.hash = NFP_IPSEC_HASH_GF128_128;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		/* Aead->alg_key_len includes 32-bit salt */
40862306a36Sopenharmony_ci		if (set_aes_keylen(cfg, x->props.ealgo, x->aead->alg_key_len - 32)) {
40962306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported AES key length");
41062306a36Sopenharmony_ci			return -EINVAL;
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci		break;
41362306a36Sopenharmony_ci	case SADB_X_EALG_AESCBC:
41462306a36Sopenharmony_ci		cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
41562306a36Sopenharmony_ci		if (!x->ealg) {
41662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid AES key data");
41762306a36Sopenharmony_ci			return -EINVAL;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci		if (set_aes_keylen(cfg, x->props.ealgo, x->ealg->alg_key_len) < 0) {
42062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported AES key length");
42162306a36Sopenharmony_ci			return -EINVAL;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	default:
42562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported encryption algorithm for offload");
42662306a36Sopenharmony_ci		return -EINVAL;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (x->aead) {
43062306a36Sopenharmony_ci		int salt_len = 4;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		key_len = DIV_ROUND_UP(x->aead->alg_key_len, BITS_PER_BYTE);
43362306a36Sopenharmony_ci		key_len -= salt_len;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		if (key_len > sizeof(cfg->ciph_key)) {
43662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "aead: Insufficient space for offloaded key");
43762306a36Sopenharmony_ci			return -EINVAL;
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		for (i = 0; i < key_len / sizeof(cfg->ciph_key[0]) ; i++)
44162306a36Sopenharmony_ci			cfg->ciph_key[i] = get_unaligned_be32(x->aead->alg_key +
44262306a36Sopenharmony_ci							      sizeof(cfg->ciph_key[0]) * i);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		/* Load up the salt */
44562306a36Sopenharmony_ci		cfg->aesgcm_fields.salt = get_unaligned_be32(x->aead->alg_key + key_len);
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (x->ealg) {
44962306a36Sopenharmony_ci		key_len = DIV_ROUND_UP(x->ealg->alg_key_len, BITS_PER_BYTE);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		if (key_len > sizeof(cfg->ciph_key)) {
45262306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "ealg: Insufficient space for offloaded key");
45362306a36Sopenharmony_ci			return -EINVAL;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci		for (i = 0; i < key_len / sizeof(cfg->ciph_key[0]) ; i++)
45662306a36Sopenharmony_ci			cfg->ciph_key[i] = get_unaligned_be32(x->ealg->alg_key +
45762306a36Sopenharmony_ci							      sizeof(cfg->ciph_key[0]) * i);
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* IP related info */
46162306a36Sopenharmony_ci	switch (x->props.family) {
46262306a36Sopenharmony_ci	case AF_INET:
46362306a36Sopenharmony_ci		cfg->ipv6 = 0;
46462306a36Sopenharmony_ci		cfg->src_ip[0] = ntohl(x->props.saddr.a4);
46562306a36Sopenharmony_ci		cfg->dst_ip[0] = ntohl(x->id.daddr.a4);
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci	case AF_INET6:
46862306a36Sopenharmony_ci		cfg->ipv6 = 1;
46962306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
47062306a36Sopenharmony_ci			cfg->src_ip[i] = ntohl(x->props.saddr.a6[i]);
47162306a36Sopenharmony_ci			cfg->dst_ip[i] = ntohl(x->id.daddr.a6[i]);
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	default:
47562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported address family");
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Maximum nic IPsec code could handle. Other limits may apply. */
48062306a36Sopenharmony_ci	cfg->pmtu_limit = 0xffff;
48162306a36Sopenharmony_ci	cfg->ctrl_word.encap_dsbl = 1;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* SA direction */
48462306a36Sopenharmony_ci	cfg->ctrl_word.dir = x->xso.dir;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* Find unused SA data*/
48762306a36Sopenharmony_ci	err = xa_alloc(&nn->xa_ipsec, &saidx, x,
48862306a36Sopenharmony_ci		       XA_LIMIT(0, NFP_NET_IPSEC_MAX_SA_CNT - 1), GFP_KERNEL);
48962306a36Sopenharmony_ci	if (err < 0) {
49062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unable to get sa_data number for IPsec");
49162306a36Sopenharmony_ci		return err;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* Allocate saidx and commit the SA */
49562306a36Sopenharmony_ci	msg.cmd = NFP_IPSEC_CFG_MSSG_ADD_SA;
49662306a36Sopenharmony_ci	msg.sa_idx = saidx;
49762306a36Sopenharmony_ci	err = nfp_net_sched_mbox_amsg_work(nn, NFP_NET_CFG_MBOX_CMD_IPSEC, &msg,
49862306a36Sopenharmony_ci					   sizeof(msg), nfp_net_ipsec_cfg);
49962306a36Sopenharmony_ci	if (err) {
50062306a36Sopenharmony_ci		xa_erase(&nn->xa_ipsec, saidx);
50162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to issue IPsec command");
50262306a36Sopenharmony_ci		return err;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* 0 is invalid offload_handle for kernel */
50662306a36Sopenharmony_ci	x->xso.offload_handle = saidx + 1;
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic void nfp_net_xfrm_del_state(struct xfrm_state *x)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct nfp_ipsec_cfg_mssg msg = {
51362306a36Sopenharmony_ci		.cmd = NFP_IPSEC_CFG_MSSG_INV_SA,
51462306a36Sopenharmony_ci		.sa_idx = x->xso.offload_handle - 1,
51562306a36Sopenharmony_ci	};
51662306a36Sopenharmony_ci	struct net_device *netdev = x->xso.real_dev;
51762306a36Sopenharmony_ci	struct nfp_net *nn;
51862306a36Sopenharmony_ci	int err;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	nn = netdev_priv(netdev);
52162306a36Sopenharmony_ci	err = nfp_net_sched_mbox_amsg_work(nn, NFP_NET_CFG_MBOX_CMD_IPSEC, &msg,
52262306a36Sopenharmony_ci					   sizeof(msg), nfp_net_ipsec_cfg);
52362306a36Sopenharmony_ci	if (err)
52462306a36Sopenharmony_ci		nn_warn(nn, "Failed to invalidate SA in hardware\n");
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	xa_erase(&nn->xa_ipsec, x->xso.offload_handle - 1);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic bool nfp_net_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	if (x->props.family == AF_INET)
53262306a36Sopenharmony_ci		/* Offload with IPv4 options is not supported yet */
53362306a36Sopenharmony_ci		return ip_hdr(skb)->ihl == 5;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Offload with IPv6 extension headers is not support yet */
53662306a36Sopenharmony_ci	return !(ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr));
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic const struct xfrmdev_ops nfp_net_ipsec_xfrmdev_ops = {
54062306a36Sopenharmony_ci	.xdo_dev_state_add = nfp_net_xfrm_add_state,
54162306a36Sopenharmony_ci	.xdo_dev_state_delete = nfp_net_xfrm_del_state,
54262306a36Sopenharmony_ci	.xdo_dev_offload_ok = nfp_net_ipsec_offload_ok,
54362306a36Sopenharmony_ci};
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_civoid nfp_net_ipsec_init(struct nfp_net *nn)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC))
54862306a36Sopenharmony_ci		return;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	xa_init_flags(&nn->xa_ipsec, XA_FLAGS_ALLOC);
55162306a36Sopenharmony_ci	nn->dp.netdev->xfrmdev_ops = &nfp_net_ipsec_xfrmdev_ops;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_civoid nfp_net_ipsec_clean(struct nfp_net *nn)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC))
55762306a36Sopenharmony_ci		return;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	WARN_ON(!xa_empty(&nn->xa_ipsec));
56062306a36Sopenharmony_ci	xa_destroy(&nn->xa_ipsec);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cibool nfp_net_ipsec_tx_prep(struct nfp_net_dp *dp, struct sk_buff *skb,
56462306a36Sopenharmony_ci			   struct nfp_ipsec_offload *offload_info)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct xfrm_offload *xo = xfrm_offload(skb);
56762306a36Sopenharmony_ci	struct xfrm_state *x;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	x = xfrm_input_state(skb);
57062306a36Sopenharmony_ci	if (!x)
57162306a36Sopenharmony_ci		return false;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	offload_info->seq_hi = xo->seq.hi;
57462306a36Sopenharmony_ci	offload_info->seq_low = xo->seq.low;
57562306a36Sopenharmony_ci	offload_info->handle = x->xso.offload_handle;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return true;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ciint nfp_net_ipsec_rx(struct nfp_meta_parsed *meta, struct sk_buff *skb)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct net_device *netdev = skb->dev;
58362306a36Sopenharmony_ci	struct xfrm_offload *xo;
58462306a36Sopenharmony_ci	struct xfrm_state *x;
58562306a36Sopenharmony_ci	struct sec_path *sp;
58662306a36Sopenharmony_ci	struct nfp_net *nn;
58762306a36Sopenharmony_ci	u32 saidx;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	nn = netdev_priv(netdev);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	saidx = meta->ipsec_saidx - 1;
59262306a36Sopenharmony_ci	if (saidx >= NFP_NET_IPSEC_MAX_SA_CNT)
59362306a36Sopenharmony_ci		return -EINVAL;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	sp = secpath_set(skb);
59662306a36Sopenharmony_ci	if (unlikely(!sp))
59762306a36Sopenharmony_ci		return -ENOMEM;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	xa_lock(&nn->xa_ipsec);
60062306a36Sopenharmony_ci	x = xa_load(&nn->xa_ipsec, saidx);
60162306a36Sopenharmony_ci	xa_unlock(&nn->xa_ipsec);
60262306a36Sopenharmony_ci	if (!x)
60362306a36Sopenharmony_ci		return -EINVAL;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	xfrm_state_hold(x);
60662306a36Sopenharmony_ci	sp->xvec[sp->len++] = x;
60762306a36Sopenharmony_ci	sp->olen++;
60862306a36Sopenharmony_ci	xo = xfrm_offload(skb);
60962306a36Sopenharmony_ci	xo->flags = CRYPTO_DONE;
61062306a36Sopenharmony_ci	xo->status = CRYPTO_SUCCESS;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	return 0;
61362306a36Sopenharmony_ci}
614