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