From b2ee269267755f3784efcc5da3bf65e6f4f3dda4 Mon Sep 17 00:00:00 2001 From: Vladimir Medvedkin Date: Mon, 21 Oct 2019 15:35:44 +0100 Subject: [PATCH] ipsec: add SAD add/delete/lookup implementation Replace rte_ipsec_sad_add(), rte_ipsec_sad_del() and rte_ipsec_sad_lookup() stubs with actual implementation. It uses three librte_hash tables each of which contains an entries for a specific SA type (either it is addressed by SPI only or SPI+DIP or SPI+DIP+SIP) Signed-off-by: Vladimir Medvedkin Acked-by: Akhil Goyal Acked-by: Konstantin Ananyev Tested-by: Konstantin Ananyev --- doc/guides/prog_guide/ipsec_lib.rst | 110 +++++++++++ lib/librte_ipsec/ipsec_sad.c | 278 ++++++++++++++++++++++++++-- 2 files changed, 376 insertions(+), 12 deletions(-) diff --git a/doc/guides/prog_guide/ipsec_lib.rst b/doc/guides/prog_guide/ipsec_lib.rst index f69019e5f5..1ce0db4530 100644 --- a/doc/guides/prog_guide/ipsec_lib.rst +++ b/doc/guides/prog_guide/ipsec_lib.rst @@ -186,6 +186,116 @@ As an example: for more information please refer to ipsec library API reference +Add/delete rules +~~~~~~~~~~~~~~~~ + +Library also provides methods to add or delete key/value pairs from the SAD. +To add user has to specify key, key type and a value which is an opaque pointer to SA. +The key type reflects a set of tuple fields that will be used for lookup of the SA. +As mentioned above there are 3 types of a key and the representation of a key type is: + +.. code-block:: c + + RTE_IPSEC_SAD_SPI_ONLY, + RTE_IPSEC_SAD_SPI_DIP, + RTE_IPSEC_SAD_SPI_DIP_SIP, + +As an example, to add new entry into the SAD for IPv4 addresses: + +.. code-block:: c + + struct rte_ipsec_sa *sa; + union rte_ipsec_sad_key key; + + key.v4.spi = rte_cpu_to_be_32(spi_val); + if (key_type >= RTE_IPSEC_SAD_SPI_DIP) /* DIP is optional*/ + key.v4.dip = rte_cpu_to_be_32(dip_val); + if (key_type == RTE_IPSEC_SAD_SPI_DIP_SIP) /* SIP is optional*/ + key.v4.sip = rte_cpu_to_be_32(sip_val); + + rte_ipsec_sad_add(sad, &key, key_type, sa); + +.. note:: + + By performance reason it is better to keep spi/dip/sip in net byte order + to eliminate byteswap on lookup + +To delete user has to specify key and key type. + +Delete code would look like: + +.. code-block:: c + + union rte_ipsec_sad_key key; + + key.v4.spi = rte_cpu_to_be_32(necessary_spi); + if (key_type >= RTE_IPSEC_SAD_SPI_DIP) /* DIP is optional*/ + key.v4.dip = rte_cpu_to_be_32(necessary_dip); + if (key_type == RTE_IPSEC_SAD_SPI_DIP_SIP) /* SIP is optional*/ + key.v4.sip = rte_cpu_to_be_32(necessary_sip); + + rte_ipsec_sad_del(sad, &key, key_type); + + +Lookup +~~~~~~ +Library provides lookup by the given {SPI,DIP,SIP} tuple of +inbound ipsec packet as a key. + +The search key is represented by: + +.. code-block:: c + + union rte_ipsec_sad_key { + struct rte_ipsec_sadv4_key v4; + struct rte_ipsec_sadv6_key v6; + }; + +where v4 is a tuple for IPv4: + +.. code-block:: c + + struct rte_ipsec_sadv4_key { + uint32_t spi; + uint32_t dip; + uint32_t sip; + }; + +and v6 is a tuple for IPv6: + +.. code-block:: c + + struct rte_ipsec_sadv6_key { + uint32_t spi; + uint8_t dip[16]; + uint8_t sip[16]; + }; + +As an example, lookup related code could look like that: + +.. code-block:: c + + int i; + union rte_ipsec_sad_key keys[BURST_SZ]; + const union rte_ipsec_sad_key *keys_p[BURST_SZ]; + void *vals[BURST_SZ]; + + for (i = 0; i < BURST_SZ_MAX; i++) { + keys[i].v4.spi = esp_hdr[i]->spi; + keys[i].v4.dip = ipv4_hdr[i]->dst_addr; + keys[i].v4.sip = ipv4_hdr[i]->src_addr; + keys_p[i] = &keys[i]; + } + rte_ipsec_sad_lookup(sad, keys_p, vals, BURST_SZ); + + for (i = 0; i < BURST_SZ_MAX; i++) { + if (vals[i] == NULL) + printf("SA not found for key index %d\n", i); + else + printf("SA pointer is %p\n", vals[i]); + } + + Supported features ------------------ diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c index e16c7d94f7..db2c44c804 100644 --- a/lib/librte_ipsec/ipsec_sad.c +++ b/lib/librte_ipsec/ipsec_sad.c @@ -49,20 +49,182 @@ static struct rte_tailq_elem rte_ipsec_sad_tailq = { }; EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq) +#define SET_BIT(ptr, bit) (void *)((uintptr_t)(ptr) | (uintptr_t)(bit)) +#define CLEAR_BIT(ptr, bit) (void *)((uintptr_t)(ptr) & ~(uintptr_t)(bit)) +#define GET_BIT(ptr, bit) (void *)((uintptr_t)(ptr) & (uintptr_t)(bit)) + +/* + * @internal helper function + * Add a rule of type SPI_DIP or SPI_DIP_SIP. + * Inserts a rule into an appropriate hash table, + * updates the value for a given SPI in SPI_ONLY hash table + * reflecting presence of more specific rule type in two LSBs. + * Updates a counter that reflects the number of rules whith the same SPI. + */ +static inline int +add_specific(struct rte_ipsec_sad *sad, const void *key, + int key_type, void *sa) +{ + void *tmp_val; + int ret, notexist; + + /* Check if the key is present in the table. + * Need for further accaunting in cnt_arr + */ + ret = rte_hash_lookup(sad->hash[key_type], key); + notexist = (ret == -ENOENT); + + /* Add an SA to the corresponding table.*/ + ret = rte_hash_add_key_data(sad->hash[key_type], key, sa); + if (ret != 0) + return ret; + + /* Check if there is an entry in SPI only table with the same SPI */ + ret = rte_hash_lookup_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], + key, &tmp_val); + if (ret < 0) + tmp_val = NULL; + tmp_val = SET_BIT(tmp_val, key_type); + + /* Add an entry into SPI only table */ + ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], + key, tmp_val); + if (ret != 0) + return ret; + + /* Update a counter for a given SPI */ + ret = rte_hash_lookup(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key); + if (key_type == RTE_IPSEC_SAD_SPI_DIP) + sad->cnt_arr[ret].cnt_dip += notexist; + else + sad->cnt_arr[ret].cnt_dip_sip += notexist; + + return 0; +} + int -rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad, - __rte_unused const union rte_ipsec_sad_key *key, - __rte_unused int key_type, __rte_unused void *sa) +rte_ipsec_sad_add(struct rte_ipsec_sad *sad, + const union rte_ipsec_sad_key *key, + int key_type, void *sa) { - return -ENOTSUP; + void *tmp_val; + int ret; + + if ((sad == NULL) || (key == NULL) || (sa == NULL) || + /* sa must be 4 byte aligned */ + (GET_BIT(sa, RTE_IPSEC_SAD_KEY_TYPE_MASK) != 0)) + return -EINVAL; + + /* + * Rules are stored in three hash tables depending on key_type. + * All rules will also have an entry in SPI_ONLY table, with entry + * value's two LSB's also indicating presence of rule with this SPI + * in other tables. + */ + switch (key_type) { + case(RTE_IPSEC_SAD_SPI_ONLY): + ret = rte_hash_lookup_data(sad->hash[key_type], + key, &tmp_val); + if (ret >= 0) + tmp_val = SET_BIT(sa, GET_BIT(tmp_val, + RTE_IPSEC_SAD_KEY_TYPE_MASK)); + else + tmp_val = sa; + ret = rte_hash_add_key_data(sad->hash[key_type], + key, tmp_val); + return ret; + case(RTE_IPSEC_SAD_SPI_DIP): + case(RTE_IPSEC_SAD_SPI_DIP_SIP): + return add_specific(sad, key, key_type, sa); + default: + return -EINVAL; + } +} + +/* + * @internal helper function + * Delete a rule of type SPI_DIP or SPI_DIP_SIP. + * Deletes an entry from an appropriate hash table and decrements + * an entry counter for given SPI. + * If entry to remove is the last one with given SPI within the table, + * then it will also update related entry in SPI_ONLY table. + * Removes an entry from SPI_ONLY hash table if there no rule left + * for this SPI in any table. + */ +static inline int +del_specific(struct rte_ipsec_sad *sad, const void *key, int key_type) +{ + void *tmp_val; + int ret; + uint32_t *cnt; + + /* Remove an SA from the corresponding table.*/ + ret = rte_hash_del_key(sad->hash[key_type], key); + if (ret < 0) + return ret; + + /* Get an index of cnt_arr entry for a given SPI */ + ret = rte_hash_lookup_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], + key, &tmp_val); + if (ret < 0) + return ret; + cnt = (key_type == RTE_IPSEC_SAD_SPI_DIP) ? + &sad->cnt_arr[ret].cnt_dip : + &sad->cnt_arr[ret].cnt_dip_sip; + if (--(*cnt) != 0) + return 0; + + /* corresponding counter is 0, clear the bit indicating + * the presence of more specific rule for a given SPI. + */ + tmp_val = CLEAR_BIT(tmp_val, key_type); + + /* if there are no rules left with same SPI, + * remove an entry from SPI_only table + */ + if (tmp_val == NULL) + ret = rte_hash_del_key(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key); + else + ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], + key, tmp_val); + if (ret < 0) + return ret; + return 0; } int -rte_ipsec_sad_del(__rte_unused struct rte_ipsec_sad *sad, - __rte_unused const union rte_ipsec_sad_key *key, - __rte_unused int key_type) +rte_ipsec_sad_del(struct rte_ipsec_sad *sad, + const union rte_ipsec_sad_key *key, + int key_type) { - return -ENOTSUP; + void *tmp_val; + int ret; + + if ((sad == NULL) || (key == NULL)) + return -EINVAL; + switch (key_type) { + case(RTE_IPSEC_SAD_SPI_ONLY): + ret = rte_hash_lookup_data(sad->hash[key_type], + key, &tmp_val); + if (ret < 0) + return ret; + if (GET_BIT(tmp_val, RTE_IPSEC_SAD_KEY_TYPE_MASK) == 0) { + ret = rte_hash_del_key(sad->hash[key_type], + key); + ret = ret < 0 ? ret : 0; + } else { + tmp_val = GET_BIT(tmp_val, + RTE_IPSEC_SAD_KEY_TYPE_MASK); + ret = rte_hash_add_key_data(sad->hash[key_type], + key, tmp_val); + } + return ret; + case(RTE_IPSEC_SAD_SPI_DIP): + case(RTE_IPSEC_SAD_SPI_DIP_SIP): + return del_specific(sad, key, key_type); + default: + return -EINVAL; + } } struct rte_ipsec_sad * @@ -252,10 +414,102 @@ rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad) rte_free(te); } +/* + * @internal helper function + * Lookup a batch of keys in three hash tables. + * First lookup key in SPI_ONLY table. + * If there is an entry for the corresponding SPI check its value. + * Two least significant bits of the value indicate + * the presence of more specific rule in other tables. + * Perform additional lookup in corresponding hash tables + * and update the value if lookup succeeded. + */ +static int +__ipsec_sad_lookup(const struct rte_ipsec_sad *sad, + const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n) +{ + const void *keys_2[RTE_HASH_LOOKUP_BULK_MAX]; + const void *keys_3[RTE_HASH_LOOKUP_BULK_MAX]; + void *vals_2[RTE_HASH_LOOKUP_BULK_MAX] = {NULL}; + void *vals_3[RTE_HASH_LOOKUP_BULK_MAX] = {NULL}; + uint32_t idx_2[RTE_HASH_LOOKUP_BULK_MAX]; + uint32_t idx_3[RTE_HASH_LOOKUP_BULK_MAX]; + uint64_t mask_1, mask_2, mask_3; + uint64_t map, map_spec; + uint32_t n_2 = 0; + uint32_t n_3 = 0; + uint32_t i; + int found = 0; + + for (i = 0; i < n; i++) + sa[i] = NULL; + + /* + * Lookup keys in SPI only hash table first. + */ + rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], + (const void **)keys, n, &mask_1, sa); + for (map = mask_1; map; map &= (map - 1)) { + i = rte_bsf64(map); + /* + * if returned value indicates presence of a rule in other + * tables save a key for further lookup. + */ + if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP_SIP) { + idx_3[n_3] = i; + keys_3[n_3++] = keys[i]; + } + if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP) { + idx_2[n_2] = i; + keys_2[n_2++] = keys[i]; + } + /* clear 2 LSB's which indicate the presence + * of more specific rules + */ + sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK); + } + + /* Lookup for more specific rules in SPI_DIP table */ + if (n_2 != 0) { + rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_DIP], + keys_2, n_2, &mask_2, vals_2); + for (map_spec = mask_2; map_spec; map_spec &= (map_spec - 1)) { + i = rte_bsf64(map_spec); + sa[idx_2[i]] = vals_2[i]; + } + } + /* Lookup for more specific rules in SPI_DIP_SIP table */ + if (n_3 != 0) { + rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP], + keys_3, n_3, &mask_3, vals_3); + for (map_spec = mask_3; map_spec; map_spec &= (map_spec - 1)) { + i = rte_bsf64(map_spec); + sa[idx_3[i]] = vals_3[i]; + } + } + + for (i = 0; i < n; i++) + found += (sa[i] != NULL); + + return found; +} + int -rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad, - __rte_unused const union rte_ipsec_sad_key *keys[], - __rte_unused void *sa[], __rte_unused uint32_t n) +rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad, + const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n) { - return -ENOTSUP; + uint32_t num, i = 0; + int found = 0; + + if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL))) + return -EINVAL; + + do { + num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX); + found += __ipsec_sad_lookup(sad, + &keys[i], &sa[i], num); + i += num; + } while (i != n); + + return found; } -- 2.20.1