crypto: make crypto-dispatch node working in adaptive mode
[vpp.git] / src / vnet / ipsec / ipsec_spd_fp_lookup.h
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2022 Intel and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #ifndef IPSEC_SPD_FP_LOOKUP_H
19 #define IPSEC_SPD_FP_LOOKUP_H
20
21 #include <vnet/ipsec/ipsec.h>
22
23 static_always_inline int
24 single_rule_out_match_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *match)
25 {
26   if (PREDICT_FALSE (policy->is_ipv6 != match->is_ipv6))
27     return (0);
28
29   if (PREDICT_FALSE (policy->protocol != IPSEC_POLICY_PROTOCOL_ANY &&
30                      (policy->protocol != match->protocol)))
31     return (0);
32
33   if (!policy->is_ipv6)
34     {
35       if (PREDICT_FALSE (
36             clib_net_to_host_u32 (match->laddr.as_u32) <
37             clib_net_to_host_u32 (policy->laddr.start.ip4.as_u32)))
38         return (0);
39
40       if (PREDICT_FALSE (clib_net_to_host_u32 (match->laddr.as_u32) >
41                          clib_net_to_host_u32 (policy->laddr.stop.ip4.as_u32)))
42         return (0);
43
44       if (PREDICT_FALSE (
45             clib_net_to_host_u32 (match->raddr.as_u32) <
46             clib_net_to_host_u32 (policy->raddr.start.ip4.as_u32)))
47         return (0);
48
49       if (PREDICT_FALSE (clib_net_to_host_u32 (match->raddr.as_u32) >
50                          clib_net_to_host_u32 (policy->raddr.stop.ip4.as_u32)))
51         return (0);
52     }
53   else
54     {
55
56       if (ip6_address_compare (&match->ip6_laddr, &policy->laddr.start.ip6) <
57           0)
58         return (0);
59
60       if (ip6_address_compare (&policy->laddr.stop.ip6, &match->ip6_laddr) < 0)
61
62         return (0);
63
64       if (ip6_address_compare (&match->ip6_raddr, &policy->raddr.start.ip6) <
65           0)
66
67         return (0);
68
69       if (ip6_address_compare (&policy->raddr.stop.ip6, &match->ip6_raddr) < 0)
70
71         return (0);
72     }
73
74   if (PREDICT_FALSE ((match->protocol != IP_PROTOCOL_TCP) &&
75                      (match->protocol != IP_PROTOCOL_UDP) &&
76                      (match->protocol != IP_PROTOCOL_SCTP)))
77     {
78       return (1);
79     }
80
81   if (match->lport < policy->lport.start)
82     return (0);
83
84   if (match->lport > policy->lport.stop)
85     return (0);
86
87   if (match->rport < policy->rport.start)
88     return (0);
89
90   if (match->rport > policy->rport.stop)
91     return (0);
92
93   return (1);
94 }
95
96 static_always_inline int
97 single_rule_in_match_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *match)
98 {
99
100   u32 da = clib_net_to_host_u32 (match->laddr.as_u32);
101   u32 sa = clib_net_to_host_u32 (match->raddr.as_u32);
102
103   if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
104     {
105       ipsec_sa_t *s = ipsec_sa_get (policy->sa_index);
106
107       if (match->spi != s->spi)
108         return (0);
109
110       if (ipsec_sa_is_set_IS_TUNNEL (s))
111         {
112           if (da != clib_net_to_host_u32 (s->tunnel.t_dst.ip.ip4.as_u32))
113             return (0);
114
115           if (sa != clib_net_to_host_u32 (s->tunnel.t_src.ip.ip4.as_u32))
116             return (0);
117         }
118     }
119   else
120     {
121       if (sa < clib_net_to_host_u32 (policy->raddr.start.ip4.as_u32))
122         return (0);
123
124       if (sa > clib_net_to_host_u32 (policy->raddr.stop.ip4.as_u32))
125         return (0);
126
127       if (da < clib_net_to_host_u32 (policy->laddr.start.ip4.as_u32))
128         return (0);
129
130       if (da > clib_net_to_host_u32 (policy->laddr.stop.ip4.as_u32))
131         return (0);
132     }
133   return (1);
134 }
135
136 static_always_inline u32
137 ipsec_fp_in_ip6_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
138                                 ipsec_policy_t **policies, u32 n)
139 {
140   u32 last_priority[n];
141   u32 i = 0;
142   u32 counter = 0;
143   ipsec_fp_mask_type_entry_t *mte;
144   ipsec_fp_mask_id_t *mti;
145   ipsec_fp_5tuple_t *match = tuples;
146   ipsec_policy_t *policy;
147   u32 n_left = n;
148   clib_bihash_kv_40_8_t kv;
149   /* result of the lookup */
150   clib_bihash_kv_40_8_t result;
151   ipsec_fp_lookup_value_t *result_val =
152     (ipsec_fp_lookup_value_t *) &result.value;
153   u64 *pkey, *pmatch, *pmask;
154   ipsec_main_t *im = &ipsec_main;
155   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
156   ipsec_fp_mask_id_t *mask_type_ids = pspd_fp->fp_mask_ids[match->action];
157   clib_bihash_40_8_t *bihash_table = pool_elt_at_index (
158     im->fp_ip6_lookup_hashes_pool, pspd_fp->ip6_in_lookup_hash_idx);
159
160   /* clear the list of matched policies pointers */
161   clib_memset (policies, 0, n * sizeof (*policies));
162   clib_memset (last_priority, 0, n * sizeof (u32));
163   n_left = n;
164   while (n_left)
165     {
166       vec_foreach (mti, mask_type_ids)
167         {
168           mte = im->fp_mask_types + mti->mask_type_idx;
169           if (mte->mask.action == 0)
170             continue;
171
172           pmatch = (u64 *) match->kv_40_8.key;
173           pmask = (u64 *) mte->mask.kv_40_8.key;
174           pkey = (u64 *) kv.key;
175
176           *pkey++ = *pmatch++ & *pmask++;
177           *pkey++ = *pmatch++ & *pmask++;
178           *pkey++ = *pmatch++ & *pmask++;
179           *pkey++ = *pmatch++ & *pmask++;
180           *pkey = *pmatch & *pmask;
181
182           int res =
183             clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
184           /* lookup the hash by each packet in the burst for this mask. */
185
186           if (res == 0)
187             {
188               /* There is a hit in the hash table. */
189               /* Find the policy with highest priority. */
190               /* Store the lookup results in a dedicated array. */
191
192               if (vec_len (result_val->fp_policies_ids) > 1)
193                 {
194                   u32 *policy_id;
195                   vec_foreach (policy_id, result_val->fp_policies_ids)
196                     {
197                       policy = im->policies + *policy_id;
198
199                       if ((last_priority[i] < policy->priority) &&
200                           (single_rule_in_match_5tuple (policy, match)))
201                         {
202                           last_priority[i] = policy->priority;
203                           if (policies[i] == 0)
204                             counter++;
205                           policies[i] = policy;
206                         }
207                     }
208                 }
209               else
210                 {
211                   u32 *policy_id;
212                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
213                   policy_id = result_val->fp_policies_ids;
214                   policy = im->policies + *policy_id;
215                   if ((last_priority[i] < policy->priority) &&
216                       (single_rule_in_match_5tuple (policy, match)))
217                     {
218                       last_priority[i] = policy->priority;
219                       if (policies[i] == 0)
220                         counter++;
221                       policies[i] = policy;
222                     }
223                 }
224             }
225         }
226
227       i++;
228       n_left--;
229       match++;
230     }
231   return counter;
232 }
233
234 static_always_inline u32
235 ipsec_fp_in_ip4_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
236                                 ipsec_policy_t **policies, u32 n)
237
238 {
239   u32 last_priority[n];
240   u32 i = 0;
241   u32 counter = 0;
242   ipsec_fp_mask_type_entry_t *mte;
243   ipsec_fp_mask_id_t *mti;
244   ipsec_fp_5tuple_t *match = tuples;
245   ipsec_policy_t *policy;
246   u32 n_left = n;
247   clib_bihash_kv_16_8_t kv;
248   /* result of the lookup */
249   clib_bihash_kv_16_8_t result;
250   ipsec_fp_lookup_value_t *result_val =
251     (ipsec_fp_lookup_value_t *) &result.value;
252   u64 *pkey, *pmatch, *pmask;
253   ipsec_main_t *im = &ipsec_main;
254   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
255   ipsec_fp_mask_id_t *mask_type_ids = pspd_fp->fp_mask_ids[match->action];
256   clib_bihash_16_8_t *bihash_table = pool_elt_at_index (
257     im->fp_ip4_lookup_hashes_pool, pspd_fp->ip4_in_lookup_hash_idx);
258
259   /* clear the list of matched policies pointers */
260   clib_memset (policies, 0, n * sizeof (*policies));
261   clib_memset (last_priority, 0, n * sizeof (u32));
262   n_left = n;
263   while (n_left)
264     {
265       vec_foreach (mti, mask_type_ids)
266         {
267           mte = im->fp_mask_types + mti->mask_type_idx;
268           if (mte->mask.action == 0)
269             continue;
270           pmatch = (u64 *) match->kv_16_8.key;
271           pmask = (u64 *) mte->mask.kv_16_8.key;
272           pkey = (u64 *) kv.key;
273
274           *pkey++ = *pmatch++ & *pmask++;
275           *pkey = *pmatch & *pmask;
276
277           int res =
278             clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
279           /* lookup the hash by each packet in the burst for this mask. */
280
281           if (res == 0)
282             {
283               /* There is a hit in the hash table. */
284               /* Find the policy with highest priority. */
285               /* Store the lookup results in a dedicated array. */
286
287               if (vec_len (result_val->fp_policies_ids) > 1)
288                 {
289                   u32 *policy_id;
290                   vec_foreach (policy_id, result_val->fp_policies_ids)
291                     {
292                       policy = im->policies + *policy_id;
293
294                       if ((last_priority[i] < policy->priority) &&
295                           (single_rule_in_match_5tuple (policy, match)))
296                         {
297                           last_priority[i] = policy->priority;
298                           if (policies[i] == 0)
299                             counter++;
300                           policies[i] = policy;
301                         }
302                     }
303                 }
304               else
305                 {
306                   u32 *policy_id;
307                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
308                   policy_id = result_val->fp_policies_ids;
309                   policy = im->policies + *policy_id;
310                   if ((last_priority[i] < policy->priority) &&
311                       (single_rule_in_match_5tuple (policy, match)))
312                     {
313                       last_priority[i] = policy->priority;
314                       if (policies[i] == 0)
315                         counter++;
316                       policies[i] = policy;
317                     }
318                 }
319             }
320         }
321
322       i++;
323       n_left--;
324       match++;
325     }
326   return counter;
327 }
328
329 /**
330  * @brief function handler to perform lookup in fastpath SPD
331  * for inbound traffic burst of n packets
332  **/
333
334 static_always_inline u32
335 ipsec_fp_in_policy_match_n (void *spd_fp, u8 is_ipv6,
336                             ipsec_fp_5tuple_t *tuples,
337                             ipsec_policy_t **policies, u32 n)
338 {
339   if (is_ipv6)
340     return ipsec_fp_in_ip6_policy_match_n (spd_fp, tuples, policies, n);
341   else
342     return ipsec_fp_in_ip4_policy_match_n (spd_fp, tuples, policies, n);
343 }
344
345 static_always_inline u32
346 ipsec_fp_out_ip6_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
347                                  ipsec_policy_t **policies, u32 *ids, u32 n)
348
349 {
350   u32 last_priority[n];
351   u32 i = 0;
352   u32 counter = 0;
353   ipsec_fp_mask_type_entry_t *mte;
354   ipsec_fp_mask_id_t *mti;
355   ipsec_fp_5tuple_t *match = tuples;
356   ipsec_policy_t *policy;
357
358   u32 n_left = n;
359   clib_bihash_kv_40_8_t kv;
360   /* result of the lookup */
361   clib_bihash_kv_40_8_t result;
362   ipsec_fp_lookup_value_t *result_val =
363     (ipsec_fp_lookup_value_t *) &result.value;
364   u64 *pkey, *pmatch, *pmask;
365   ipsec_main_t *im = &ipsec_main;
366   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
367   ipsec_fp_mask_id_t *mask_type_ids =
368     pspd_fp->fp_mask_ids[IPSEC_SPD_POLICY_IP6_OUTBOUND];
369   clib_bihash_40_8_t *bihash_table = pool_elt_at_index (
370     im->fp_ip6_lookup_hashes_pool, pspd_fp->ip6_out_lookup_hash_idx);
371
372   /*clear the list of matched policies pointers */
373   clib_memset (policies, 0, n * sizeof (*policies));
374   clib_memset (last_priority, 0, n * sizeof (u32));
375   n_left = n;
376   while (n_left)
377     {
378       vec_foreach (mti, mask_type_ids)
379         {
380           mte = im->fp_mask_types + mti->mask_type_idx;
381           if (mte->mask.action != 0)
382             continue;
383
384           pmatch = (u64 *) match->kv_40_8.key;
385           pmask = (u64 *) mte->mask.kv_40_8.key;
386           pkey = (u64 *) kv.key;
387
388           *pkey++ = *pmatch++ & *pmask++;
389           *pkey++ = *pmatch++ & *pmask++;
390           *pkey++ = *pmatch++ & *pmask++;
391           *pkey++ = *pmatch++ & *pmask++;
392           *pkey = *pmatch & *pmask;
393
394           int res =
395             clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
396           /* lookup the hash by each packet in the burst for this mask. */
397
398           if (res == 0)
399             {
400               /* There is a hit in the hash table. */
401               /* Find the policy with highest priority. */
402               /* Store the lookup results in a dedicated array. */
403
404               if (vec_len (result_val->fp_policies_ids) > 1)
405                 {
406                   u32 *policy_id;
407                   vec_foreach (policy_id, result_val->fp_policies_ids)
408                     {
409                       policy = im->policies + *policy_id;
410
411                       if (single_rule_out_match_5tuple (policy, match))
412                         {
413                           if (last_priority[i] < policy->priority)
414                             {
415                               last_priority[i] = policy->priority;
416                               if (policies[i] == 0)
417                                 counter++;
418                               policies[i] = policy;
419                               ids[i] = *policy_id;
420                             }
421                         }
422                     }
423                 }
424               else
425                 {
426                   u32 *policy_id;
427                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
428                   policy_id = result_val->fp_policies_ids;
429                   policy = im->policies + *policy_id;
430                   if (single_rule_out_match_5tuple (policy, match))
431                     {
432                       if (last_priority[i] < policy->priority)
433                         {
434                           last_priority[i] = policy->priority;
435                           if (policies[i] == 0)
436                             counter++;
437                           policies[i] = policy;
438                           ids[i] = *policy_id;
439                         }
440                     }
441                 }
442             }
443         }
444       n_left--;
445       match++;
446       i++;
447     }
448   return counter;
449 }
450
451 static_always_inline u32
452 ipsec_fp_out_ip4_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
453                                  ipsec_policy_t **policies, u32 *ids, u32 n)
454
455 {
456   u32 last_priority[n];
457   u32 i = 0;
458   u32 counter = 0;
459   ipsec_fp_mask_type_entry_t *mte;
460   ipsec_fp_mask_id_t *mti;
461   ipsec_fp_5tuple_t *match = tuples;
462   ipsec_policy_t *policy;
463
464   u32 n_left = n;
465   clib_bihash_kv_16_8_t kv;
466   /* result of the lookup */
467   clib_bihash_kv_16_8_t result;
468   ipsec_fp_lookup_value_t *result_val =
469     (ipsec_fp_lookup_value_t *) &result.value;
470   u64 *pkey, *pmatch, *pmask;
471   ipsec_main_t *im = &ipsec_main;
472   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
473   ipsec_fp_mask_id_t *mask_type_ids =
474     pspd_fp->fp_mask_ids[IPSEC_SPD_POLICY_IP4_OUTBOUND];
475   clib_bihash_16_8_t *bihash_table = pool_elt_at_index (
476     im->fp_ip4_lookup_hashes_pool, pspd_fp->ip4_out_lookup_hash_idx);
477
478   /* clear the list of matched policies pointers */
479   clib_memset (policies, 0, n * sizeof (*policies));
480   clib_memset (last_priority, 0, n * sizeof (u32));
481   n_left = n;
482   while (n_left)
483     {
484       vec_foreach (mti, mask_type_ids)
485         {
486           mte = im->fp_mask_types + mti->mask_type_idx;
487           if (mte->mask.action != 0)
488             continue;
489
490           pmatch = (u64 *) match->kv_16_8.key;
491           pmask = (u64 *) mte->mask.kv_16_8.key;
492           pkey = (u64 *) kv.key;
493
494           *pkey++ = *pmatch++ & *pmask++;
495           *pkey = *pmatch & *pmask;
496
497           int res =
498             clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
499           /* lookup the hash by each packet in the burst for this mask. */
500
501           if (res == 0)
502             {
503               /* There is a hit in the hash table. */
504               /* Find the policy with highest priority. */
505               /* Store the lookup results in a dedicated array. */
506
507               if (vec_len (result_val->fp_policies_ids) > 1)
508                 {
509                   u32 *policy_id;
510                   vec_foreach (policy_id, result_val->fp_policies_ids)
511                     {
512                       policy = im->policies + *policy_id;
513
514                       if ((last_priority[i] < policy->priority) &&
515                           (single_rule_out_match_5tuple (policy, match)))
516                         {
517                           last_priority[i] = policy->priority;
518                           if (policies[i] == 0)
519                             counter++;
520                           policies[i] = policy;
521                           ids[i] = *policy_id;
522                         }
523                     }
524                 }
525               else
526                 {
527                   u32 *policy_id;
528                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
529                   policy_id = result_val->fp_policies_ids;
530                   policy = im->policies + *policy_id;
531                   if ((last_priority[i] < policy->priority) &&
532                       (single_rule_out_match_5tuple (policy, match)))
533                     {
534                       last_priority[i] = policy->priority;
535                       if (policies[i] == 0)
536                         counter++;
537                       policies[i] = policy;
538                       ids[i] = *policy_id;
539                     }
540                 }
541             }
542         }
543
544       i++;
545       n_left--;
546       match++;
547     }
548   return counter;
549 }
550
551 /**
552  * @brief function handler to perform lookup in fastpath SPD
553  * for outbound traffic burst of n packets
554  * returns number of successfully matched policies
555  **/
556
557 static_always_inline u32
558 ipsec_fp_out_policy_match_n (void *spd_fp, u8 is_ipv6,
559                              ipsec_fp_5tuple_t *tuples,
560                              ipsec_policy_t **policies, u32 *ids, u32 n)
561
562 {
563   if (is_ipv6)
564     return ipsec_fp_out_ip6_policy_match_n (spd_fp, tuples, policies, ids, n);
565   else
566     return ipsec_fp_out_ip4_policy_match_n (spd_fp, tuples, policies, ids, n);
567 }
568
569 #endif /* !IPSEC_SPD_FP_LOOKUP_H */