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