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