d5310a61cbde245079db948a37e4750ee7ba69cb
[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   vec_add1 (fp_spd->fp_policies[policy->type], policy_index);
626   clib_memcpy (vp, policy, sizeof (*vp));
627
628   return 0;
629
630 error:
631   pool_put (im->policies, vp);
632   ipsec_fp_release_mask_type (im, mask_index);
633   return -1;
634 }
635
636 int
637 ipsec_fp_ip6_add_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
638                          ipsec_policy_t *policy, u32 *stat_index)
639 {
640
641   u32 mask_index, searched_idx;
642   ipsec_policy_t *vp;
643   ipsec_fp_mask_type_entry_t *mte;
644   u32 policy_index;
645   clib_bihash_kv_40_8_t kv;
646   clib_bihash_kv_40_8_t result;
647   ipsec_fp_lookup_value_t *result_val =
648     (ipsec_fp_lookup_value_t *) &result.value;
649   ipsec_fp_lookup_value_t *key_val = (ipsec_fp_lookup_value_t *) &kv.value;
650
651   ipsec_fp_5tuple_t mask, policy_5tuple;
652   int res;
653   bool inbound = ipsec_is_policy_inbound (policy);
654
655   ipsec_fp_ip6_get_policy_mask (policy, &mask, inbound);
656   pool_get (im->policies, vp);
657   policy_index = vp - im->policies;
658   vlib_validate_combined_counter (&ipsec_spd_policy_counters, policy_index);
659   vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index);
660   *stat_index = policy_index;
661   mask_index = find_mask_type_index (im, &mask);
662   clib_bihash_40_8_t *bihash_table =
663     inbound ? pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
664                                  fp_spd->ip6_in_lookup_hash_idx) :
665                     pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
666                                  fp_spd->ip6_out_lookup_hash_idx);
667
668   if (mask_index == ~0)
669     {
670       /* mask type not found, we need to create a new entry */
671       pool_get (im->fp_mask_types, mte);
672       mask_index = mte - im->fp_mask_types;
673       mte->refcount = 0;
674     }
675   else
676     mte = im->fp_mask_types + mask_index;
677
678   policy->fp_mask_type_id = mask_index;
679   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
680
681   fill_ip6_hash_policy_kv (&policy_5tuple, &mask, &kv);
682
683   res = clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
684   if (res != 0)
685     {
686       /* key was not found crate a new entry */
687       vec_add1 (key_val->fp_policies_ids, policy_index);
688       res = clib_bihash_add_del_40_8 (bihash_table, &kv, 1);
689       if (res != 0)
690         goto error;
691     }
692   else
693     {
694
695       if (vec_max_len (result_val->fp_policies_ids) !=
696           vec_len (result_val->fp_policies_ids))
697         {
698           /* no need to resize */
699           vec_add1 (result_val->fp_policies_ids, policy_index);
700         }
701       else
702         {
703           vec_add1 (result_val->fp_policies_ids, policy_index);
704
705           res = clib_bihash_add_del_40_8 (bihash_table, &result, 1);
706
707           if (res != 0)
708             goto error;
709         }
710     }
711
712   if (mte->refcount == 0)
713     {
714       clib_memcpy (&mte->mask, &mask, sizeof (mask));
715       mte->refcount = 0;
716     }
717
718   searched_idx =
719     vec_search_with_function (fp_spd->fp_mask_ids[policy->type], &mask_index,
720                               ipsec_fp_mask_type_idx_cmp);
721   if (~0 == searched_idx)
722     {
723       ipsec_fp_mask_id_t mask_id = { mask_index, 1 };
724       vec_add1 (fp_spd->fp_mask_ids[policy->type], mask_id);
725     }
726   else
727     (fp_spd->fp_mask_ids[policy->type] + searched_idx)->refcount++;
728
729   mte->refcount++;
730   vec_add1 (fp_spd->fp_policies[policy->type], policy_index);
731   clib_memcpy (vp, policy, sizeof (*vp));
732
733   return 0;
734
735 error:
736   pool_put (im->policies, vp);
737   ipsec_fp_release_mask_type (im, mask_index);
738   return -1;
739 }
740
741 int
742 ipsec_fp_ip6_del_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
743                          ipsec_policy_t *policy)
744 {
745   int res;
746   ipsec_fp_5tuple_t mask = { 0 }, policy_5tuple;
747   clib_bihash_kv_40_8_t kv;
748   clib_bihash_kv_40_8_t result;
749   ipsec_fp_lookup_value_t *result_val =
750     (ipsec_fp_lookup_value_t *) &result.value;
751   bool inbound = ipsec_is_policy_inbound (policy);
752   clib_bihash_40_8_t *bihash_table =
753     inbound ? pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
754                                  fp_spd->ip6_in_lookup_hash_idx) :
755                     pool_elt_at_index (im->fp_ip6_lookup_hashes_pool,
756                                  fp_spd->ip6_out_lookup_hash_idx);
757
758   ipsec_policy_t *vp;
759   u32 ii, iii, imt;
760
761   ipsec_fp_ip6_get_policy_mask (policy, &mask, inbound);
762   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
763   fill_ip6_hash_policy_kv (&policy_5tuple, &mask, &kv);
764   res = clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result);
765   if (res != 0)
766     return -1;
767
768   res = -1;
769   vec_foreach_index (ii, result_val->fp_policies_ids)
770     {
771       vp =
772         pool_elt_at_index (im->policies, *(result_val->fp_policies_ids + ii));
773       if (ipsec_policy_is_equal (vp, policy))
774         {
775           vec_foreach_index (iii, fp_spd->fp_policies[policy->type])
776             {
777               if (*(fp_spd->fp_policies[policy->type] + iii) ==
778                   *(result_val->fp_policies_ids + ii))
779                 {
780                   if (vec_len (result_val->fp_policies_ids) == 1)
781                     {
782                       vec_free (result_val->fp_policies_ids);
783                       clib_bihash_add_del_40_8 (bihash_table, &result, 0);
784                     }
785                   else
786                     {
787                       vec_del1 (result_val->fp_policies_ids, ii);
788                     }
789                   vec_del1 (fp_spd->fp_policies[policy->type], iii);
790
791                   vec_foreach_index (imt, fp_spd->fp_mask_ids[policy->type])
792                     {
793                       if ((fp_spd->fp_mask_ids[policy->type] + imt)
794                             ->mask_type_idx == vp->fp_mask_type_id)
795                         {
796
797                           if ((fp_spd->fp_mask_ids[policy->type] + imt)
798                                 ->refcount-- == 1)
799                             vec_del1 (fp_spd->fp_mask_ids[policy->type], imt);
800
801                           break;
802                         }
803                     }
804
805                   res = 0;
806                   break;
807                 }
808             }
809
810           if (res != 0)
811             continue;
812           else
813             {
814               ipsec_fp_release_mask_type (im, vp->fp_mask_type_id);
815               ipsec_sa_unlock (vp->sa_index);
816               pool_put (im->policies, vp);
817               return 0;
818             }
819         }
820     }
821   return -1;
822 }
823
824 int
825 ipsec_fp_ip4_del_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd,
826                          ipsec_policy_t *policy)
827 {
828   int res;
829   ipsec_fp_5tuple_t mask = { 0 }, policy_5tuple;
830   clib_bihash_kv_16_8_t kv;
831   clib_bihash_kv_16_8_t result;
832   ipsec_fp_lookup_value_t *result_val =
833     (ipsec_fp_lookup_value_t *) &result.value;
834   bool inbound = ipsec_is_policy_inbound (policy);
835   ipsec_policy_t *vp;
836   u32 ii, iii, imt;
837   clib_bihash_16_8_t *bihash_table =
838     inbound ? pool_elt_at_index (im->fp_ip4_lookup_hashes_pool,
839                                  fp_spd->ip4_in_lookup_hash_idx) :
840                     pool_elt_at_index (im->fp_ip4_lookup_hashes_pool,
841                                  fp_spd->ip4_out_lookup_hash_idx);
842
843   ipsec_fp_ip4_get_policy_mask (policy, &mask, inbound);
844   ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound);
845   fill_ip4_hash_policy_kv (&policy_5tuple, &mask, &kv);
846   res = clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result);
847
848   if (res != 0)
849     return -1;
850
851   res = -1;
852   vec_foreach_index (ii, result_val->fp_policies_ids)
853     {
854       vp =
855         pool_elt_at_index (im->policies, *(result_val->fp_policies_ids + ii));
856       if (ipsec_policy_is_equal (vp, policy))
857         {
858           vec_foreach_index (iii, fp_spd->fp_policies[policy->type])
859             {
860               if (*(fp_spd->fp_policies[policy->type] + iii) ==
861                   *(result_val->fp_policies_ids + ii))
862                 {
863                   if (vec_len (result_val->fp_policies_ids) == 1)
864                     {
865                       vec_free (result_val->fp_policies_ids);
866                       clib_bihash_add_del_16_8 (bihash_table, &result, 0);
867                     }
868                   else
869                     {
870                       vec_del1 (result_val->fp_policies_ids, ii);
871                     }
872                   vec_del1 (fp_spd->fp_policies[policy->type], iii);
873
874                   vec_foreach_index (imt, fp_spd->fp_mask_ids[policy->type])
875                     {
876                       if ((fp_spd->fp_mask_ids[policy->type] + imt)
877                             ->mask_type_idx == vp->fp_mask_type_id)
878                         {
879
880                           if ((fp_spd->fp_mask_ids[policy->type] + imt)
881                                 ->refcount-- == 1)
882                             vec_del1 (fp_spd->fp_mask_ids[policy->type], imt);
883
884                           break;
885                         }
886                     }
887
888                   res = 0;
889                   break;
890                 }
891             }
892
893           if (res != 0)
894             continue;
895           else
896             {
897               ipsec_fp_release_mask_type (im, vp->fp_mask_type_id);
898               ipsec_sa_unlock (vp->sa_index);
899               pool_put (im->policies, vp);
900               return 0;
901             }
902         }
903     }
904   return -1;
905 }
906
907 int
908 ipsec_fp_add_del_policy (void *fp_spd, ipsec_policy_t *policy, int is_add,
909                          u32 *stat_index)
910 {
911   ipsec_main_t *im = &ipsec_main;
912
913   if (is_add)
914     if (policy->is_ipv6)
915       return ipsec_fp_ip6_add_policy (im, (ipsec_spd_fp_t *) fp_spd, policy,
916                                       stat_index);
917     else
918       return ipsec_fp_ip4_add_policy (im, (ipsec_spd_fp_t *) fp_spd, policy,
919                                       stat_index);
920
921   else if (policy->is_ipv6)
922
923     return ipsec_fp_ip6_del_policy (im, (ipsec_spd_fp_t *) fp_spd, policy);
924   else
925     return ipsec_fp_ip4_del_policy (im, (ipsec_spd_fp_t *) fp_spd, policy);
926 }
927
928 /*
929  * fd.io coding-style-patch-verification: ON
930  *
931  * Local Variables:
932  * eval: (c-set-style "gnu")
933  * End:
934  */