xref: /dpdk/lib/ipsec/ipsec_sad.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
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 }
560