ipsec: introduce fast path ipv4 inbound matching
[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
24 static_always_inline int
25 single_rule_match_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *match)
26 {
27   if (PREDICT_FALSE (policy->is_ipv6 != match->is_ipv6))
28     return (0);
29
30   if (PREDICT_FALSE (policy->protocol != IPSEC_POLICY_PROTOCOL_ANY &&
31                      (policy->protocol != match->protocol)))
32     return (0);
33
34   if (!policy->is_ipv6)
35     {
36       if (PREDICT_FALSE (
37             clib_net_to_host_u32 (match->laddr.as_u32) <
38             clib_net_to_host_u32 (policy->laddr.start.ip4.as_u32)))
39         return (0);
40
41       if (PREDICT_FALSE (clib_net_to_host_u32 (match->laddr.as_u32) >
42                          clib_net_to_host_u32 (policy->laddr.stop.ip4.as_u32)))
43         return (0);
44
45       if (PREDICT_FALSE (
46             clib_net_to_host_u32 (match->raddr.as_u32) <
47             clib_net_to_host_u32 (policy->raddr.start.ip4.as_u32)))
48         return (0);
49
50       if (PREDICT_FALSE (clib_net_to_host_u32 (match->raddr.as_u32) >
51                          clib_net_to_host_u32 (policy->raddr.stop.ip4.as_u32)))
52         return (0);
53     }
54   else
55     {
56
57       if (ip6_address_compare (&match->ip6_laddr, &policy->laddr.start.ip6) <
58           0)
59         return (0);
60
61       if (ip6_address_compare (&policy->laddr.stop.ip6, &match->ip6_laddr) < 0)
62
63         return (0);
64
65       if (ip6_address_compare (&match->ip6_raddr, &policy->raddr.start.ip6) <
66           0)
67
68         return (0);
69
70       if (ip6_address_compare (&policy->raddr.stop.ip6, &match->ip6_raddr) < 0)
71
72         return (0);
73     }
74
75   if (PREDICT_FALSE ((match->protocol != IP_PROTOCOL_TCP) &&
76                      (match->protocol != IP_PROTOCOL_UDP) &&
77                      (match->protocol != IP_PROTOCOL_SCTP)))
78     {
79       return (1);
80     }
81
82   if (match->lport < policy->lport.start)
83     return (0);
84
85   if (match->lport > policy->lport.stop)
86     return (0);
87
88   if (match->rport < policy->rport.start)
89     return (0);
90
91   if (match->rport > policy->rport.stop)
92     return (0);
93
94   return (1);
95 }
96
97 static_always_inline int
98 single_rule_in_match_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *match)
99 {
100
101   u32 sa = clib_net_to_host_u32 (match->laddr.as_u32);
102   u32 da = clib_net_to_host_u32 (match->raddr.as_u32);
103
104   if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
105     {
106       ipsec_sa_t *s = ipsec_sa_get (policy->sa_index);
107
108       if (match->spi != s->spi)
109         return (0);
110
111       if (ipsec_sa_is_set_IS_TUNNEL (s))
112         {
113           if (da != clib_net_to_host_u32 (s->tunnel.t_dst.ip.ip4.as_u32))
114             return (0);
115
116           if (sa != clib_net_to_host_u32 (s->tunnel.t_src.ip.ip4.as_u32))
117             return (0);
118         }
119     }
120   else
121     {
122       if (da < clib_net_to_host_u32 (policy->raddr.start.ip4.as_u32))
123         return (0);
124
125       if (da > clib_net_to_host_u32 (policy->raddr.stop.ip4.as_u32))
126         return (0);
127
128       if (sa < clib_net_to_host_u32 (policy->laddr.start.ip4.as_u32))
129         return (0);
130
131       if (sa > clib_net_to_host_u32 (policy->laddr.stop.ip4.as_u32))
132         return (0);
133     }
134   return (1);
135 }
136
137 static_always_inline u32
138 ipsec_fp_in_ip6_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
139                                 ipsec_policy_t **policies, u32 n)
140 {
141   return 0;
142 }
143
144 static_always_inline u32
145 ipsec_fp_in_ip4_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
146                                 ipsec_policy_t **policies, u32 n)
147
148 {
149   u32 last_priority[n];
150   u32 i = 0;
151   u32 counter = 0;
152   ipsec_fp_mask_type_entry_t *mte;
153   ipsec_fp_mask_id_t *mti;
154   ipsec_fp_5tuple_t *match = tuples;
155   ipsec_policy_t *policy;
156   u32 n_left = n;
157   clib_bihash_kv_16_8_t kv;
158   /* result of the lookup */
159   clib_bihash_kv_16_8_t result;
160   ipsec_fp_lookup_value_t *result_val =
161     (ipsec_fp_lookup_value_t *) &result.value;
162   u64 *pkey, *pmatch, *pmask;
163   ipsec_main_t *im = &ipsec_main;
164   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
165   ipsec_fp_mask_id_t *mask_type_ids = pspd_fp->fp_mask_ids[match->action];
166   clib_bihash_16_8_t *bihash_table = pool_elt_at_index (
167     im->fp_ip4_lookup_hashes_pool, pspd_fp->ip4_in_lookup_hash_idx);
168
169   /* clear the list of matched policies pointers */
170   clib_memset (policies, 0, n * sizeof (*policies));
171   clib_memset (last_priority, 0, n * sizeof (u32));
172   n_left = n;
173   while (n_left)
174     {
175       vec_foreach (mti, mask_type_ids)
176         {
177           mte = im->fp_mask_types + mti->mask_type_idx;
178           if (mte->mask.action == 0)
179             continue;
180           pmatch = (u64 *) match->kv_16_8.key;
181           pmask = (u64 *) mte->mask.kv_16_8.key;
182           pkey = (u64 *) kv.key;
183
184           *pkey++ = *pmatch++ & *pmask++;
185           *pkey = *pmatch & *pmask;
186
187           int res =
188             clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
189           /* lookup the hash by each packet in the burst for this mask. */
190
191           if (res == 0)
192             {
193               /* There is a hit in the hash table. */
194               /* Find the policy with highest priority. */
195               /* Store the lookup results in a dedicated array. */
196
197               if (vec_len (result_val->fp_policies_ids) > 1)
198                 {
199                   u32 *policy_id;
200                   vec_foreach (policy_id, result_val->fp_policies_ids)
201                     {
202                       policy = im->policies + *policy_id;
203
204                       if ((last_priority[i] < policy->priority) &&
205                           (single_rule_in_match_5tuple (policy, match)))
206                         {
207                           last_priority[i] = policy->priority;
208                           if (policies[i] == 0)
209                             counter++;
210                           policies[i] = policy;
211                         }
212                     }
213                 }
214               else
215                 {
216                   u32 *policy_id;
217                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
218                   policy_id = result_val->fp_policies_ids;
219                   policy = im->policies + *policy_id;
220                   if ((last_priority[i] < policy->priority) &&
221                       (single_rule_in_match_5tuple (policy, match)))
222                     {
223                       last_priority[i] = policy->priority;
224                       if (policies[i] == 0)
225                         counter++;
226                       policies[i] = policy;
227                     }
228                 }
229             }
230         }
231
232       i++;
233       n_left--;
234       match++;
235     }
236   return counter;
237 }
238
239 /**
240  * @brief function handler to perform lookup in fastpath SPD
241  * for inbound traffic burst of n packets
242  **/
243
244 static_always_inline u32
245 ipsec_fp_in_policy_match_n (void *spd_fp, u8 is_ipv6,
246                             ipsec_fp_5tuple_t *tuples,
247                             ipsec_policy_t **policies, u32 n)
248 {
249   if (is_ipv6)
250     return ipsec_fp_in_ip6_policy_match_n (spd_fp, tuples, policies, n);
251   else
252     return ipsec_fp_in_ip4_policy_match_n (spd_fp, tuples, policies, n);
253 }
254
255 static_always_inline u32
256 ipsec_fp_ip6_out_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
257                                  ipsec_policy_t **policies, u32 *ids, u32 n)
258
259 {
260   u32 last_priority[n];
261   u32 i = 0;
262   u32 counter = 0;
263   ipsec_fp_mask_type_entry_t *mte;
264   ipsec_fp_mask_id_t *mti;
265   ipsec_fp_5tuple_t *match = tuples;
266   ipsec_policy_t *policy;
267
268   u32 n_left = n;
269   clib_bihash_kv_40_8_t kv;
270   /* result of the lookup */
271   clib_bihash_kv_40_8_t result;
272   ipsec_fp_lookup_value_t *result_val =
273     (ipsec_fp_lookup_value_t *) &result.value;
274   u64 *pkey, *pmatch, *pmask;
275   ipsec_main_t *im = &ipsec_main;
276   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
277   ipsec_fp_mask_id_t *mask_type_ids =
278     pspd_fp->fp_mask_ids[IPSEC_SPD_POLICY_IP6_OUTBOUND];
279   clib_bihash_40_8_t *bihash_table = pool_elt_at_index (
280     im->fp_ip6_lookup_hashes_pool, pspd_fp->ip6_out_lookup_hash_idx);
281
282   /*clear the list of matched policies pointers */
283   clib_memset (policies, 0, n * sizeof (*policies));
284   clib_memset (last_priority, 0, n * sizeof (u32));
285   n_left = n;
286   while (n_left)
287     {
288       vec_foreach (mti, mask_type_ids)
289         {
290           mte = im->fp_mask_types + mti->mask_type_idx;
291
292           pmatch = (u64 *) match->kv_40_8.key;
293           pmask = (u64 *) mte->mask.kv_40_8.key;
294           pkey = (u64 *) kv.key;
295
296           *pkey++ = *pmatch++ & *pmask++;
297           *pkey++ = *pmatch++ & *pmask++;
298           *pkey++ = *pmatch++ & *pmask++;
299           *pkey++ = *pmatch++ & *pmask++;
300           *pkey = *pmatch & *pmask;
301
302           int res =
303             clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
304           /* lookup the hash by each packet in the burst for this mask. */
305
306           if (res == 0)
307             {
308               /* There is a hit in the hash table. */
309               /* Find the policy with highest priority. */
310               /* Store the lookup results in a dedicated array. */
311
312               if (vec_len (result_val->fp_policies_ids) > 1)
313                 {
314                   u32 *policy_id;
315                   vec_foreach (policy_id, result_val->fp_policies_ids)
316                     {
317                       policy = im->policies + *policy_id;
318
319                       if (single_rule_match_5tuple (policy, match))
320                         {
321                           if (last_priority[i] < policy->priority)
322                             {
323                               last_priority[i] = policy->priority;
324                               if (policies[i] == 0)
325                                 counter++;
326                               policies[i] = policy;
327                               ids[i] = *policy_id;
328                             }
329                         }
330                     }
331                 }
332               else
333                 {
334                   u32 *policy_id;
335                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
336                   policy_id = result_val->fp_policies_ids;
337                   policy = im->policies + *policy_id;
338                   if (single_rule_match_5tuple (policy, match))
339                     {
340                       if (last_priority[i] < policy->priority)
341                         {
342                           last_priority[i] = policy->priority;
343                           if (policies[i] == 0)
344                             counter++;
345                           policies[i] = policy;
346                           ids[i] = *policy_id;
347                         }
348                     }
349                 }
350             }
351         }
352       n_left--;
353       match++;
354       i++;
355     }
356   return counter;
357 }
358
359 static_always_inline u32
360 ipsec_fp_ip4_out_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples,
361                                  ipsec_policy_t **policies, u32 *ids, u32 n)
362
363 {
364   u32 last_priority[n];
365   u32 i = 0;
366   u32 counter = 0;
367   ipsec_fp_mask_type_entry_t *mte;
368   ipsec_fp_mask_id_t *mti;
369   ipsec_fp_5tuple_t *match = tuples;
370   ipsec_policy_t *policy;
371
372   u32 n_left = n;
373   clib_bihash_kv_16_8_t kv;
374   /* result of the lookup */
375   clib_bihash_kv_16_8_t result;
376   ipsec_fp_lookup_value_t *result_val =
377     (ipsec_fp_lookup_value_t *) &result.value;
378   u64 *pkey, *pmatch, *pmask;
379   ipsec_main_t *im = &ipsec_main;
380   ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp;
381   ipsec_fp_mask_id_t *mask_type_ids =
382     pspd_fp->fp_mask_ids[IPSEC_SPD_POLICY_IP4_OUTBOUND];
383   clib_bihash_16_8_t *bihash_table = pool_elt_at_index (
384     im->fp_ip4_lookup_hashes_pool, pspd_fp->ip4_out_lookup_hash_idx);
385
386   /* clear the list of matched policies pointers */
387   clib_memset (policies, 0, n * sizeof (*policies));
388   clib_memset (last_priority, 0, n * sizeof (u32));
389   n_left = n;
390   while (n_left)
391     {
392       vec_foreach (mti, mask_type_ids)
393         {
394           mte = im->fp_mask_types + mti->mask_type_idx;
395           if (mte->mask.action != 0)
396             continue;
397
398           pmatch = (u64 *) match->kv_16_8.key;
399           pmask = (u64 *) mte->mask.kv_16_8.key;
400           pkey = (u64 *) kv.key;
401
402           *pkey++ = *pmatch++ & *pmask++;
403           *pkey = *pmatch & *pmask;
404
405           int res =
406             clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
407           /* lookup the hash by each packet in the burst for this mask. */
408
409           if (res == 0)
410             {
411               /* There is a hit in the hash table. */
412               /* Find the policy with highest priority. */
413               /* Store the lookup results in a dedicated array. */
414
415               if (vec_len (result_val->fp_policies_ids) > 1)
416                 {
417                   u32 *policy_id;
418                   vec_foreach (policy_id, result_val->fp_policies_ids)
419                     {
420                       policy = im->policies + *policy_id;
421
422                       if ((last_priority[i] < policy->priority) &&
423                           (single_rule_match_5tuple (policy, match)))
424                         {
425                           last_priority[i] = policy->priority;
426                           if (policies[i] == 0)
427                             counter++;
428                           policies[i] = policy;
429                           ids[i] = *policy_id;
430                         }
431                     }
432                 }
433               else
434                 {
435                   u32 *policy_id;
436                   ASSERT (vec_len (result_val->fp_policies_ids) == 1);
437                   policy_id = result_val->fp_policies_ids;
438                   policy = im->policies + *policy_id;
439                   if ((last_priority[i] < policy->priority) &&
440                       (single_rule_match_5tuple (policy, match)))
441                     {
442                       last_priority[i] = policy->priority;
443                       if (policies[i] == 0)
444                         counter++;
445                       policies[i] = policy;
446                       ids[i] = *policy_id;
447                     }
448                 }
449             }
450         }
451
452       i++;
453       n_left--;
454       match++;
455     }
456   return counter;
457 }
458
459 /**
460  * @brief function handler to perform lookup in fastpath SPD
461  * for outbound traffic burst of n packets
462  * returns number of successfully matched policies
463  **/
464
465 static_always_inline u32
466 ipsec_fp_out_policy_match_n (void *spd_fp, u8 is_ipv6,
467                              ipsec_fp_5tuple_t *tuples,
468                              ipsec_policy_t **policies, u32 *ids, u32 n)
469
470 {
471   if (is_ipv6)
472     return ipsec_fp_ip6_out_policy_match_n (spd_fp, tuples, policies, ids, n);
473   else
474     return ipsec_fp_ip4_out_policy_match_n (spd_fp, tuples, policies, ids, n);
475 }
476
477 #endif /* !IPSEC_SPD_FP_LOOKUP_H */