]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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 | } |