xref: /dpdk/lib/ipsec/ipsec_sad.c (revision 3401a4afbb54a1b897551847a3e16c36afdf707a)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2019 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
599a2dd95SBruce Richardson #include <string.h>
699a2dd95SBruce Richardson 
799a2dd95SBruce Richardson #include <rte_eal_memconfig.h>
899a2dd95SBruce Richardson #include <rte_errno.h>
999a2dd95SBruce Richardson #include <rte_hash.h>
1099a2dd95SBruce Richardson #include <rte_hash_crc.h>
1199a2dd95SBruce Richardson #include <rte_malloc.h>
1299a2dd95SBruce Richardson #include <rte_random.h>
1399a2dd95SBruce Richardson #include <rte_tailq.h>
1499a2dd95SBruce Richardson 
1599a2dd95SBruce Richardson #include "rte_ipsec_sad.h"
1699a2dd95SBruce Richardson 
1799a2dd95SBruce Richardson /*
1899a2dd95SBruce Richardson  * Rules are stored in three hash tables depending on key_type.
1999a2dd95SBruce Richardson  * Each rule will also be stored in SPI_ONLY table.
2099a2dd95SBruce Richardson  * for each data entry within this table last two bits are reserved to
2199a2dd95SBruce Richardson  * indicate presence of entries with the same SPI in DIP and DIP+SIP tables.
2299a2dd95SBruce Richardson  */
2399a2dd95SBruce Richardson 
2499a2dd95SBruce Richardson #define SAD_PREFIX		"SAD_"
2599a2dd95SBruce Richardson /* "SAD_<name>" */
2699a2dd95SBruce Richardson #define SAD_FORMAT		SAD_PREFIX "%s"
2799a2dd95SBruce Richardson 
2899a2dd95SBruce Richardson #define DEFAULT_HASH_FUNC	rte_hash_crc
2999a2dd95SBruce Richardson #define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
3099a2dd95SBruce Richardson 
3199a2dd95SBruce Richardson struct hash_cnt {
3299a2dd95SBruce Richardson 	uint32_t cnt_dip;
3399a2dd95SBruce Richardson 	uint32_t cnt_dip_sip;
3499a2dd95SBruce Richardson };
3599a2dd95SBruce Richardson 
3699a2dd95SBruce Richardson struct rte_ipsec_sad {
3799a2dd95SBruce Richardson 	char name[RTE_IPSEC_SAD_NAMESIZE];
3899a2dd95SBruce Richardson 	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
3999a2dd95SBruce Richardson 	uint32_t keysize[RTE_IPSEC_SAD_KEY_TYPE_MASK];
4099a2dd95SBruce Richardson 	uint32_t init_val;
4199a2dd95SBruce Richardson 	/* Array to track number of more specific rules
4299a2dd95SBruce Richardson 	 * (spi_dip or spi_dip_sip). Used only in add/delete
4399a2dd95SBruce Richardson 	 * as a helper struct.
4499a2dd95SBruce Richardson 	 */
45*3401a4afSDavid Marchand 	struct hash_cnt cnt_arr[];
4699a2dd95SBruce Richardson };
4799a2dd95SBruce Richardson 
4899a2dd95SBruce Richardson TAILQ_HEAD(rte_ipsec_sad_list, rte_tailq_entry);
4999a2dd95SBruce Richardson static struct rte_tailq_elem rte_ipsec_sad_tailq = {
5099a2dd95SBruce Richardson 	.name = "RTE_IPSEC_SAD",
5199a2dd95SBruce Richardson };
EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq)5299a2dd95SBruce Richardson EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq)
5399a2dd95SBruce Richardson 
5499a2dd95SBruce Richardson #define SET_BIT(ptr, bit)	(void *)((uintptr_t)(ptr) | (uintptr_t)(bit))
5599a2dd95SBruce Richardson #define CLEAR_BIT(ptr, bit)	(void *)((uintptr_t)(ptr) & ~(uintptr_t)(bit))
5699a2dd95SBruce Richardson #define GET_BIT(ptr, bit)	(void *)((uintptr_t)(ptr) & (uintptr_t)(bit))
5799a2dd95SBruce Richardson 
5899a2dd95SBruce Richardson /*
5999a2dd95SBruce Richardson  * @internal helper function
6099a2dd95SBruce Richardson  * Add a rule of type SPI_DIP or SPI_DIP_SIP.
6199a2dd95SBruce Richardson  * Inserts a rule into an appropriate hash table,
6299a2dd95SBruce Richardson  * updates the value for a given SPI in SPI_ONLY hash table
6399a2dd95SBruce Richardson  * reflecting presence of more specific rule type in two LSBs.
644a6672c2SStephen Hemminger  * Updates a counter that reflects the number of rules with the same SPI.
6599a2dd95SBruce Richardson  */
6699a2dd95SBruce Richardson static inline int
6799a2dd95SBruce Richardson add_specific(struct rte_ipsec_sad *sad, const void *key,
6899a2dd95SBruce Richardson 		int key_type, void *sa)
6999a2dd95SBruce Richardson {
7099a2dd95SBruce Richardson 	void *tmp_val;
7199a2dd95SBruce Richardson 	int ret, notexist;
7299a2dd95SBruce Richardson 
7399a2dd95SBruce Richardson 	/* Check if the key is present in the table.
7499a2dd95SBruce Richardson 	 * Need for further accaunting in cnt_arr
7599a2dd95SBruce Richardson 	 */
7699a2dd95SBruce Richardson 	ret = rte_hash_lookup_with_hash(sad->hash[key_type], key,
7799a2dd95SBruce Richardson 		rte_hash_crc(key, sad->keysize[key_type], sad->init_val));
7899a2dd95SBruce Richardson 	notexist = (ret == -ENOENT);
7999a2dd95SBruce Richardson 
8099a2dd95SBruce Richardson 	/* Add an SA to the corresponding table.*/
8199a2dd95SBruce Richardson 	ret = rte_hash_add_key_with_hash_data(sad->hash[key_type], key,
8299a2dd95SBruce Richardson 		rte_hash_crc(key, sad->keysize[key_type], sad->init_val), sa);
8399a2dd95SBruce Richardson 	if (ret != 0)
8499a2dd95SBruce Richardson 		return ret;
8599a2dd95SBruce Richardson 
8699a2dd95SBruce Richardson 	/* Check if there is an entry in SPI only table with the same SPI */
8799a2dd95SBruce Richardson 	ret = rte_hash_lookup_with_hash_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
8899a2dd95SBruce Richardson 		key, rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
8999a2dd95SBruce Richardson 		sad->init_val), &tmp_val);
9099a2dd95SBruce Richardson 	if (ret < 0)
9199a2dd95SBruce Richardson 		tmp_val = NULL;
9299a2dd95SBruce Richardson 	tmp_val = SET_BIT(tmp_val, key_type);
9399a2dd95SBruce Richardson 
9499a2dd95SBruce Richardson 	/* Add an entry into SPI only table */
9599a2dd95SBruce Richardson 	ret = rte_hash_add_key_with_hash_data(
9699a2dd95SBruce Richardson 		sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key,
9799a2dd95SBruce Richardson 		rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
9899a2dd95SBruce Richardson 		sad->init_val), tmp_val);
9999a2dd95SBruce Richardson 	if (ret != 0)
10099a2dd95SBruce Richardson 		return ret;
10199a2dd95SBruce Richardson 
10299a2dd95SBruce Richardson 	/* Update a counter for a given SPI */
10399a2dd95SBruce Richardson 	ret = rte_hash_lookup_with_hash(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key,
10499a2dd95SBruce Richardson 		rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
10599a2dd95SBruce Richardson 		sad->init_val));
10699a2dd95SBruce Richardson 	if (ret < 0)
10799a2dd95SBruce Richardson 		return ret;
10899a2dd95SBruce Richardson 	if (key_type == RTE_IPSEC_SAD_SPI_DIP)
10999a2dd95SBruce Richardson 		sad->cnt_arr[ret].cnt_dip += notexist;
11099a2dd95SBruce Richardson 	else
11199a2dd95SBruce Richardson 		sad->cnt_arr[ret].cnt_dip_sip += notexist;
11299a2dd95SBruce Richardson 
11399a2dd95SBruce Richardson 	return 0;
11499a2dd95SBruce Richardson }
11599a2dd95SBruce Richardson 
11699a2dd95SBruce Richardson int
rte_ipsec_sad_add(struct rte_ipsec_sad * sad,const union rte_ipsec_sad_key * key,int key_type,void * sa)11799a2dd95SBruce Richardson rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
11899a2dd95SBruce Richardson 		const union rte_ipsec_sad_key *key,
11999a2dd95SBruce Richardson 		int key_type, void *sa)
12099a2dd95SBruce Richardson {
12199a2dd95SBruce Richardson 	void *tmp_val;
12299a2dd95SBruce Richardson 	int ret;
12399a2dd95SBruce Richardson 
12499a2dd95SBruce Richardson 	if ((sad == NULL) || (key == NULL) || (sa == NULL) ||
12599a2dd95SBruce Richardson 			/* sa must be 4 byte aligned */
12699a2dd95SBruce Richardson 			(GET_BIT(sa, RTE_IPSEC_SAD_KEY_TYPE_MASK) != 0))
12799a2dd95SBruce Richardson 		return -EINVAL;
12899a2dd95SBruce Richardson 
12999a2dd95SBruce Richardson 	/*
13099a2dd95SBruce Richardson 	 * Rules are stored in three hash tables depending on key_type.
13199a2dd95SBruce Richardson 	 * All rules will also have an entry in SPI_ONLY table, with entry
13299a2dd95SBruce Richardson 	 * value's two LSB's also indicating presence of rule with this SPI
13399a2dd95SBruce Richardson 	 * in other tables.
13499a2dd95SBruce Richardson 	 */
13599a2dd95SBruce Richardson 	switch (key_type) {
13699a2dd95SBruce Richardson 	case(RTE_IPSEC_SAD_SPI_ONLY):
13799a2dd95SBruce Richardson 		ret = rte_hash_lookup_with_hash_data(sad->hash[key_type],
13899a2dd95SBruce Richardson 			key, rte_hash_crc(key, sad->keysize[key_type],
13999a2dd95SBruce Richardson 			sad->init_val), &tmp_val);
14099a2dd95SBruce Richardson 		if (ret >= 0)
14199a2dd95SBruce Richardson 			tmp_val = SET_BIT(sa, GET_BIT(tmp_val,
14299a2dd95SBruce Richardson 				RTE_IPSEC_SAD_KEY_TYPE_MASK));
14399a2dd95SBruce Richardson 		else
14499a2dd95SBruce Richardson 			tmp_val = sa;
14599a2dd95SBruce Richardson 		ret = rte_hash_add_key_with_hash_data(sad->hash[key_type],
14699a2dd95SBruce Richardson 			key, rte_hash_crc(key, sad->keysize[key_type],
14799a2dd95SBruce Richardson 			sad->init_val), tmp_val);
14899a2dd95SBruce Richardson 		return ret;
14999a2dd95SBruce Richardson 	case(RTE_IPSEC_SAD_SPI_DIP):
15099a2dd95SBruce Richardson 	case(RTE_IPSEC_SAD_SPI_DIP_SIP):
15199a2dd95SBruce Richardson 		return add_specific(sad, key, key_type, sa);
15299a2dd95SBruce Richardson 	default:
15399a2dd95SBruce Richardson 		return -EINVAL;
15499a2dd95SBruce Richardson 	}
15599a2dd95SBruce Richardson }
15699a2dd95SBruce Richardson 
15799a2dd95SBruce Richardson /*
15899a2dd95SBruce Richardson  * @internal helper function
15999a2dd95SBruce Richardson  * Delete a rule of type SPI_DIP or SPI_DIP_SIP.
16099a2dd95SBruce Richardson  * Deletes an entry from an appropriate hash table and decrements
16199a2dd95SBruce Richardson  * an entry counter for given SPI.
16299a2dd95SBruce Richardson  * If entry to remove is the last one with given SPI within the table,
16399a2dd95SBruce Richardson  * then it will also update related entry in SPI_ONLY table.
16499a2dd95SBruce Richardson  * Removes an entry from SPI_ONLY hash table if there no rule left
16599a2dd95SBruce Richardson  * for this SPI in any table.
16699a2dd95SBruce Richardson  */
16799a2dd95SBruce Richardson static inline int
del_specific(struct rte_ipsec_sad * sad,const void * key,int key_type)16899a2dd95SBruce Richardson del_specific(struct rte_ipsec_sad *sad, const void *key, int key_type)
16999a2dd95SBruce Richardson {
17099a2dd95SBruce Richardson 	void *tmp_val;
17199a2dd95SBruce Richardson 	int ret;
17299a2dd95SBruce Richardson 	uint32_t *cnt;
17399a2dd95SBruce Richardson 
17499a2dd95SBruce Richardson 	/* Remove an SA from the corresponding table.*/
17599a2dd95SBruce Richardson 	ret = rte_hash_del_key_with_hash(sad->hash[key_type], key,
17699a2dd95SBruce Richardson 		rte_hash_crc(key, sad->keysize[key_type], sad->init_val));
17799a2dd95SBruce Richardson 	if (ret < 0)
17899a2dd95SBruce Richardson 		return ret;
17999a2dd95SBruce Richardson 
18099a2dd95SBruce Richardson 	/* Get an index of cnt_arr entry for a given SPI */
18199a2dd95SBruce Richardson 	ret = rte_hash_lookup_with_hash_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
18299a2dd95SBruce Richardson 		key, rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
18399a2dd95SBruce Richardson 		sad->init_val), &tmp_val);
18499a2dd95SBruce Richardson 	if (ret < 0)
18599a2dd95SBruce Richardson 		return ret;
18699a2dd95SBruce Richardson 	cnt = (key_type == RTE_IPSEC_SAD_SPI_DIP) ?
18799a2dd95SBruce Richardson 			&sad->cnt_arr[ret].cnt_dip :
18899a2dd95SBruce Richardson 			&sad->cnt_arr[ret].cnt_dip_sip;
18999a2dd95SBruce Richardson 	if (--(*cnt) != 0)
19099a2dd95SBruce Richardson 		return 0;
19199a2dd95SBruce Richardson 
19299a2dd95SBruce Richardson 	/* corresponding counter is 0, clear the bit indicating
19399a2dd95SBruce Richardson 	 * the presence of more specific rule for a given SPI.
19499a2dd95SBruce Richardson 	 */
19599a2dd95SBruce Richardson 	tmp_val = CLEAR_BIT(tmp_val, key_type);
19699a2dd95SBruce Richardson 
19799a2dd95SBruce Richardson 	/* if there are no rules left with same SPI,
19899a2dd95SBruce Richardson 	 * remove an entry from SPI_only table
19999a2dd95SBruce Richardson 	 */
20099a2dd95SBruce Richardson 	if (tmp_val == NULL)
20199a2dd95SBruce Richardson 		ret = rte_hash_del_key_with_hash(
20299a2dd95SBruce Richardson 			sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key,
20399a2dd95SBruce Richardson 			rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
20499a2dd95SBruce Richardson 			sad->init_val));
20599a2dd95SBruce Richardson 	else
20699a2dd95SBruce Richardson 		ret = rte_hash_add_key_with_hash_data(
20799a2dd95SBruce Richardson 			sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key,
20899a2dd95SBruce Richardson 			rte_hash_crc(key, sad->keysize[RTE_IPSEC_SAD_SPI_ONLY],
20999a2dd95SBruce Richardson 			sad->init_val), tmp_val);
21099a2dd95SBruce Richardson 	if (ret < 0)
21199a2dd95SBruce Richardson 		return ret;
21299a2dd95SBruce Richardson 	return 0;
21399a2dd95SBruce Richardson }
21499a2dd95SBruce Richardson 
21599a2dd95SBruce Richardson int
rte_ipsec_sad_del(struct rte_ipsec_sad * sad,const union rte_ipsec_sad_key * key,int key_type)21699a2dd95SBruce Richardson rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
21799a2dd95SBruce Richardson 		const union rte_ipsec_sad_key *key,
21899a2dd95SBruce Richardson 		int key_type)
21999a2dd95SBruce Richardson {
22099a2dd95SBruce Richardson 	void *tmp_val;
22199a2dd95SBruce Richardson 	int ret;
22299a2dd95SBruce Richardson 
22399a2dd95SBruce Richardson 	if ((sad == NULL) || (key == NULL))
22499a2dd95SBruce Richardson 		return -EINVAL;
22599a2dd95SBruce Richardson 	switch (key_type) {
22699a2dd95SBruce Richardson 	case(RTE_IPSEC_SAD_SPI_ONLY):
22799a2dd95SBruce Richardson 		ret = rte_hash_lookup_with_hash_data(sad->hash[key_type],
22899a2dd95SBruce Richardson 			key, rte_hash_crc(key, sad->keysize[key_type],
22999a2dd95SBruce Richardson 			sad->init_val), &tmp_val);
23099a2dd95SBruce Richardson 		if (ret < 0)
23199a2dd95SBruce Richardson 			return ret;
23299a2dd95SBruce Richardson 		if (GET_BIT(tmp_val, RTE_IPSEC_SAD_KEY_TYPE_MASK) == 0) {
23399a2dd95SBruce Richardson 			ret = rte_hash_del_key_with_hash(sad->hash[key_type],
23499a2dd95SBruce Richardson 				key, rte_hash_crc(key, sad->keysize[key_type],
23599a2dd95SBruce Richardson 				sad->init_val));
23699a2dd95SBruce Richardson 			ret = ret < 0 ? ret : 0;
23799a2dd95SBruce Richardson 		} else {
23899a2dd95SBruce Richardson 			tmp_val = GET_BIT(tmp_val,
23999a2dd95SBruce Richardson 				RTE_IPSEC_SAD_KEY_TYPE_MASK);
24099a2dd95SBruce Richardson 			ret = rte_hash_add_key_with_hash_data(
24199a2dd95SBruce Richardson 				sad->hash[key_type], key,
24299a2dd95SBruce Richardson 				rte_hash_crc(key, sad->keysize[key_type],
24399a2dd95SBruce Richardson 				sad->init_val), tmp_val);
24499a2dd95SBruce Richardson 		}
24599a2dd95SBruce Richardson 		return ret;
24699a2dd95SBruce Richardson 	case(RTE_IPSEC_SAD_SPI_DIP):
24799a2dd95SBruce Richardson 	case(RTE_IPSEC_SAD_SPI_DIP_SIP):
24899a2dd95SBruce Richardson 		return del_specific(sad, key, key_type);
24999a2dd95SBruce Richardson 	default:
25099a2dd95SBruce Richardson 		return -EINVAL;
25199a2dd95SBruce Richardson 	}
25299a2dd95SBruce Richardson }
25399a2dd95SBruce Richardson 
25499a2dd95SBruce Richardson struct rte_ipsec_sad *
rte_ipsec_sad_create(const char * name,const struct rte_ipsec_sad_conf * conf)25599a2dd95SBruce Richardson rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
25699a2dd95SBruce Richardson {
25799a2dd95SBruce Richardson 	char hash_name[RTE_HASH_NAMESIZE];
25899a2dd95SBruce Richardson 	char sad_name[RTE_IPSEC_SAD_NAMESIZE];
25999a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
26099a2dd95SBruce Richardson 	struct rte_ipsec_sad_list *sad_list;
26199a2dd95SBruce Richardson 	struct rte_ipsec_sad *sad, *tmp_sad = NULL;
26299a2dd95SBruce Richardson 	struct rte_hash_parameters hash_params = {0};
26399a2dd95SBruce Richardson 	int ret;
26499a2dd95SBruce Richardson 	uint32_t sa_sum;
26599a2dd95SBruce Richardson 
26699a2dd95SBruce Richardson 	RTE_BUILD_BUG_ON(RTE_IPSEC_SAD_KEY_TYPE_MASK != 3);
26799a2dd95SBruce Richardson 
26899a2dd95SBruce Richardson 	if ((name == NULL) || (conf == NULL) ||
26999a2dd95SBruce Richardson 			((conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY] == 0) &&
27099a2dd95SBruce Richardson 			(conf->max_sa[RTE_IPSEC_SAD_SPI_DIP] == 0) &&
27199a2dd95SBruce Richardson 			(conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] == 0))) {
27299a2dd95SBruce Richardson 		rte_errno = EINVAL;
27399a2dd95SBruce Richardson 		return NULL;
27499a2dd95SBruce Richardson 	}
27599a2dd95SBruce Richardson 
27699a2dd95SBruce Richardson 	ret = snprintf(sad_name, RTE_IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
27799a2dd95SBruce Richardson 	if (ret < 0 || ret >= RTE_IPSEC_SAD_NAMESIZE) {
27899a2dd95SBruce Richardson 		rte_errno = ENAMETOOLONG;
27999a2dd95SBruce Richardson 		return NULL;
28099a2dd95SBruce Richardson 	}
28199a2dd95SBruce Richardson 
28299a2dd95SBruce Richardson 	/** Init SAD*/
28399a2dd95SBruce Richardson 	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
28499a2dd95SBruce Richardson 		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
28599a2dd95SBruce Richardson 		RTE_MAX(MIN_HASH_ENTRIES,
28699a2dd95SBruce Richardson 		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
28799a2dd95SBruce Richardson 		RTE_MAX(MIN_HASH_ENTRIES,
28899a2dd95SBruce Richardson 		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP]);
28999a2dd95SBruce Richardson 	sad = rte_zmalloc_socket(NULL, sizeof(*sad) +
29099a2dd95SBruce Richardson 		(sizeof(struct hash_cnt) * sa_sum),
29199a2dd95SBruce Richardson 		RTE_CACHE_LINE_SIZE, conf->socket_id);
29299a2dd95SBruce Richardson 	if (sad == NULL) {
29399a2dd95SBruce Richardson 		rte_errno = ENOMEM;
29499a2dd95SBruce Richardson 		return NULL;
29599a2dd95SBruce Richardson 	}
29699a2dd95SBruce Richardson 	memcpy(sad->name, sad_name, sizeof(sad_name));
29799a2dd95SBruce Richardson 
29899a2dd95SBruce Richardson 	hash_params.hash_func = DEFAULT_HASH_FUNC;
29999a2dd95SBruce Richardson 	hash_params.hash_func_init_val = rte_rand();
30099a2dd95SBruce Richardson 	sad->init_val = hash_params.hash_func_init_val;
30199a2dd95SBruce Richardson 	hash_params.socket_id = conf->socket_id;
30299a2dd95SBruce Richardson 	hash_params.name = hash_name;
30399a2dd95SBruce Richardson 	if (conf->flags & RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY)
30499a2dd95SBruce Richardson 		hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
30599a2dd95SBruce Richardson 
30699a2dd95SBruce Richardson 	/** Init hash[RTE_IPSEC_SAD_SPI_ONLY] for SPI only */
30799a2dd95SBruce Richardson 	snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
30899a2dd95SBruce Richardson 	hash_params.key_len = sizeof(((struct rte_ipsec_sadv4_key *)0)->spi);
30999a2dd95SBruce Richardson 	sad->keysize[RTE_IPSEC_SAD_SPI_ONLY] = hash_params.key_len;
31099a2dd95SBruce Richardson 	hash_params.entries = sa_sum;
31199a2dd95SBruce Richardson 	sad->hash[RTE_IPSEC_SAD_SPI_ONLY] = rte_hash_create(&hash_params);
31299a2dd95SBruce Richardson 	if (sad->hash[RTE_IPSEC_SAD_SPI_ONLY] == NULL) {
31399a2dd95SBruce Richardson 		rte_ipsec_sad_destroy(sad);
31499a2dd95SBruce Richardson 		return NULL;
31599a2dd95SBruce Richardson 	}
31699a2dd95SBruce Richardson 
31799a2dd95SBruce Richardson 	/** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
31899a2dd95SBruce Richardson 	snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
31999a2dd95SBruce Richardson 	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
32099a2dd95SBruce Richardson 		hash_params.key_len +=
32199a2dd95SBruce Richardson 			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
32299a2dd95SBruce Richardson 	else
32399a2dd95SBruce Richardson 		hash_params.key_len +=
32499a2dd95SBruce Richardson 			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
32599a2dd95SBruce Richardson 	sad->keysize[RTE_IPSEC_SAD_SPI_DIP] = hash_params.key_len;
32699a2dd95SBruce Richardson 	hash_params.entries = RTE_MAX(MIN_HASH_ENTRIES,
32799a2dd95SBruce Richardson 			conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]);
32899a2dd95SBruce Richardson 	sad->hash[RTE_IPSEC_SAD_SPI_DIP] = rte_hash_create(&hash_params);
32999a2dd95SBruce Richardson 	if (sad->hash[RTE_IPSEC_SAD_SPI_DIP] == NULL) {
33099a2dd95SBruce Richardson 		rte_ipsec_sad_destroy(sad);
33199a2dd95SBruce Richardson 		return NULL;
33299a2dd95SBruce Richardson 	}
33399a2dd95SBruce Richardson 
33499a2dd95SBruce Richardson 	/** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
33599a2dd95SBruce Richardson 	snprintf(hash_name, sizeof(hash_name), "sad_3_%p", sad);
33699a2dd95SBruce Richardson 	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
33799a2dd95SBruce Richardson 		hash_params.key_len +=
33899a2dd95SBruce Richardson 			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
33999a2dd95SBruce Richardson 	else
34099a2dd95SBruce Richardson 		hash_params.key_len +=
34199a2dd95SBruce Richardson 			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
34299a2dd95SBruce Richardson 	sad->keysize[RTE_IPSEC_SAD_SPI_DIP_SIP] = hash_params.key_len;
34399a2dd95SBruce Richardson 	hash_params.entries = RTE_MAX(MIN_HASH_ENTRIES,
34499a2dd95SBruce Richardson 			conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP]);
34599a2dd95SBruce Richardson 	sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] = rte_hash_create(&hash_params);
34699a2dd95SBruce Richardson 	if (sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] == NULL) {
34799a2dd95SBruce Richardson 		rte_ipsec_sad_destroy(sad);
34899a2dd95SBruce Richardson 		return NULL;
34999a2dd95SBruce Richardson 	}
35099a2dd95SBruce Richardson 
35199a2dd95SBruce Richardson 	sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
35299a2dd95SBruce Richardson 			rte_ipsec_sad_list);
35399a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
35499a2dd95SBruce Richardson 	/* guarantee there's no existing */
35599a2dd95SBruce Richardson 	TAILQ_FOREACH(te, sad_list, next) {
35699a2dd95SBruce Richardson 		tmp_sad = (struct rte_ipsec_sad *)te->data;
35799a2dd95SBruce Richardson 		if (strncmp(sad_name, tmp_sad->name,
35899a2dd95SBruce Richardson 				RTE_IPSEC_SAD_NAMESIZE) == 0)
35999a2dd95SBruce Richardson 			break;
36099a2dd95SBruce Richardson 	}
36199a2dd95SBruce Richardson 	if (te != NULL) {
36299a2dd95SBruce Richardson 		rte_mcfg_tailq_write_unlock();
36399a2dd95SBruce Richardson 		rte_errno = EEXIST;
36499a2dd95SBruce Richardson 		rte_ipsec_sad_destroy(sad);
36599a2dd95SBruce Richardson 		return NULL;
36699a2dd95SBruce Richardson 	}
36799a2dd95SBruce Richardson 
36899a2dd95SBruce Richardson 	/* allocate tailq entry */
36999a2dd95SBruce Richardson 	te = rte_zmalloc("IPSEC_SAD_TAILQ_ENTRY", sizeof(*te), 0);
37099a2dd95SBruce Richardson 	if (te == NULL) {
37199a2dd95SBruce Richardson 		rte_mcfg_tailq_write_unlock();
37299a2dd95SBruce Richardson 		rte_errno = ENOMEM;
37399a2dd95SBruce Richardson 		rte_ipsec_sad_destroy(sad);
37499a2dd95SBruce Richardson 		return NULL;
37599a2dd95SBruce Richardson 	}
37699a2dd95SBruce Richardson 
37799a2dd95SBruce Richardson 	te->data = (void *)sad;
37899a2dd95SBruce Richardson 	TAILQ_INSERT_TAIL(sad_list, te, next);
37999a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
38099a2dd95SBruce Richardson 	return sad;
38199a2dd95SBruce Richardson }
38299a2dd95SBruce Richardson 
38399a2dd95SBruce Richardson struct rte_ipsec_sad *
rte_ipsec_sad_find_existing(const char * name)38499a2dd95SBruce Richardson rte_ipsec_sad_find_existing(const char *name)
38599a2dd95SBruce Richardson {
38699a2dd95SBruce Richardson 	char sad_name[RTE_IPSEC_SAD_NAMESIZE];
38799a2dd95SBruce Richardson 	struct rte_ipsec_sad *sad = NULL;
38899a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
38999a2dd95SBruce Richardson 	struct rte_ipsec_sad_list *sad_list;
39099a2dd95SBruce Richardson 	int ret;
39199a2dd95SBruce Richardson 
39299a2dd95SBruce Richardson 	ret = snprintf(sad_name, RTE_IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
39399a2dd95SBruce Richardson 	if (ret < 0 || ret >= RTE_IPSEC_SAD_NAMESIZE) {
39499a2dd95SBruce Richardson 		rte_errno = ENAMETOOLONG;
39599a2dd95SBruce Richardson 		return NULL;
39699a2dd95SBruce Richardson 	}
39799a2dd95SBruce Richardson 
39899a2dd95SBruce Richardson 	sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
39999a2dd95SBruce Richardson 		rte_ipsec_sad_list);
40099a2dd95SBruce Richardson 
40199a2dd95SBruce Richardson 	rte_mcfg_tailq_read_lock();
40299a2dd95SBruce Richardson 	TAILQ_FOREACH(te, sad_list, next) {
40399a2dd95SBruce Richardson 		sad = (struct rte_ipsec_sad *) te->data;
40499a2dd95SBruce Richardson 		if (strncmp(sad_name, sad->name, RTE_IPSEC_SAD_NAMESIZE) == 0)
40599a2dd95SBruce Richardson 			break;
40699a2dd95SBruce Richardson 	}
40799a2dd95SBruce Richardson 	rte_mcfg_tailq_read_unlock();
40899a2dd95SBruce Richardson 
40999a2dd95SBruce Richardson 	if (te == NULL) {
41099a2dd95SBruce Richardson 		rte_errno = ENOENT;
41199a2dd95SBruce Richardson 		return NULL;
41299a2dd95SBruce Richardson 	}
41399a2dd95SBruce Richardson 
41499a2dd95SBruce Richardson 	return sad;
41599a2dd95SBruce Richardson }
41699a2dd95SBruce Richardson 
41799a2dd95SBruce Richardson void
rte_ipsec_sad_destroy(struct rte_ipsec_sad * sad)41899a2dd95SBruce Richardson rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
41999a2dd95SBruce Richardson {
42099a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
42199a2dd95SBruce Richardson 	struct rte_ipsec_sad_list *sad_list;
42299a2dd95SBruce Richardson 
42399a2dd95SBruce Richardson 	if (sad == NULL)
42499a2dd95SBruce Richardson 		return;
42599a2dd95SBruce Richardson 
42699a2dd95SBruce Richardson 	sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
42799a2dd95SBruce Richardson 			rte_ipsec_sad_list);
42899a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
42999a2dd95SBruce Richardson 	TAILQ_FOREACH(te, sad_list, next) {
43099a2dd95SBruce Richardson 		if (te->data == (void *)sad)
43199a2dd95SBruce Richardson 			break;
43299a2dd95SBruce Richardson 	}
43399a2dd95SBruce Richardson 	if (te != NULL)
43499a2dd95SBruce Richardson 		TAILQ_REMOVE(sad_list, te, next);
43599a2dd95SBruce Richardson 
43699a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
43799a2dd95SBruce Richardson 
43899a2dd95SBruce Richardson 	rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_ONLY]);
43999a2dd95SBruce Richardson 	rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP]);
44099a2dd95SBruce Richardson 	rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP]);
44199a2dd95SBruce Richardson 	rte_free(sad);
44299a2dd95SBruce Richardson 	rte_free(te);
44399a2dd95SBruce Richardson }
44499a2dd95SBruce Richardson 
44599a2dd95SBruce Richardson /*
44699a2dd95SBruce Richardson  * @internal helper function
44799a2dd95SBruce Richardson  * Lookup a batch of keys in three hash tables.
44899a2dd95SBruce Richardson  * First lookup key in SPI_ONLY table.
44999a2dd95SBruce Richardson  * If there is an entry for the corresponding SPI check its value.
45099a2dd95SBruce Richardson  * Two least significant bits of the value indicate
45199a2dd95SBruce Richardson  * the presence of more specific rule in other tables.
45299a2dd95SBruce Richardson  * Perform additional lookup in corresponding hash tables
45399a2dd95SBruce Richardson  * and update the value if lookup succeeded.
45499a2dd95SBruce Richardson  */
45599a2dd95SBruce Richardson static int
__ipsec_sad_lookup(const struct rte_ipsec_sad * sad,const union rte_ipsec_sad_key * keys[],void * sa[],uint32_t n)45699a2dd95SBruce Richardson __ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
45799a2dd95SBruce Richardson 		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
45899a2dd95SBruce Richardson {
45999a2dd95SBruce Richardson 	const void *keys_2[RTE_HASH_LOOKUP_BULK_MAX];
46099a2dd95SBruce Richardson 	const void *keys_3[RTE_HASH_LOOKUP_BULK_MAX];
46199a2dd95SBruce Richardson 	void *vals_2[RTE_HASH_LOOKUP_BULK_MAX] = {NULL};
46299a2dd95SBruce Richardson 	void *vals_3[RTE_HASH_LOOKUP_BULK_MAX] = {NULL};
46399a2dd95SBruce Richardson 	uint32_t idx_2[RTE_HASH_LOOKUP_BULK_MAX];
46499a2dd95SBruce Richardson 	uint32_t idx_3[RTE_HASH_LOOKUP_BULK_MAX];
46599a2dd95SBruce Richardson 	uint64_t mask_1, mask_2, mask_3;
46699a2dd95SBruce Richardson 	uint64_t map, map_spec;
46799a2dd95SBruce Richardson 	uint32_t n_2 = 0;
46899a2dd95SBruce Richardson 	uint32_t n_3 = 0;
46999a2dd95SBruce Richardson 	uint32_t i;
47099a2dd95SBruce Richardson 	int found = 0;
47199a2dd95SBruce Richardson 	hash_sig_t hash_sig[RTE_HASH_LOOKUP_BULK_MAX];
47299a2dd95SBruce Richardson 	hash_sig_t hash_sig_2[RTE_HASH_LOOKUP_BULK_MAX];
47399a2dd95SBruce Richardson 	hash_sig_t hash_sig_3[RTE_HASH_LOOKUP_BULK_MAX];
47499a2dd95SBruce Richardson 
47599a2dd95SBruce Richardson 	for (i = 0; i < n; i++) {
47699a2dd95SBruce Richardson 		sa[i] = NULL;
47799a2dd95SBruce Richardson 		hash_sig[i] = rte_hash_crc_4byte(keys[i]->v4.spi,
47899a2dd95SBruce Richardson 			sad->init_val);
47999a2dd95SBruce Richardson 	}
48099a2dd95SBruce Richardson 
48199a2dd95SBruce Richardson 	/*
48299a2dd95SBruce Richardson 	 * Lookup keys in SPI only hash table first.
48399a2dd95SBruce Richardson 	 */
48499a2dd95SBruce Richardson 	rte_hash_lookup_with_hash_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
48599a2dd95SBruce Richardson 		(const void **)keys, hash_sig, n, &mask_1, sa);
48699a2dd95SBruce Richardson 	for (map = mask_1; map; map &= (map - 1)) {
48799a2dd95SBruce Richardson 		i = rte_bsf64(map);
48899a2dd95SBruce Richardson 		/*
48999a2dd95SBruce Richardson 		 * if returned value indicates presence of a rule in other
49099a2dd95SBruce Richardson 		 * tables save a key for further lookup.
49199a2dd95SBruce Richardson 		 */
49299a2dd95SBruce Richardson 		if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP_SIP) {
49399a2dd95SBruce Richardson 			idx_3[n_3] = i;
49499a2dd95SBruce Richardson 			hash_sig_3[n_3] = rte_hash_crc(keys[i],
49599a2dd95SBruce Richardson 				sad->keysize[RTE_IPSEC_SAD_SPI_DIP_SIP],
49699a2dd95SBruce Richardson 				sad->init_val);
49799a2dd95SBruce Richardson 			keys_3[n_3++] = keys[i];
49899a2dd95SBruce Richardson 		}
49999a2dd95SBruce Richardson 		if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP) {
50099a2dd95SBruce Richardson 			idx_2[n_2] = i;
50199a2dd95SBruce Richardson 			hash_sig_2[n_2] = rte_hash_crc(keys[i],
50299a2dd95SBruce Richardson 				sad->keysize[RTE_IPSEC_SAD_SPI_DIP],
50399a2dd95SBruce Richardson 				sad->init_val);
50499a2dd95SBruce Richardson 			keys_2[n_2++] = keys[i];
50599a2dd95SBruce Richardson 		}
50699a2dd95SBruce Richardson 		/* clear 2 LSB's which indicate the presence
50799a2dd95SBruce Richardson 		 * of more specific rules
50899a2dd95SBruce Richardson 		 */
50999a2dd95SBruce Richardson 		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
51099a2dd95SBruce Richardson 	}
51199a2dd95SBruce Richardson 
51299a2dd95SBruce Richardson 	/* Lookup for more specific rules in SPI_DIP table */
51399a2dd95SBruce Richardson 	if (n_2 != 0) {
51499a2dd95SBruce Richardson 		rte_hash_lookup_with_hash_bulk_data(
51599a2dd95SBruce Richardson 			sad->hash[RTE_IPSEC_SAD_SPI_DIP],
51699a2dd95SBruce Richardson 			keys_2, hash_sig_2, n_2, &mask_2, vals_2);
51799a2dd95SBruce Richardson 		for (map_spec = mask_2; map_spec; map_spec &= (map_spec - 1)) {
51899a2dd95SBruce Richardson 			i = rte_bsf64(map_spec);
51999a2dd95SBruce Richardson 			sa[idx_2[i]] = vals_2[i];
52099a2dd95SBruce Richardson 		}
52199a2dd95SBruce Richardson 	}
52299a2dd95SBruce Richardson 	/* Lookup for more specific rules in SPI_DIP_SIP table */
52399a2dd95SBruce Richardson 	if (n_3 != 0) {
52499a2dd95SBruce Richardson 		rte_hash_lookup_with_hash_bulk_data(
52599a2dd95SBruce Richardson 			sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP],
52699a2dd95SBruce Richardson 			keys_3, hash_sig_3, n_3, &mask_3, vals_3);
52799a2dd95SBruce Richardson 		for (map_spec = mask_3; map_spec; map_spec &= (map_spec - 1)) {
52899a2dd95SBruce Richardson 			i = rte_bsf64(map_spec);
52999a2dd95SBruce Richardson 			sa[idx_3[i]] = vals_3[i];
53099a2dd95SBruce Richardson 		}
53199a2dd95SBruce Richardson 	}
53299a2dd95SBruce Richardson 
53399a2dd95SBruce Richardson 	for (i = 0; i < n; i++)
53499a2dd95SBruce Richardson 		found += (sa[i] != NULL);
53599a2dd95SBruce Richardson 
53699a2dd95SBruce Richardson 	return found;
53799a2dd95SBruce Richardson }
53899a2dd95SBruce Richardson 
53999a2dd95SBruce Richardson int
rte_ipsec_sad_lookup(const struct rte_ipsec_sad * sad,const union rte_ipsec_sad_key * keys[],void * sa[],uint32_t n)54099a2dd95SBruce Richardson rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
54199a2dd95SBruce Richardson 		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
54299a2dd95SBruce Richardson {
54399a2dd95SBruce Richardson 	uint32_t num, i = 0;
54499a2dd95SBruce Richardson 	int found = 0;
54599a2dd95SBruce Richardson 
54699a2dd95SBruce Richardson 	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
54799a2dd95SBruce Richardson 		return -EINVAL;
54899a2dd95SBruce Richardson 
54999a2dd95SBruce Richardson 	do {
55099a2dd95SBruce Richardson 		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
55199a2dd95SBruce Richardson 		found += __ipsec_sad_lookup(sad,
55299a2dd95SBruce Richardson 			&keys[i], &sa[i], num);
55399a2dd95SBruce Richardson 		i += num;
55499a2dd95SBruce Richardson 	} while (i != n);
55599a2dd95SBruce Richardson 
55699a2dd95SBruce Richardson 	return found;
55799a2dd95SBruce Richardson }
558