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