ipsec: remove redundant policy array in fast path spd
[vpp.git] / src / vnet / ipsec / ipsec_spd_policy.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/ipsec/ipsec.h>
17
18 /**
19  * @brief
20  * Policy packet & bytes counters
21  */
22 vlib_combined_counter_main_t ipsec_spd_policy_counters = {
23   .name = "policy",
24   .stat_segment_name = "/net/ipsec/policy",
25 };
26
27 int
28 ipsec_policy_mk_type (bool is_outbound,
29                       bool is_ipv6,
30                       ipsec_policy_action_t action,
31                       ipsec_spd_policy_type_t * type)
32 {
33   if (is_outbound)
34     {
35       *type = (is_ipv6 ?
36                IPSEC_SPD_POLICY_IP6_OUTBOUND : IPSEC_SPD_POLICY_IP4_OUTBOUND);
37       return (0);
38     }
39   else
40     {
41       switch (action)
42         {
43         case IPSEC_POLICY_ACTION_PROTECT:
44           *type = (is_ipv6 ?
45                    IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT :
46                    IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT);
47           return (0);
48         case IPSEC_POLICY_ACTION_BYPASS:
49           *type = (is_ipv6 ?
50                    IPSEC_SPD_POLICY_IP6_INBOUND_BYPASS :
51                    IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
52           return (0);
53         case IPSEC_POLICY_ACTION_DISCARD:
54           *type = (is_ipv6 ?
55                    IPSEC_SPD_POLICY_IP6_INBOUND_DISCARD :
56                    IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
57           return (0);
58         case IPSEC_POLICY_ACTION_RESOLVE:
59           break;
60         }
61     }
62
63   /* Unsupported type */
64   return (-1);
65 }
66
67 static_always_inline int
68 ipsec_is_policy_inbound (ipsec_policy_t *policy)
69 {
70   if (policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT ||
71       policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS ||
72       policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD ||
73       policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT ||
74       policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_BYPASS ||
75       policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_DISCARD)
76     return 1;
77
78   return 0;
79 }
80
81 static_always_inline int
82 ipsec_is_fp_enabled (ipsec_main_t *im, ipsec_spd_t *spd,
83                      ipsec_policy_t *policy)
84 {
85   if ((im->fp_spd_ipv4_out_is_enabled &&
86        PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip4_out_lookup_hash_idx) &&
87        policy->type == IPSEC_SPD_POLICY_IP4_OUTBOUND) ||
88       (im->fp_spd_ipv4_in_is_enabled &&
89        PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip4_in_lookup_hash_idx) &&
90        (policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT ||
91         policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS ||
92         policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD)) ||
93       (im->fp_spd_ipv6_in_is_enabled &&
94        PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip6_in_lookup_hash_idx) &&
95        (policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT ||
96         policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_BYPASS ||
97         policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_DISCARD)) ||
98       (im->fp_spd_ipv6_out_is_enabled &&
99        PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip6_out_lookup_hash_idx) &&
100        policy->type == IPSEC_SPD_POLICY_IP6_OUTBOUND))
101     return 1;
102   return 0;
103 }
104
105 int
106 ipsec_add_del_policy (vlib_main_t * vm,
107                       ipsec_policy_t * policy, int is_add, u32 * stat_index)
108 {
109   ipsec_main_t *im = &ipsec_main;
110   ipsec_spd_t *spd = 0;
111   ipsec_policy_t *vp;
112   u32 spd_index;
113   uword *p;
114
115   p = hash_get (im->spd_index_by_spd_id, policy->id);
116
117   if (!p)
118     return VNET_API_ERROR_SYSCALL_ERROR_1;
119
120   spd_index = p[0];
121   spd = pool_elt_at_index (im->spds, spd_index);
122   if (!spd)
123     return VNET_API_ERROR_SYSCALL_ERROR_1;
124
125   if (im->output_flow_cache_flag && !policy->is_ipv6 &&
126       policy->type == IPSEC_SPD_POLICY_IP4_OUTBOUND)
127     {
128       /*
129        * Flow cache entry is valid only when epoch_count value in control
130        * plane and data plane match. Otherwise, flow cache entry is considered
131        * stale. To avoid the race condition of using old epoch_count value
132        * in data plane after the roll over of epoch_count in control plane,
133        * entire flow cache is reset.
134        */
135       if (im->epoch_count == 0xFFFFFFFF)
136         {
137           /* Reset all the entries in flow cache */
138           clib_memset_u8 (im->ipsec4_out_spd_hash_tbl, 0,
139                           im->ipsec4_out_spd_hash_num_buckets *
140                             (sizeof (*(im->ipsec4_out_spd_hash_tbl))));
141         }
142       /* Increment epoch counter by 1 */
143       clib_atomic_fetch_add_relax (&im->epoch_count, 1);
144       /* Reset spd flow cache counter since all old entries are stale */
145       clib_atomic_store_relax_n (&im->ipsec4_out_spd_flow_cache_entries, 0);
146     }
147
148   if ((policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT ||
149        policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS ||
150        policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD) &&
151       im->input_flow_cache_flag && !policy->is_ipv6)
152     {
153       /*
154        * Flow cache entry is valid only when input_epoch_count value in control
155        * plane and data plane match. Otherwise, flow cache entry is considered
156        * stale. To avoid the race condition of using old input_epoch_count
157        * value in data plane after the roll over of input_epoch_count in
158        * control plane, entire flow cache is reset.
159        */
160       if (im->input_epoch_count == 0xFFFFFFFF)
161         {
162           /* Reset all the entries in flow cache */
163           clib_memset_u8 (im->ipsec4_in_spd_hash_tbl, 0,
164                           im->ipsec4_in_spd_hash_num_buckets *
165                             (sizeof (*(im->ipsec4_in_spd_hash_tbl))));
166         }
167       /* Increment epoch counter by 1 */
168       clib_atomic_fetch_add_relax (&im->input_epoch_count, 1);
169       /* Reset spd flow cache counter since all old entries are stale */
170       im->ipsec4_in_spd_flow_cache_entries = 0;
171     }
172
173   if (is_add)
174     {
175       u32 policy_index;
176       u32 i;
177
178       if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
179         {
180           index_t sa_index = ipsec_sa_find_and_lock (policy->sa_id);
181
182           if (INDEX_INVALID == sa_index)
183             return VNET_API_ERROR_SYSCALL_ERROR_1;
184           policy->sa_index = sa_index;
185         }
186       else
187         policy->sa_index = INDEX_INVALID;
188
189       /**
190        * Try adding the policy into fast path SPD first. Only adding to
191        * traditional SPD when failed.
192        **/
193       if (ipsec_is_fp_enabled (im, spd, policy))
194         return ipsec_fp_add_del_policy ((void *) &spd->fp_spd, policy, 1,
195                                         stat_index);
196
197       pool_get (im->policies, vp);
198       clib_memcpy (vp, policy, sizeof (*vp));
199       policy_index = vp - im->policies;
200
201       vlib_validate_combined_counter (&ipsec_spd_policy_counters,
202                                       policy_index);
203       vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index);
204
205       vec_foreach_index (i, spd->policies[policy->type])
206         {
207           ipsec_policy_t *p =
208             pool_elt_at_index (im->policies, spd->policies[policy->type][i]);
209
210           if (p->priority <= vp->priority)
211             {
212               break;
213             }
214         }
215
216       vec_insert_elts (spd->policies[policy->type], &policy_index, 1, i);
217
218       *stat_index = policy_index;
219     }
220   else
221     {
222       u32 ii;
223
224       /**
225        * Try to delete the policy from the fast path SPD first. Delete from
226        * traditional SPD when fp delete fails.
227        **/
228
229       if (ipsec_is_fp_enabled (im, spd, policy))
230
231         {
232           if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
233             {
234               index_t sa_index = ipsec_sa_find_and_lock (policy->sa_id);
235
236               if (INDEX_INVALID == sa_index)
237                 return VNET_API_ERROR_SYSCALL_ERROR_1;
238               policy->sa_index = sa_index;
239               ipsec_sa_unlock_id (policy->sa_id);
240             }
241           else
242             policy->sa_index = INDEX_INVALID;
243
244           return ipsec_fp_add_del_policy ((void *) &spd->fp_spd, policy, 0,
245                                           stat_index);
246         }
247
248       vec_foreach_index (ii, (spd->policies[policy->type]))
249       {
250         vp = pool_elt_at_index (im->policies,
251                                 spd->policies[policy->type][ii]);
252         if (ipsec_policy_is_equal (vp, policy))
253           {
254             vec_delete (spd->policies[policy->type], 1, ii);
255             ipsec_sa_unlock (vp->sa_index);
256             pool_put (im->policies, vp);
257             break;
258           }
259       }
260     }
261
262   return 0;
263 }
264
265 static_always_inline void
266 ipsec_fp_release_mask_type (ipsec_main_t *im, u32 mask_type_index)
267 {
268   ipsec_fp_mask_type_entry_t *mte =
269     pool_elt_at_index (im->fp_mask_types, mask_type_index);
270   mte->refcount--;
271   if (mte->refcount == 0)
272     {
273       /* this entry is not in use anymore */
274       ASSERT (clib_memset (mte, 0xae, sizeof (*mte)) == EOK);
275       pool_put (im->fp_mask_types, mte);
276     }
277 }
278
279 static_always_inline u32
280 find_mask_type_index (ipsec_main_t *im, ipsec_fp_5tuple_t *mask)
281 {
282   ipsec_fp_mask_type_entry_t *mte;
283
284   pool_foreach (mte, im->fp_mask_types)
285     {
286       if (memcmp (&mte->mask, mask, sizeof (*mask)) == 0)
287         return (mte - im->fp_mask_types);
288     }
289
290   return ~0;
291 }
292
293 static_always_inline void
294 fill_ip6_hash_policy_kv (ipsec_fp_5tuple_t *match, ipsec_fp_5tuple_t *mask,
295                          clib_bihash_kv_40_8_t *kv)
296 {
297   ipsec_fp_lookup_value_t *kv_val = (ipsec_fp_lookup_value_t *) &kv->value;
298   u64 *pmatch = (u64 *) match->kv_40_8.key;
299   u64 *pmask = (u64 *) mask->kv_40_8.key;
300   u64 *pkey = (u64 *) kv->key;
301
302   *pkey++ = *pmatch++ & *pmask++;
303   *pkey++ = *pmatch++ & *pmask++;
304   *pkey++ = *pmatch++ & *pmask++;
305   *pkey++ = *pmatch++ & *pmask++;
306   *pkey = *pmatch & *pmask;
307
308   kv_val->as_u64 = 0;
309 }
310
311 static_always_inline void
312 fill_ip4_hash_policy_kv (ipsec_fp_5tuple_t *match, ipsec_fp_5tuple_t *mask,
313                          clib_bihash_kv_16_8_t *kv)
314 {
315   ipsec_fp_lookup_value_t *kv_val = (ipsec_fp_lookup_value_t *) &kv->value;
316   u64 *pmatch = (u64 *) match->kv_16_8.key;
317   u64 *pmask = (u64 *) mask->kv_16_8.key;
318   u64 *pkey = (u64 *) kv->key;
319
320   *pkey++ = *pmatch++ & *pmask++;
321   *pkey = *pmatch & *pmask;
322
323   kv_val->as_u64 = 0;
324 }
325
326 static_always_inline u16
327 mask_out_highest_set_bit_u16 (u16 x)
328 {
329   x |= x >> 8;
330   x |= x >> 4;
331   x |= x >> 2;
332   x |= x >> 1;
333   return ~x;
334 }
335
336 static_always_inline u32
337 mask_out_highest_set_bit_u32 (u32 x)
338 {
339   x |= x >> 16;
340   x |= x >> 8;
341   x |= x >> 4;
342   x |= x >> 2;
343   x |= x >> 1;
344   return ~x;
345 }
346
347 static_always_inline u64
348 mask_out_highest_set_bit_u64 (u64 x)
349 {
350   x |= x >> 32;
351   x |= x >> 16;
352   x |= x >> 8;
353   x |= x >> 4;
354   x |= x >> 2;
355   x |= x >> 1;
356   return ~x;
357 }
358
359 static_always_inline void
360 ipsec_fp_get_policy_ports_mask (ipsec_policy_t *policy,
361                                 ipsec_fp_5tuple_t *mask)
362 {
363   if (PREDICT_TRUE ((policy->protocol == IP_PROTOCOL_TCP) ||
364                     (policy->protocol == IP_PROTOCOL_UDP) ||
365                     (policy->protocol == IP_PROTOCOL_SCTP)))
366     {
367       mask->lport = policy->lport.start ^ policy->lport.stop;
368       mask->rport = policy->rport.start ^ policy->rport.stop;
369
370       mask->lport = mask_out_highest_set_bit_u16 (mask->lport);
371
372       mask->rport = mask_out_highest_set_bit_u16 (mask->rport);
373     }
374   else
375     {
376       mask->lport = 0;
377       mask->rport = 0;
378     }
379
380   mask->protocol = (policy->protocol == IPSEC_POLICY_PROTOCOL_ANY) ? 0 : ~0;
381   mask->action = 0;
382 }
383
384 static_always_inline void
385 ipsec_fp_ip4_get_policy_mask (ipsec_policy_t *policy, ipsec_fp_5tuple_t *mask,
386                               bool inbound)
387 {
388   u32 *pladdr_start = (u32 *) &policy->laddr.start.ip4;
389   u32 *pladdr_stop = (u32 *) &policy->laddr.stop.ip4;
390   u32 *plmask = (u32 *) &mask->laddr;
391   u32 *praddr_start = (u32 *) &policy->raddr.start.ip4;
392   u32 *praddr_stop = (u32 *) &policy->raddr.stop.ip4;
393   u32 *prmask = (u32 *) &mask->raddr;
394
395   clib_memset_u8 (mask, 0xff, sizeof (ipsec_fp_5tuple_t));
396   clib_memset_u8 (&mask->l3_zero_pad, 0, sizeof (mask->l3_zero_pad));
397
398   /* find bits where start != stop */
399   *plmask = *pladdr_start ^ *pladdr_stop;
400   *prmask = *praddr_start ^ *praddr_stop;
401   /* Find most significant bit set (that is the first position
402    * start differs from stop). Mask out everything after that bit and
403    * the bit itself. Remember that policy stores start and stop in the net
404    * order.
405    */
406   *plmask = clib_host_to_net_u32 (
407     mask_out_highest_set_bit_u32 (clib_net_to_host_u32 (*plmask)));
408
409   *prmask = clib_host_to_net_u32 (
410     mask_out_highest_set_bit_u32 (clib_net_to_host_u32 (*prmask)));
411
412   if (inbound)
413     {
414       if (policy->type != IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT)
415         mask->spi = 0;
416
417       mask->protocol = 0;
418     }
419   else
420     {
421       mask->action = 0;
422       ipsec_fp_get_policy_ports_mask (policy, mask);
423     }
424 }
425
426 static_always_inline void
427 ipsec_fp_ip6_get_policy_mask (ipsec_policy_t *policy, ipsec_fp_5tuple_t *mask,
428                               bool inbound)
429 {
430   u64 *pladdr_start = (u64 *) &policy->laddr.start;
431   u64 *pladdr_stop = (u64 *) &policy->laddr.stop;
432   u64 *plmask = (u64 *) &mask->ip6_laddr;
433   u64 *praddr_start = (u64 *) &policy->raddr.start;
434   u64 *praddr_stop = (u64 *) &policy->raddr.stop;
435   u64 *prmask = (u64 *) &mask->ip6_raddr;
436
437   clib_memset_u8 (mask, 0xff, sizeof (ipsec_fp_5tuple_t));
438
439   *plmask = (*pladdr_start++ ^ *pladdr_stop++);
440
441   *prmask = (*praddr_start++ ^ *praddr_stop++);
442
443   /* Find most significant bit set (that is the first position
444    * start differs from stop). Mask out everything after that bit and
445    * the bit itself. Remember that policy stores start and stop in the net
446    * order.
447    */
448   *plmask = clib_host_to_net_u64 (
449     mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*plmask)));
450
451   if (*plmask++ & clib_host_to_net_u64 (0x1))
452     {
453       *plmask = (*pladdr_start ^ *pladdr_stop);
454       *plmask = clib_host_to_net_u64 (
455         mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*plmask)));
456     }
457   else
458     *plmask = 0;
459
460   *prmask = clib_host_to_net_u64 (
461     mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*prmask)));
462
463   if (*prmask++ & clib_host_to_net_u64 (0x1))
464     {
465       *prmask = (*pladdr_start ^ *pladdr_stop);
466       *prmask = clib_host_to_net_u64 (
467         mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*prmask)));
468     }
469   else
470     *prmask = 0;
471
472   if (inbound)
473     {
474       if (policy->type != IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT)
475         mask->spi = 0;
476
477       mask->protocol = 0;
478     }
479   else
480     {
481       mask->action = 0;
482       ipsec_fp_get_policy_ports_mask (policy, mask);
483     }
484 }
485
486 static_always_inline void
487 ipsec_fp_get_policy_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *tuple,
488                             bool inbound)
489 {
490   memset (tuple, 0, sizeof (*tuple));
491   tuple->is_ipv6 = policy->is_ipv6;
492   if (tuple->is_ipv6)
493     {
494       tuple->ip6_laddr = policy->laddr.start.ip6;
495       tuple->ip6_raddr = policy->raddr.start.ip6;
496     }
497   else
498     {
499       tuple->laddr = policy->laddr.start.ip4;
500       tuple->raddr = policy->raddr.start.ip4;
501     }
502
503   if (inbound)
504     {
505
506       if ((policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT ||
507            policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT) &&
508           policy->sa_index != INDEX_INVALID)
509         {
510           ipsec_sa_t *s = ipsec_sa_get (policy->sa_index);
511           tuple->spi = s->spi;
512         }
513       else
514         tuple->spi = INDEX_INVALID;
515       tuple->action = policy->type;
516       return;
517     }
518
519   tuple->protocol = policy->protocol;
520
521   tuple->lport = policy->lport.start;
522   tuple->rport = policy->rport.start;
523 }
524
525 static_always_inline int
526 ipsec_fp_mask_type_idx_cmp (ipsec_fp_mask_id_t *mask_id, u32 *idx)
527 {
528   return mask_id->mask_type_idx == *idx;
529 }
530
531 int
532 ipsec_fp_ip4_add_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
533                          ipsec_policy_t *policy, u32 *stat_index)
534 {
535   u32 mask_index, searched_idx;
536   ipsec_policy_t *vp;
537   ipsec_fp_mask_type_entry_t *mte;
538   u32 policy_index;
539   clib_bihash_kv_16_8_t kv;
540   clib_bihash_kv_16_8_t result;
541   ipsec_fp_lookup_value_t *result_val =
542     (ipsec_fp_lookup_value_t *) &result.value;
543   ipsec_fp_lookup_value_t *key_val = (ipsec_fp_lookup_value_t *) &kv.value;
544
545   ipsec_fp_5tuple_t mask, policy_5tuple;
546   int res;
547   bool inbound = ipsec_is_policy_inbound (policy);
548   clib_bihash_16_8_t *bihash_table =
549     inbound ? pool_elt_at_index (im->fp_ip4_lookup_hashes_pool,
550                                  fp_spd->ip4_in_lookup_hash_idx) :
551                     pool_elt_at_index (im->fp_ip4_lookup_hashes_pool,
552                                  fp_spd->ip4_out_lookup_hash_idx);
553
554   ipsec_fp_ip4_get_policy_mask (policy, &mask, inbound);
555   pool_get (im->policies, vp);
556   policy_index = vp - im->policies;
557   vlib_validate_combined_counter (&ipsec_spd_policy_counters, policy_index);
558   vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index);
559   *stat_index = policy_index;
560   mask_index = find_mask_type_index (im, &mask);
561
562   if (mask_index == ~0)
563     {
564       /* mask type not found, we need to create a new entry */
565       pool_get (im->fp_mask_types, mte);
566       mask_index = mte - im->fp_mask_types;
567       mte->refcount = 0;
568     }
569   else
570     mte = im->fp_mask_types + mask_index;
571
572   policy->fp_mask_type_id = mask_index;
573   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
574
575   fill_ip4_hash_policy_kv (&policy_5tuple, &mask, &kv);
576
577   res = clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
578   if (res != 0)
579     {
580       /* key was not found crate a new entry */
581       vec_add1 (key_val->fp_policies_ids, policy_index);
582       res = clib_bihash_add_del_16_8 (bihash_table, &kv, 1);
583
584       if (res != 0)
585         goto error;
586     }
587   else
588     {
589
590       if (vec_max_len (result_val->fp_policies_ids) !=
591           vec_len (result_val->fp_policies_ids))
592         {
593           /* no need to resize */
594           vec_add1 (result_val->fp_policies_ids, policy_index);
595         }
596       else
597         {
598           vec_add1 (result_val->fp_policies_ids, policy_index);
599
600           res = clib_bihash_add_del_16_8 (bihash_table, &result, 1);
601
602           if (res != 0)
603             goto error;
604         }
605     }
606
607   if (mte->refcount == 0)
608     {
609       clib_memcpy (&mte->mask, &mask, sizeof (mask));
610       mte->refcount = 0;
611     }
612
613   searched_idx =
614     vec_search_with_function (fp_spd->fp_mask_ids[policy->type], &mask_index,
615                               ipsec_fp_mask_type_idx_cmp);
616   if (~0 == searched_idx)
617     {
618       ipsec_fp_mask_id_t mask_id = { mask_index, 1 };
619       vec_add1 (fp_spd->fp_mask_ids[policy->type], mask_id);
620     }
621   else
622     (fp_spd->fp_mask_ids[policy->type] + searched_idx)->refcount++;
623
624   mte->refcount++;
625   clib_memcpy (vp, policy, sizeof (*vp));
626
627   return 0;
628
629 error:
630   pool_put (im->policies, vp);
631   ipsec_fp_release_mask_type (im, mask_index);
632   return -1;
633 }
634
635 int
636 ipsec_fp_ip6_add_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
637                          ipsec_policy_t *policy, u32 *stat_index)
638 {
639
640   u32 mask_index, searched_idx;
641   ipsec_policy_t *vp;
642   ipsec_fp_mask_type_entry_t *mte;
643   u32 policy_index;
644   clib_bihash_kv_40_8_t kv;
645   clib_bihash_kv_40_8_t result;
646   ipsec_fp_lookup_value_t *result_val =
647     (ipsec_fp_lookup_value_t *) &result.value;
648   ipsec_fp_lookup_value_t *key_val = (ipsec_fp_lookup_value_t *) &kv.value;
649
650   ipsec_fp_5tuple_t mask, policy_5tuple;
651   int res;
652   bool inbound = ipsec_is_policy_inbound (policy);
653
654   ipsec_fp_ip6_get_policy_mask (policy, &mask, inbound);
655   pool_get (im->policies, vp);
656   policy_index = vp - im->policies;
657   vlib_validate_combined_counter (&ipsec_spd_policy_counters, policy_index);
658   vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index);
659   *stat_index = policy_index;
660   mask_index = find_mask_type_index (im, &mask);
661   clib_bihash_40_8_t *bihash_table =
662     inbound ? pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
663                                  fp_spd->ip6_in_lookup_hash_idx) :
664                     pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
665                                  fp_spd->ip6_out_lookup_hash_idx);
666
667   if (mask_index == ~0)
668     {
669       /* mask type not found, we need to create a new entry */
670       pool_get (im->fp_mask_types, mte);
671       mask_index = mte - im->fp_mask_types;
672       mte->refcount = 0;
673     }
674   else
675     mte = im->fp_mask_types + mask_index;
676
677   policy->fp_mask_type_id = mask_index;
678   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
679
680   fill_ip6_hash_policy_kv (&policy_5tuple, &mask, &kv);
681
682   res = clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
683   if (res != 0)
684     {
685       /* key was not found crate a new entry */
686       vec_add1 (key_val->fp_policies_ids, policy_index);
687       res = clib_bihash_add_del_40_8 (bihash_table, &kv, 1);
688       if (res != 0)
689         goto error;
690     }
691   else
692     {
693
694       if (vec_max_len (result_val->fp_policies_ids) !=
695           vec_len (result_val->fp_policies_ids))
696         {
697           /* no need to resize */
698           vec_add1 (result_val->fp_policies_ids, policy_index);
699         }
700       else
701         {
702           vec_add1 (result_val->fp_policies_ids, policy_index);
703
704           res = clib_bihash_add_del_40_8 (bihash_table, &result, 1);
705
706           if (res != 0)
707             goto error;
708         }
709     }
710
711   if (mte->refcount == 0)
712     {
713       clib_memcpy (&mte->mask, &mask, sizeof (mask));
714       mte->refcount = 0;
715     }
716
717   searched_idx =
718     vec_search_with_function (fp_spd->fp_mask_ids[policy->type], &mask_index,
719                               ipsec_fp_mask_type_idx_cmp);
720   if (~0 == searched_idx)
721     {
722       ipsec_fp_mask_id_t mask_id = { mask_index, 1 };
723       vec_add1 (fp_spd->fp_mask_ids[policy->type], mask_id);
724     }
725   else
726     (fp_spd->fp_mask_ids[policy->type] + searched_idx)->refcount++;
727
728   mte->refcount++;
729   clib_memcpy (vp, policy, sizeof (*vp));
730
731   return 0;
732
733 error:
734   pool_put (im->policies, vp);
735   ipsec_fp_release_mask_type (im, mask_index);
736   return -1;
737 }
738
739 int
740 ipsec_fp_ip6_del_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
741                          ipsec_policy_t *policy)
742 {
743   int res;
744   ipsec_fp_5tuple_t mask = { 0 }, policy_5tuple;
745   clib_bihash_kv_40_8_t kv;
746   clib_bihash_kv_40_8_t result;
747   ipsec_fp_lookup_value_t *result_val =
748     (ipsec_fp_lookup_value_t *) &result.value;
749   bool inbound = ipsec_is_policy_inbound (policy);
750   clib_bihash_40_8_t *bihash_table =
751     inbound ? pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
752                                  fp_spd->ip6_in_lookup_hash_idx) :
753                     pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
754                                  fp_spd->ip6_out_lookup_hash_idx);
755
756   ipsec_policy_t *vp;
757   u32 ii, imt;
758
759   ipsec_fp_ip6_get_policy_mask (policy, &mask, inbound);
760   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
761   fill_ip6_hash_policy_kv (&policy_5tuple, &mask, &kv);
762   res = clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
763   if (res != 0)
764     return -1;
765
766   vec_foreach_index (ii, result_val->fp_policies_ids)
767     {
768       vp =
769         pool_elt_at_index (im->policies, *(result_val->fp_policies_ids + ii));
770       if (ipsec_policy_is_equal (vp, policy))
771         {
772           if (vec_len (result_val->fp_policies_ids) == 1)
773             {
774               vec_free (result_val->fp_policies_ids);
775               clib_bihash_add_del_40_8 (bihash_table, &result, 0);
776             }
777           else
778             vec_del1 (result_val->fp_policies_ids, ii);
779
780           vec_foreach_index (imt, fp_spd->fp_mask_ids[policy->type])
781             {
782               if ((fp_spd->fp_mask_ids[policy->type] + imt)->mask_type_idx ==
783                   vp->fp_mask_type_id)
784                 {
785
786                   if ((fp_spd->fp_mask_ids[policy->type] + imt)->refcount-- ==
787                       1)
788                     vec_del1 (fp_spd->fp_mask_ids[policy->type], imt);
789
790                   break;
791                 }
792             }
793
794           ipsec_fp_release_mask_type (im, vp->fp_mask_type_id);
795           ipsec_sa_unlock (vp->sa_index);
796           pool_put (im->policies, vp);
797           return 0;
798         }
799     }
800   return -1;
801 }
802
803 int
804 ipsec_fp_ip4_del_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
805                          ipsec_policy_t *policy)
806 {
807   int res;
808   ipsec_fp_5tuple_t mask = { 0 }, policy_5tuple;
809   clib_bihash_kv_16_8_t kv;
810   clib_bihash_kv_16_8_t result;
811   ipsec_fp_lookup_value_t *result_val =
812     (ipsec_fp_lookup_value_t *) &result.value;
813   bool inbound = ipsec_is_policy_inbound (policy);
814   ipsec_policy_t *vp;
815   u32 ii, imt;
816   clib_bihash_16_8_t *bihash_table =
817     inbound ? pool_elt_at_index (im->fp_ip4_lookup_hashes_pool,
818                                  fp_spd->ip4_in_lookup_hash_idx) :
819                     pool_elt_at_index (im->fp_ip4_lookup_hashes_pool,
820                                  fp_spd->ip4_out_lookup_hash_idx);
821
822   ipsec_fp_ip4_get_policy_mask (policy, &mask, inbound);
823   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
824   fill_ip4_hash_policy_kv (&policy_5tuple, &mask, &kv);
825   res = clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
826
827   if (res != 0)
828     return -1;
829
830   vec_foreach_index (ii, result_val->fp_policies_ids)
831     {
832       vp =
833         pool_elt_at_index (im->policies, *(result_val->fp_policies_ids + ii));
834       if (ipsec_policy_is_equal (vp, policy))
835         {
836           if (vec_len (result_val->fp_policies_ids) == 1)
837             {
838               vec_free (result_val->fp_policies_ids);
839               clib_bihash_add_del_16_8 (bihash_table, &result, 0);
840             }
841           else
842             vec_del1 (result_val->fp_policies_ids, ii);
843
844           vec_foreach_index (imt, fp_spd->fp_mask_ids[policy->type])
845             {
846               if ((fp_spd->fp_mask_ids[policy->type] + imt)->mask_type_idx ==
847                   vp->fp_mask_type_id)
848                 {
849
850                   if ((fp_spd->fp_mask_ids[policy->type] + imt)->refcount-- ==
851                       1)
852                     vec_del1 (fp_spd->fp_mask_ids[policy->type], imt);
853
854                   break;
855                 }
856             }
857           ipsec_fp_release_mask_type (im, vp->fp_mask_type_id);
858           ipsec_sa_unlock (vp->sa_index);
859           pool_put (im->policies, vp);
860           return 0;
861         }
862     }
863   return -1;
864 }
865
866 int
867 ipsec_fp_add_del_policy (void *fp_spd, ipsec_policy_t *policy, int is_add,
868                          u32 *stat_index)
869 {
870   ipsec_main_t *im = &ipsec_main;
871
872   if (is_add)
873     if (policy->is_ipv6)
874       return ipsec_fp_ip6_add_policy (im, (ipsec_spd_fp_t *) fp_spd, policy,
875                                       stat_index);
876     else
877       return ipsec_fp_ip4_add_policy (im, (ipsec_spd_fp_t *) fp_spd, policy,
878                                       stat_index);
879
880   else if (policy->is_ipv6)
881
882     return ipsec_fp_ip6_del_policy (im, (ipsec_spd_fp_t *) fp_spd, policy);
883   else
884     return ipsec_fp_ip4_del_policy (im, (ipsec_spd_fp_t *) fp_spd, policy);
885 }
886
887 /*
888  * fd.io coding-style-patch-verification: ON
889  *
890  * Local Variables:
891  * eval: (c-set-style "gnu")
892  * End:
893  */