ip6: fix icmp throttling error index
[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 (single_rule_in_match_5tuple (policy, match))
200                         {
201                           if (last_priority[i] < policy->priority)
202                             {
203                               last_priority[i] = policy->priority;
204                               if (policies[i] == 0)
205                                 counter++;
206                               policies[i] = policy;
207                             }
208                           break;
209                         }
210                     }
211                 }
212               else
213                 {
214                   u32 *policy_id;
215                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
216                   policy_id = result_val->fp_policies_ids;
217                   policy = im->policies + *policy_id;
218                   if ((last_priority[i] < policy->priority) &&
219                       (single_rule_in_match_5tuple (policy, match)))
220                     {
221                       last_priority[i] = policy->priority;
222                       if (policies[i] == 0)
223                         counter++;
224                       policies[i] = policy;
225                     }
226                 }
227             }
228         }
229
230       i++;
231       n_left--;
232       match++;
233     }
234   return counter;
235 }
236
237 static_always_inline u32
238 ipsec_fp_in_ip4_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
239                                 ipsec_policy_t **policies, u32 n)
240
241 {
242   u32 last_priority[n];
243   u32 i = 0;
244   u32 counter = 0;
245   ipsec_fp_mask_type_entry_t *mte;
246   ipsec_fp_mask_id_t *mti;
247   ipsec_fp_5tuple_t *match = tuples;
248   ipsec_policy_t *policy;
249   u32 n_left = n;
250   clib_bihash_kv_16_8_t kv;
251   /* result of the lookup */
252   clib_bihash_kv_16_8_t result;
253   ipsec_fp_lookup_value_t *result_val =
254     (ipsec_fp_lookup_value_t *) &result.value;
255   u64 *pkey, *pmatch, *pmask;
256   ipsec_main_t *im = &ipsec_main;
257   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
258   ipsec_fp_mask_id_t *mask_type_ids = pspd_fp->fp_mask_ids[match->action];
259   clib_bihash_16_8_t *bihash_table = pool_elt_at_index (
260     im->fp_ip4_lookup_hashes_pool, pspd_fp->ip4_in_lookup_hash_idx);
261
262   /* clear the list of matched policies pointers */
263   clib_memset (policies, 0, n * sizeof (*policies));
264   clib_memset (last_priority, 0, n * sizeof (u32));
265   n_left = n;
266   while (n_left)
267     {
268       vec_foreach (mti, mask_type_ids)
269         {
270           mte = im->fp_mask_types + mti->mask_type_idx;
271           if (mte->mask.action == 0)
272             continue;
273           pmatch = (u64 *) match->kv_16_8.key;
274           pmask = (u64 *) mte->mask.kv_16_8.key;
275           pkey = (u64 *) kv.key;
276
277           *pkey++ = *pmatch++ & *pmask++;
278           *pkey = *pmatch & *pmask;
279
280           int res =
281             clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
282           /* lookup the hash by each packet in the burst for this mask. */
283
284           if (res == 0)
285             {
286               /* There is a hit in the hash table. */
287               /* Find the policy with highest priority. */
288               /* Store the lookup results in a dedicated array. */
289
290               if (vec_len (result_val->fp_policies_ids) > 1)
291                 {
292                   u32 *policy_id;
293                   vec_foreach (policy_id, result_val->fp_policies_ids)
294                     {
295                       policy = im->policies + *policy_id;
296
297                       if (single_rule_in_match_5tuple (policy, match))
298                         {
299                           if (last_priority[i] < policy->priority)
300                             {
301                               last_priority[i] = policy->priority;
302                               if (policies[i] == 0)
303                                 counter++;
304                               policies[i] = policy;
305                             }
306                           break;
307                         }
308                     }
309                 }
310               else
311                 {
312                   u32 *policy_id;
313                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
314                   policy_id = result_val->fp_policies_ids;
315                   policy = im->policies + *policy_id;
316                   if ((last_priority[i] < policy->priority) &&
317                       (single_rule_in_match_5tuple (policy, match)))
318                     {
319                       last_priority[i] = policy->priority;
320                       if (policies[i] == 0)
321                         counter++;
322                       policies[i] = policy;
323                     }
324                 }
325             }
326         }
327
328       i++;
329       n_left--;
330       match++;
331     }
332   return counter;
333 }
334
335 /**
336  * @brief function handler to perform lookup in fastpath SPD
337  * for inbound traffic burst of n packets
338  **/
339
340 static_always_inline u32
341 ipsec_fp_in_policy_match_n (void *spd_fp, u8 is_ipv6,
342                             ipsec_fp_5tuple_t *tuples,
343                             ipsec_policy_t **policies, u32 n)
344 {
345   if (is_ipv6)
346     return ipsec_fp_in_ip6_policy_match_n (spd_fp, tuples, policies, n);
347   else
348     return ipsec_fp_in_ip4_policy_match_n (spd_fp, tuples, policies, n);
349 }
350
351 static_always_inline u32
352 ipsec_fp_out_ip6_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
353                                  ipsec_policy_t **policies, u32 *ids, u32 n)
354
355 {
356   u32 last_priority[n];
357   u32 i = 0;
358   u32 counter = 0;
359   ipsec_fp_mask_type_entry_t *mte;
360   ipsec_fp_mask_id_t *mti;
361   ipsec_fp_5tuple_t *match = tuples;
362   ipsec_policy_t *policy;
363
364   u32 n_left = n;
365   clib_bihash_kv_40_8_t kv;
366   /* result of the lookup */
367   clib_bihash_kv_40_8_t result;
368   ipsec_fp_lookup_value_t *result_val =
369     (ipsec_fp_lookup_value_t *) &result.value;
370   u64 *pkey, *pmatch, *pmask;
371   ipsec_main_t *im = &ipsec_main;
372   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
373   ipsec_fp_mask_id_t *mask_type_ids =
374     pspd_fp->fp_mask_ids[IPSEC_SPD_POLICY_IP6_OUTBOUND];
375   clib_bihash_40_8_t *bihash_table = pool_elt_at_index (
376     im->fp_ip6_lookup_hashes_pool, pspd_fp->ip6_out_lookup_hash_idx);
377
378   /*clear the list of matched policies pointers */
379   clib_memset (policies, 0, n * sizeof (*policies));
380   clib_memset (last_priority, 0, n * sizeof (u32));
381   n_left = n;
382   while (n_left)
383     {
384       vec_foreach (mti, mask_type_ids)
385         {
386           mte = im->fp_mask_types + mti->mask_type_idx;
387           if (mte->mask.action != 0)
388             continue;
389
390           pmatch = (u64 *) match->kv_40_8.key;
391           pmask = (u64 *) mte->mask.kv_40_8.key;
392           pkey = (u64 *) kv.key;
393
394           *pkey++ = *pmatch++ & *pmask++;
395           *pkey++ = *pmatch++ & *pmask++;
396           *pkey++ = *pmatch++ & *pmask++;
397           *pkey++ = *pmatch++ & *pmask++;
398           *pkey = *pmatch & *pmask;
399
400           int res =
401             clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
402           /* lookup the hash by each packet in the burst for this mask. */
403
404           if (res == 0)
405             {
406               /* There is a hit in the hash table. */
407               /* Find the policy with highest priority. */
408               /* Store the lookup results in a dedicated array. */
409
410               if (vec_len (result_val->fp_policies_ids) > 1)
411                 {
412                   u32 *policy_id;
413                   vec_foreach (policy_id, result_val->fp_policies_ids)
414                     {
415                       policy = im->policies + *policy_id;
416
417                       if (single_rule_out_match_5tuple (policy, match))
418                         {
419                           if (last_priority[i] < policy->priority)
420                             {
421                               last_priority[i] = policy->priority;
422                               if (policies[i] == 0)
423                                 counter++;
424                               policies[i] = policy;
425                               ids[i] = *policy_id;
426                             }
427                           break;
428                         }
429                     }
430                 }
431               else
432                 {
433                   u32 *policy_id;
434                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
435                   policy_id = result_val->fp_policies_ids;
436                   policy = im->policies + *policy_id;
437                   if (single_rule_out_match_5tuple (policy, match))
438                     {
439                       if (last_priority[i] < policy->priority)
440                         {
441                           last_priority[i] = policy->priority;
442                           if (policies[i] == 0)
443                             counter++;
444                           policies[i] = policy;
445                           ids[i] = *policy_id;
446                         }
447                     }
448                 }
449             }
450         }
451       n_left--;
452       match++;
453       i++;
454     }
455   return counter;
456 }
457
458 static_always_inline u32
459 ipsec_fp_out_ip4_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
460                                  ipsec_policy_t **policies, u32 *ids, u32 n)
461
462 {
463   u32 last_priority[n];
464   u32 i = 0;
465   u32 counter = 0;
466   ipsec_fp_mask_type_entry_t *mte;
467   ipsec_fp_mask_id_t *mti;
468   ipsec_fp_5tuple_t *match = tuples;
469   ipsec_policy_t *policy;
470
471   u32 n_left = n;
472   clib_bihash_kv_16_8_t kv;
473   /* result of the lookup */
474   clib_bihash_kv_16_8_t result;
475   ipsec_fp_lookup_value_t *result_val =
476     (ipsec_fp_lookup_value_t *) &result.value;
477   u64 *pkey, *pmatch, *pmask;
478   ipsec_main_t *im = &ipsec_main;
479   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
480   ipsec_fp_mask_id_t *mask_type_ids =
481     pspd_fp->fp_mask_ids[IPSEC_SPD_POLICY_IP4_OUTBOUND];
482   clib_bihash_16_8_t *bihash_table = pool_elt_at_index (
483     im->fp_ip4_lookup_hashes_pool, pspd_fp->ip4_out_lookup_hash_idx);
484
485   /* clear the list of matched policies pointers */
486   clib_memset (policies, 0, n * sizeof (*policies));
487   clib_memset (last_priority, 0, n * sizeof (u32));
488   n_left = n;
489   while (n_left)
490     {
491       vec_foreach (mti, mask_type_ids)
492         {
493           mte = im->fp_mask_types + mti->mask_type_idx;
494           if (mte->mask.action != 0)
495             continue;
496
497           pmatch = (u64 *) match->kv_16_8.key;
498           pmask = (u64 *) mte->mask.kv_16_8.key;
499           pkey = (u64 *) kv.key;
500
501           *pkey++ = *pmatch++ & *pmask++;
502           *pkey = *pmatch & *pmask;
503
504           int res =
505             clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
506           /* lookup the hash by each packet in the burst for this mask. */
507
508           if (res == 0)
509             {
510               /* There is a hit in the hash table. */
511               /* Find the policy with highest priority. */
512               /* Store the lookup results in a dedicated array. */
513
514               if (vec_len (result_val->fp_policies_ids) > 1)
515                 {
516                   u32 *policy_id;
517                   vec_foreach (policy_id, result_val->fp_policies_ids)
518                     {
519                       policy = im->policies + *policy_id;
520
521                       if (single_rule_out_match_5tuple (policy, match))
522                         {
523                           if (last_priority[i] < policy->priority)
524                             {
525                               last_priority[i] = policy->priority;
526                               if (policies[i] == 0)
527                                 counter++;
528                               policies[i] = policy;
529                               ids[i] = *policy_id;
530                             }
531                           break;
532                         }
533                     }
534                 }
535               else
536                 {
537                   u32 *policy_id;
538                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
539                   policy_id = result_val->fp_policies_ids;
540                   policy = im->policies + *policy_id;
541                   if ((last_priority[i] < policy->priority) &&
542                       (single_rule_out_match_5tuple (policy, match)))
543                     {
544                       last_priority[i] = policy->priority;
545                       if (policies[i] == 0)
546                         counter++;
547                       policies[i] = policy;
548                       ids[i] = *policy_id;
549                     }
550                 }
551             }
552         }
553
554       i++;
555       n_left--;
556       match++;
557     }
558   return counter;
559 }
560
561 /**
562  * @brief function handler to perform lookup in fastpath SPD
563  * for outbound traffic burst of n packets
564  * returns number of successfully matched policies
565  **/
566
567 static_always_inline u32
568 ipsec_fp_out_policy_match_n (void *spd_fp, u8 is_ipv6,
569                              ipsec_fp_5tuple_t *tuples,
570                              ipsec_policy_t **policies, u32 *ids, u32 n)
571
572 {
573   if (is_ipv6)
574     return ipsec_fp_out_ip6_policy_match_n (spd_fp, tuples, policies, ids, n);
575   else
576     return ipsec_fp_out_ip4_policy_match_n (spd_fp, tuples, policies, ids, n);
577 }
578
579 #endif /* !IPSEC_SPD_FP_LOOKUP_H */