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