ipsec: add SAD add/delete/lookup implementation
[dpdk.git] / lib / librte_ipsec / ipsec_sad.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019 Intel Corporation
3  */
4
5 #include <rte_eal_memconfig.h>
6 #include <rte_errno.h>
7 #include <rte_hash.h>
8 #include <rte_jhash.h>
9 #include <rte_malloc.h>
10 #include <rte_random.h>
11 #include <rte_rwlock.h>
12 #include <rte_tailq.h>
13
14 #include "rte_ipsec_sad.h"
15
16 /*
17  * Rules are stored in three hash tables depending on key_type.
18  * Each rule will also be stored in SPI_ONLY table.
19  * for each data entry within this table last two bits are reserved to
20  * indicate presence of entries with the same SPI in DIP and DIP+SIP tables.
21  */
22
23 #define IPSEC_SAD_NAMESIZE      64
24 #define SAD_PREFIX              "SAD_"
25 /* "SAD_<name>" */
26 #define SAD_FORMAT              SAD_PREFIX "%s"
27
28 #define DEFAULT_HASH_FUNC       rte_jhash
29 #define MIN_HASH_ENTRIES        8U /* From rte_cuckoo_hash.h */
30
31 struct hash_cnt {
32         uint32_t cnt_dip;
33         uint32_t cnt_dip_sip;
34 };
35
36 struct rte_ipsec_sad {
37         char name[IPSEC_SAD_NAMESIZE];
38         struct rte_hash *hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
39         /* Array to track number of more specific rules
40          * (spi_dip or spi_dip_sip). Used only in add/delete
41          * as a helper struct.
42          */
43         __extension__ struct hash_cnt cnt_arr[];
44 };
45
46 TAILQ_HEAD(rte_ipsec_sad_list, rte_tailq_entry);
47 static struct rte_tailq_elem rte_ipsec_sad_tailq = {
48         .name = "RTE_IPSEC_SAD",
49 };
50 EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq)
51
52 #define SET_BIT(ptr, bit)       (void *)((uintptr_t)(ptr) | (uintptr_t)(bit))
53 #define CLEAR_BIT(ptr, bit)     (void *)((uintptr_t)(ptr) & ~(uintptr_t)(bit))
54 #define GET_BIT(ptr, bit)       (void *)((uintptr_t)(ptr) & (uintptr_t)(bit))
55
56 /*
57  * @internal helper function
58  * Add a rule of type SPI_DIP or SPI_DIP_SIP.
59  * Inserts a rule into an appropriate hash table,
60  * updates the value for a given SPI in SPI_ONLY hash table
61  * reflecting presence of more specific rule type in two LSBs.
62  * Updates a counter that reflects the number of rules whith the same SPI.
63  */
64 static inline int
65 add_specific(struct rte_ipsec_sad *sad, const void *key,
66                 int key_type, void *sa)
67 {
68         void *tmp_val;
69         int ret, notexist;
70
71         /* Check if the key is present in the table.
72          * Need for further accaunting in cnt_arr
73          */
74         ret = rte_hash_lookup(sad->hash[key_type], key);
75         notexist = (ret == -ENOENT);
76
77         /* Add an SA to the corresponding table.*/
78         ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
79         if (ret != 0)
80                 return ret;
81
82         /* Check if there is an entry in SPI only table with the same SPI */
83         ret = rte_hash_lookup_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
84                 key, &tmp_val);
85         if (ret < 0)
86                 tmp_val = NULL;
87         tmp_val = SET_BIT(tmp_val, key_type);
88
89         /* Add an entry into SPI only table */
90         ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
91                 key, tmp_val);
92         if (ret != 0)
93                 return ret;
94
95         /* Update a counter for a given SPI */
96         ret = rte_hash_lookup(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key);
97         if (key_type == RTE_IPSEC_SAD_SPI_DIP)
98                 sad->cnt_arr[ret].cnt_dip += notexist;
99         else
100                 sad->cnt_arr[ret].cnt_dip_sip += notexist;
101
102         return 0;
103 }
104
105 int
106 rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
107                 const union rte_ipsec_sad_key *key,
108                 int key_type, void *sa)
109 {
110         void *tmp_val;
111         int ret;
112
113         if ((sad == NULL) || (key == NULL) || (sa == NULL) ||
114                         /* sa must be 4 byte aligned */
115                         (GET_BIT(sa, RTE_IPSEC_SAD_KEY_TYPE_MASK) != 0))
116                 return -EINVAL;
117
118         /*
119          * Rules are stored in three hash tables depending on key_type.
120          * All rules will also have an entry in SPI_ONLY table, with entry
121          * value's two LSB's also indicating presence of rule with this SPI
122          * in other tables.
123          */
124         switch (key_type) {
125         case(RTE_IPSEC_SAD_SPI_ONLY):
126                 ret = rte_hash_lookup_data(sad->hash[key_type],
127                         key, &tmp_val);
128                 if (ret >= 0)
129                         tmp_val = SET_BIT(sa, GET_BIT(tmp_val,
130                                 RTE_IPSEC_SAD_KEY_TYPE_MASK));
131                 else
132                         tmp_val = sa;
133                 ret = rte_hash_add_key_data(sad->hash[key_type],
134                         key, tmp_val);
135                 return ret;
136         case(RTE_IPSEC_SAD_SPI_DIP):
137         case(RTE_IPSEC_SAD_SPI_DIP_SIP):
138                 return add_specific(sad, key, key_type, sa);
139         default:
140                 return -EINVAL;
141         }
142 }
143
144 /*
145  * @internal helper function
146  * Delete a rule of type SPI_DIP or SPI_DIP_SIP.
147  * Deletes an entry from an appropriate hash table and decrements
148  * an entry counter for given SPI.
149  * If entry to remove is the last one with given SPI within the table,
150  * then it will also update related entry in SPI_ONLY table.
151  * Removes an entry from SPI_ONLY hash table if there no rule left
152  * for this SPI in any table.
153  */
154 static inline int
155 del_specific(struct rte_ipsec_sad *sad, const void *key, int key_type)
156 {
157         void *tmp_val;
158         int ret;
159         uint32_t *cnt;
160
161         /* Remove an SA from the corresponding table.*/
162         ret = rte_hash_del_key(sad->hash[key_type], key);
163         if (ret < 0)
164                 return ret;
165
166         /* Get an index of cnt_arr entry for a given SPI */
167         ret = rte_hash_lookup_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
168                 key, &tmp_val);
169         if (ret < 0)
170                 return ret;
171         cnt = (key_type == RTE_IPSEC_SAD_SPI_DIP) ?
172                         &sad->cnt_arr[ret].cnt_dip :
173                         &sad->cnt_arr[ret].cnt_dip_sip;
174         if (--(*cnt) != 0)
175                 return 0;
176
177         /* corresponding counter is 0, clear the bit indicating
178          * the presence of more specific rule for a given SPI.
179          */
180         tmp_val = CLEAR_BIT(tmp_val, key_type);
181
182         /* if there are no rules left with same SPI,
183          * remove an entry from SPI_only table
184          */
185         if (tmp_val == NULL)
186                 ret = rte_hash_del_key(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key);
187         else
188                 ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
189                         key, tmp_val);
190         if (ret < 0)
191                 return ret;
192         return 0;
193 }
194
195 int
196 rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
197                 const union rte_ipsec_sad_key *key,
198                 int key_type)
199 {
200         void *tmp_val;
201         int ret;
202
203         if ((sad == NULL) || (key == NULL))
204                 return -EINVAL;
205         switch (key_type) {
206         case(RTE_IPSEC_SAD_SPI_ONLY):
207                 ret = rte_hash_lookup_data(sad->hash[key_type],
208                         key, &tmp_val);
209                 if (ret < 0)
210                         return ret;
211                 if (GET_BIT(tmp_val, RTE_IPSEC_SAD_KEY_TYPE_MASK) == 0) {
212                         ret = rte_hash_del_key(sad->hash[key_type],
213                                 key);
214                         ret = ret < 0 ? ret : 0;
215                 } else {
216                         tmp_val = GET_BIT(tmp_val,
217                                 RTE_IPSEC_SAD_KEY_TYPE_MASK);
218                         ret = rte_hash_add_key_data(sad->hash[key_type],
219                                 key, tmp_val);
220                 }
221                 return ret;
222         case(RTE_IPSEC_SAD_SPI_DIP):
223         case(RTE_IPSEC_SAD_SPI_DIP_SIP):
224                 return del_specific(sad, key, key_type);
225         default:
226                 return -EINVAL;
227         }
228 }
229
230 struct rte_ipsec_sad *
231 rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
232 {
233         char hash_name[RTE_HASH_NAMESIZE];
234         char sad_name[IPSEC_SAD_NAMESIZE];
235         struct rte_tailq_entry *te;
236         struct rte_ipsec_sad_list *sad_list;
237         struct rte_ipsec_sad *sad, *tmp_sad = NULL;
238         struct rte_hash_parameters hash_params = {0};
239         int ret;
240         uint32_t sa_sum;
241
242         RTE_BUILD_BUG_ON(RTE_IPSEC_SAD_KEY_TYPE_MASK != 3);
243
244         if ((name == NULL) || (conf == NULL) ||
245                         ((conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY] == 0) &&
246                         (conf->max_sa[RTE_IPSEC_SAD_SPI_DIP] == 0) &&
247                         (conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] == 0))) {
248                 rte_errno = EINVAL;
249                 return NULL;
250         }
251
252         ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
253         if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
254                 rte_errno = ENAMETOOLONG;
255                 return NULL;
256         }
257
258         /** Init SAD*/
259         sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
260                 conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
261                 RTE_MAX(MIN_HASH_ENTRIES,
262                 conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
263                 RTE_MAX(MIN_HASH_ENTRIES,
264                 conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP]);
265         sad = rte_zmalloc_socket(NULL, sizeof(*sad) +
266                 (sizeof(struct hash_cnt) * sa_sum),
267                 RTE_CACHE_LINE_SIZE, conf->socket_id);
268         if (sad == NULL) {
269                 rte_errno = ENOMEM;
270                 return NULL;
271         }
272         memcpy(sad->name, sad_name, sizeof(sad_name));
273
274         hash_params.hash_func = DEFAULT_HASH_FUNC;
275         hash_params.hash_func_init_val = rte_rand();
276         hash_params.socket_id = conf->socket_id;
277         hash_params.name = hash_name;
278         if (conf->flags & RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY)
279                 hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
280
281         /** Init hash[RTE_IPSEC_SAD_SPI_ONLY] for SPI only */
282         snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
283         hash_params.key_len = sizeof(((struct rte_ipsec_sadv4_key *)0)->spi);
284         hash_params.entries = sa_sum;
285         sad->hash[RTE_IPSEC_SAD_SPI_ONLY] = rte_hash_create(&hash_params);
286         if (sad->hash[RTE_IPSEC_SAD_SPI_ONLY] == NULL) {
287                 rte_ipsec_sad_destroy(sad);
288                 return NULL;
289         }
290
291         /** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
292         snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
293         if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
294                 hash_params.key_len +=
295                         sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
296         else
297                 hash_params.key_len +=
298                         sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
299         hash_params.entries = RTE_MAX(MIN_HASH_ENTRIES,
300                         conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]);
301         sad->hash[RTE_IPSEC_SAD_SPI_DIP] = rte_hash_create(&hash_params);
302         if (sad->hash[RTE_IPSEC_SAD_SPI_DIP] == NULL) {
303                 rte_ipsec_sad_destroy(sad);
304                 return NULL;
305         }
306
307         /** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
308         snprintf(hash_name, sizeof(hash_name), "sad_3_%p", sad);
309         if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
310                 hash_params.key_len +=
311                         sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
312         else
313                 hash_params.key_len +=
314                         sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
315         hash_params.entries = RTE_MAX(MIN_HASH_ENTRIES,
316                         conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP]);
317         sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] = rte_hash_create(&hash_params);
318         if (sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] == NULL) {
319                 rte_ipsec_sad_destroy(sad);
320                 return NULL;
321         }
322
323         sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
324                         rte_ipsec_sad_list);
325         rte_mcfg_tailq_write_lock();
326         /* guarantee there's no existing */
327         TAILQ_FOREACH(te, sad_list, next) {
328                 tmp_sad = (struct rte_ipsec_sad *)te->data;
329                 if (strncmp(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
330                         break;
331         }
332         if (te != NULL) {
333                 rte_mcfg_tailq_write_unlock();
334                 rte_errno = EEXIST;
335                 rte_ipsec_sad_destroy(sad);
336                 return NULL;
337         }
338
339         /* allocate tailq entry */
340         te = rte_zmalloc("IPSEC_SAD_TAILQ_ENTRY", sizeof(*te), 0);
341         if (te == NULL) {
342                 rte_mcfg_tailq_write_unlock();
343                 rte_errno = ENOMEM;
344                 rte_ipsec_sad_destroy(sad);
345                 return NULL;
346         }
347
348         te->data = (void *)sad;
349         TAILQ_INSERT_TAIL(sad_list, te, next);
350         rte_mcfg_tailq_write_unlock();
351         return sad;
352 }
353
354 struct rte_ipsec_sad *
355 rte_ipsec_sad_find_existing(const char *name)
356 {
357         char sad_name[IPSEC_SAD_NAMESIZE];
358         struct rte_ipsec_sad *sad = NULL;
359         struct rte_tailq_entry *te;
360         struct rte_ipsec_sad_list *sad_list;
361         int ret;
362
363         ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
364         if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
365                 rte_errno = ENAMETOOLONG;
366                 return NULL;
367         }
368
369         sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
370                 rte_ipsec_sad_list);
371
372         rte_mcfg_tailq_read_lock();
373         TAILQ_FOREACH(te, sad_list, next) {
374                 sad = (struct rte_ipsec_sad *) te->data;
375                 if (strncmp(sad_name, sad->name, IPSEC_SAD_NAMESIZE) == 0)
376                         break;
377         }
378         rte_mcfg_tailq_read_unlock();
379
380         if (te == NULL) {
381                 rte_errno = ENOENT;
382                 return NULL;
383         }
384
385         return sad;
386 }
387
388 void
389 rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
390 {
391         struct rte_tailq_entry *te;
392         struct rte_ipsec_sad_list *sad_list;
393
394         if (sad == NULL)
395                 return;
396
397         sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
398                         rte_ipsec_sad_list);
399         rte_mcfg_tailq_write_lock();
400         TAILQ_FOREACH(te, sad_list, next) {
401                 if (te->data == (void *)sad)
402                         break;
403         }
404         if (te != NULL)
405                 TAILQ_REMOVE(sad_list, te, next);
406
407         rte_mcfg_tailq_write_unlock();
408
409         rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_ONLY]);
410         rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP]);
411         rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP]);
412         rte_free(sad);
413         if (te != NULL)
414                 rte_free(te);
415 }
416
417 /*
418  * @internal helper function
419  * Lookup a batch of keys in three hash tables.
420  * First lookup key in SPI_ONLY table.
421  * If there is an entry for the corresponding SPI check its value.
422  * Two least significant bits of the value indicate
423  * the presence of more specific rule in other tables.
424  * Perform additional lookup in corresponding hash tables
425  * and update the value if lookup succeeded.
426  */
427 static int
428 __ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
429                 const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
430 {
431         const void *keys_2[RTE_HASH_LOOKUP_BULK_MAX];
432         const void *keys_3[RTE_HASH_LOOKUP_BULK_MAX];
433         void *vals_2[RTE_HASH_LOOKUP_BULK_MAX] = {NULL};
434         void *vals_3[RTE_HASH_LOOKUP_BULK_MAX] = {NULL};
435         uint32_t idx_2[RTE_HASH_LOOKUP_BULK_MAX];
436         uint32_t idx_3[RTE_HASH_LOOKUP_BULK_MAX];
437         uint64_t mask_1, mask_2, mask_3;
438         uint64_t map, map_spec;
439         uint32_t n_2 = 0;
440         uint32_t n_3 = 0;
441         uint32_t i;
442         int found = 0;
443
444         for (i = 0; i < n; i++)
445                 sa[i] = NULL;
446
447         /*
448          * Lookup keys in SPI only hash table first.
449          */
450         rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
451                 (const void **)keys, n, &mask_1, sa);
452         for (map = mask_1; map; map &= (map - 1)) {
453                 i = rte_bsf64(map);
454                 /*
455                  * if returned value indicates presence of a rule in other
456                  * tables save a key for further lookup.
457                  */
458                 if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP_SIP) {
459                         idx_3[n_3] = i;
460                         keys_3[n_3++] = keys[i];
461                 }
462                 if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP) {
463                         idx_2[n_2] = i;
464                         keys_2[n_2++] = keys[i];
465                 }
466                 /* clear 2 LSB's which indicate the presence
467                  * of more specific rules
468                  */
469                 sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
470         }
471
472         /* Lookup for more specific rules in SPI_DIP table */
473         if (n_2 != 0) {
474                 rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_DIP],
475                         keys_2, n_2, &mask_2, vals_2);
476                 for (map_spec = mask_2; map_spec; map_spec &= (map_spec - 1)) {
477                         i = rte_bsf64(map_spec);
478                         sa[idx_2[i]] = vals_2[i];
479                 }
480         }
481         /* Lookup for more specific rules in SPI_DIP_SIP table */
482         if (n_3 != 0) {
483                 rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP],
484                         keys_3, n_3, &mask_3, vals_3);
485                 for (map_spec = mask_3; map_spec; map_spec &= (map_spec - 1)) {
486                         i = rte_bsf64(map_spec);
487                         sa[idx_3[i]] = vals_3[i];
488                 }
489         }
490
491         for (i = 0; i < n; i++)
492                 found += (sa[i] != NULL);
493
494         return found;
495 }
496
497 int
498 rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
499                 const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
500 {
501         uint32_t num, i = 0;
502         int found = 0;
503
504         if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
505                 return -EINVAL;
506
507         do {
508                 num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
509                 found += __ipsec_sad_lookup(sad,
510                         &keys[i], &sa[i], num);
511                 i += num;
512         } while (i != n);
513
514         return found;
515 }