/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include /** * @brief * Policy packet & bytes counters */ vlib_combined_counter_main_t ipsec_spd_policy_counters = { .name = "policy", .stat_segment_name = "/net/ipsec/policy", }; int ipsec_policy_mk_type (bool is_outbound, bool is_ipv6, ipsec_policy_action_t action, ipsec_spd_policy_type_t * type) { if (is_outbound) { *type = (is_ipv6 ? IPSEC_SPD_POLICY_IP6_OUTBOUND : IPSEC_SPD_POLICY_IP4_OUTBOUND); return (0); } else { switch (action) { case IPSEC_POLICY_ACTION_PROTECT: *type = (is_ipv6 ? IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT : IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT); return (0); case IPSEC_POLICY_ACTION_BYPASS: *type = (is_ipv6 ? IPSEC_SPD_POLICY_IP6_INBOUND_BYPASS : IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS); return (0); case IPSEC_POLICY_ACTION_DISCARD: *type = (is_ipv6 ? IPSEC_SPD_POLICY_IP6_INBOUND_DISCARD : IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD); return (0); case IPSEC_POLICY_ACTION_RESOLVE: break; } } /* Unsupported type */ return (-1); } static_always_inline int ipsec_is_policy_inbound (ipsec_policy_t *policy) { if (policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT || policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS || policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD || policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT || policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_BYPASS || policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_DISCARD) return 1; return 0; } static_always_inline int ipsec_is_fp_enabled (ipsec_main_t *im, ipsec_spd_t *spd, ipsec_policy_t *policy) { if ((im->fp_spd_ipv4_out_is_enabled && PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip4_out_lookup_hash_idx) && policy->type == IPSEC_SPD_POLICY_IP4_OUTBOUND) || (im->fp_spd_ipv4_in_is_enabled && PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip4_in_lookup_hash_idx) && (policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT || policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS || policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD)) || (im->fp_spd_ipv6_in_is_enabled && PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip6_in_lookup_hash_idx) && (policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT || policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_BYPASS || policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_DISCARD)) || (im->fp_spd_ipv6_out_is_enabled && PREDICT_TRUE (INDEX_INVALID != spd->fp_spd.ip6_out_lookup_hash_idx) && policy->type == IPSEC_SPD_POLICY_IP6_OUTBOUND)) return 1; return 0; } int ipsec_add_del_policy (vlib_main_t * vm, ipsec_policy_t * policy, int is_add, u32 * stat_index) { ipsec_main_t *im = &ipsec_main; ipsec_spd_t *spd = 0; ipsec_policy_t *vp; u32 spd_index; uword *p; p = hash_get (im->spd_index_by_spd_id, policy->id); if (!p) return VNET_API_ERROR_SYSCALL_ERROR_1; spd_index = p[0]; spd = pool_elt_at_index (im->spds, spd_index); if (!spd) return VNET_API_ERROR_SYSCALL_ERROR_1; if (im->output_flow_cache_flag && !policy->is_ipv6 && policy->type == IPSEC_SPD_POLICY_IP4_OUTBOUND) { /* * Flow cache entry is valid only when epoch_count value in control * plane and data plane match. Otherwise, flow cache entry is considered * stale. To avoid the race condition of using old epoch_count value * in data plane after the roll over of epoch_count in control plane, * entire flow cache is reset. */ if (im->epoch_count == 0xFFFFFFFF) { /* Reset all the entries in flow cache */ clib_memset_u8 (im->ipsec4_out_spd_hash_tbl, 0, im->ipsec4_out_spd_hash_num_buckets * (sizeof (*(im->ipsec4_out_spd_hash_tbl)))); } /* Increment epoch counter by 1 */ clib_atomic_fetch_add_relax (&im->epoch_count, 1); /* Reset spd flow cache counter since all old entries are stale */ clib_atomic_store_relax_n (&im->ipsec4_out_spd_flow_cache_entries, 0); } if ((policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT || policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS || policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD) && im->input_flow_cache_flag && !policy->is_ipv6) { /* * Flow cache entry is valid only when input_epoch_count value in control * plane and data plane match. Otherwise, flow cache entry is considered * stale. To avoid the race condition of using old input_epoch_count * value in data plane after the roll over of input_epoch_count in * control plane, entire flow cache is reset. */ if (im->input_epoch_count == 0xFFFFFFFF) { /* Reset all the entries in flow cache */ clib_memset_u8 (im->ipsec4_in_spd_hash_tbl, 0, im->ipsec4_in_spd_hash_num_buckets * (sizeof (*(im->ipsec4_in_spd_hash_tbl)))); } /* Increment epoch counter by 1 */ clib_atomic_fetch_add_relax (&im->input_epoch_count, 1); /* Reset spd flow cache counter since all old entries are stale */ im->ipsec4_in_spd_flow_cache_entries = 0; } if (is_add) { u32 policy_index; u32 i; if (policy->policy == IPSEC_POLICY_ACTION_PROTECT) { index_t sa_index = ipsec_sa_find_and_lock (policy->sa_id); if (INDEX_INVALID == sa_index) return VNET_API_ERROR_SYSCALL_ERROR_1; policy->sa_index = sa_index; } else policy->sa_index = INDEX_INVALID; /** * Try adding the policy into fast path SPD first. Only adding to * traditional SPD when failed. **/ if (ipsec_is_fp_enabled (im, spd, policy)) return ipsec_fp_add_del_policy ((void *) &spd->fp_spd, policy, 1, stat_index); pool_get (im->policies, vp); clib_memcpy (vp, policy, sizeof (*vp)); policy_index = vp - im->policies; vlib_validate_combined_counter (&ipsec_spd_policy_counters, policy_index); vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index); vec_foreach_index (i, spd->policies[policy->type]) { ipsec_policy_t *p = pool_elt_at_index (im->policies, spd->policies[policy->type][i]); if (p->priority <= vp->priority) { break; } } vec_insert_elts (spd->policies[policy->type], &policy_index, 1, i); *stat_index = policy_index; } else { u32 ii; /** * Try to delete the policy from the fast path SPD first. Delete from * traditional SPD when fp delete fails. **/ if (ipsec_is_fp_enabled (im, spd, policy)) { if (policy->policy == IPSEC_POLICY_ACTION_PROTECT) { index_t sa_index = ipsec_sa_find_and_lock (policy->sa_id); if (INDEX_INVALID == sa_index) return VNET_API_ERROR_SYSCALL_ERROR_1; policy->sa_index = sa_index; ipsec_sa_unlock_id (policy->sa_id); } else policy->sa_index = INDEX_INVALID; return ipsec_fp_add_del_policy ((void *) &spd->fp_spd, policy, 0, stat_index); } vec_foreach_index (ii, (spd->policies[policy->type])) { vp = pool_elt_at_index (im->policies, spd->policies[policy->type][ii]); if (ipsec_policy_is_equal (vp, policy)) { vec_delete (spd->policies[policy->type], 1, ii); ipsec_sa_unlock (vp->sa_index); pool_put (im->policies, vp); break; } } } return 0; } static_always_inline void ipsec_fp_release_mask_type (ipsec_main_t *im, u32 mask_type_index) { ipsec_fp_mask_type_entry_t *mte = pool_elt_at_index (im->fp_mask_types, mask_type_index); mte->refcount--; if (mte->refcount == 0) { /* this entry is not in use anymore */ ASSERT (clib_memset (mte, 0xae, sizeof (*mte)) == EOK); pool_put (im->fp_mask_types, mte); } } static_always_inline u32 find_mask_type_index (ipsec_main_t *im, ipsec_fp_5tuple_t *mask) { ipsec_fp_mask_type_entry_t *mte; pool_foreach (mte, im->fp_mask_types) { if (memcmp (&mte->mask, mask, sizeof (*mask)) == 0) return (mte - im->fp_mask_types); } return ~0; } static_always_inline void fill_ip6_hash_policy_kv (ipsec_fp_5tuple_t *match, ipsec_fp_5tuple_t *mask, clib_bihash_kv_40_8_t *kv) { ipsec_fp_lookup_value_t *kv_val = (ipsec_fp_lookup_value_t *) &kv->value; u64 *pmatch = (u64 *) match->kv_40_8.key; u64 *pmask = (u64 *) mask->kv_40_8.key; u64 *pkey = (u64 *) kv->key; *pkey++ = *pmatch++ & *pmask++; *pkey++ = *pmatch++ & *pmask++; *pkey++ = *pmatch++ & *pmask++; *pkey++ = *pmatch++ & *pmask++; *pkey = *pmatch & *pmask; kv_val->as_u64 = 0; } static_always_inline void fill_ip4_hash_policy_kv (ipsec_fp_5tuple_t *match, ipsec_fp_5tuple_t *mask, clib_bihash_kv_16_8_t *kv) { ipsec_fp_lookup_value_t *kv_val = (ipsec_fp_lookup_value_t *) &kv->value; u64 *pmatch = (u64 *) match->kv_16_8.key; u64 *pmask = (u64 *) mask->kv_16_8.key; u64 *pkey = (u64 *) kv->key; *pkey++ = *pmatch++ & *pmask++; *pkey = *pmatch & *pmask; kv_val->as_u64 = 0; } static_always_inline u16 mask_out_highest_set_bit_u16 (u16 x) { x |= x >> 8; x |= x >> 4; x |= x >> 2; x |= x >> 1; return ~x; } static_always_inline u32 mask_out_highest_set_bit_u32 (u32 x) { x |= x >> 16; x |= x >> 8; x |= x >> 4; x |= x >> 2; x |= x >> 1; return ~x; } static_always_inline u64 mask_out_highest_set_bit_u64 (u64 x) { x |= x >> 32; x |= x >> 16; x |= x >> 8; x |= x >> 4; x |= x >> 2; x |= x >> 1; return ~x; } static_always_inline void ipsec_fp_get_policy_ports_mask (ipsec_policy_t *policy, ipsec_fp_5tuple_t *mask) { if (PREDICT_TRUE ((policy->protocol == IP_PROTOCOL_TCP) || (policy->protocol == IP_PROTOCOL_UDP) || (policy->protocol == IP_PROTOCOL_SCTP))) { mask->lport = policy->lport.start ^ policy->lport.stop; mask->rport = policy->rport.start ^ policy->rport.stop; mask->lport = mask_out_highest_set_bit_u16 (mask->lport); mask->rport = mask_out_highest_set_bit_u16 (mask->rport); } else { mask->lport = 0; mask->rport = 0; } mask->protocol = (policy->protocol == IPSEC_POLICY_PROTOCOL_ANY) ? 0 : ~0; } static_always_inline void ipsec_fp_ip4_get_policy_mask (ipsec_policy_t *policy, ipsec_fp_5tuple_t *mask, bool inbound) { u32 *pladdr_start = (u32 *) &policy->laddr.start.ip4; u32 *pladdr_stop = (u32 *) &policy->laddr.stop.ip4; u32 *plmask = (u32 *) &mask->laddr; u32 *praddr_start = (u32 *) &policy->raddr.start.ip4; u32 *praddr_stop = (u32 *) &policy->raddr.stop.ip4; u32 *prmask = (u32 *) &mask->raddr; clib_memset_u8 (mask, 0xff, sizeof (ipsec_fp_5tuple_t)); clib_memset_u8 (&mask->l3_zero_pad, 0, sizeof (mask->l3_zero_pad)); if (inbound && (policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT && policy->sa_index != INDEX_INVALID)) { ipsec_sa_t *s = ipsec_sa_get (policy->sa_index); if (ipsec_sa_is_set_IS_TUNNEL (s)) goto set_spi_mask; } /* find bits where start != stop */ *plmask = *pladdr_start ^ *pladdr_stop; *prmask = *praddr_start ^ *praddr_stop; /* Find most significant bit set (that is the first position * start differs from stop). Mask out everything after that bit and * the bit itself. Remember that policy stores start and stop in the net * order. */ *plmask = clib_host_to_net_u32 ( mask_out_highest_set_bit_u32 (clib_net_to_host_u32 (*plmask))); *prmask = clib_host_to_net_u32 ( mask_out_highest_set_bit_u32 (clib_net_to_host_u32 (*prmask))); set_spi_mask: if (inbound) { if (policy->type != IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT) mask->spi = 0; mask->protocol = 0; } else { mask->action = 0; ipsec_fp_get_policy_ports_mask (policy, mask); } } static_always_inline void ipsec_fp_ip6_get_policy_mask (ipsec_policy_t *policy, ipsec_fp_5tuple_t *mask, bool inbound) { u64 *pladdr_start = (u64 *) &policy->laddr.start; u64 *pladdr_stop = (u64 *) &policy->laddr.stop; u64 *plmask = (u64 *) &mask->ip6_laddr; u64 *praddr_start = (u64 *) &policy->raddr.start; u64 *praddr_stop = (u64 *) &policy->raddr.stop; u64 *prmask = (u64 *) &mask->ip6_raddr; clib_memset_u8 (mask, 0xff, sizeof (ipsec_fp_5tuple_t)); if (inbound && (policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT && policy->sa_index != INDEX_INVALID)) { ipsec_sa_t *s = ipsec_sa_get (policy->sa_index); if (ipsec_sa_is_set_IS_TUNNEL (s)) goto set_spi_mask; } *plmask = (*pladdr_start++ ^ *pladdr_stop++); *prmask = (*praddr_start++ ^ *praddr_stop++); /* Find most significant bit set (that is the first position * start differs from stop). Mask out everything after that bit and * the bit itself. Remember that policy stores start and stop in the net * order. */ *plmask = clib_host_to_net_u64 ( mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*plmask))); if (*plmask++ & clib_host_to_net_u64 (0x1)) { *plmask = (*pladdr_start ^ *pladdr_stop); *plmask = clib_host_to_net_u64 ( mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*plmask))); } else *plmask = 0; *prmask = clib_host_to_net_u64 ( mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*prmask))); if (*prmask++ & clib_host_to_net_u64 (0x1)) { *prmask = (*praddr_start ^ *praddr_stop); *prmask = clib_host_to_net_u64 ( mask_out_highest_set_bit_u64 (clib_net_to_host_u64 (*prmask))); } else *prmask = 0; set_spi_mask: if (inbound) { if (policy->type != IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT) mask->spi = 0; mask->protocol = 0; } else { mask->action = 0; ipsec_fp_get_policy_ports_mask (policy, mask); } } static_always_inline void ipsec_fp_get_policy_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *tuple, bool inbound) { memset (tuple, 0, sizeof (*tuple)); tuple->is_ipv6 = policy->is_ipv6; if (tuple->is_ipv6) { tuple->ip6_laddr = policy->laddr.start.ip6; tuple->ip6_raddr = policy->raddr.start.ip6; } else { tuple->laddr = policy->laddr.start.ip4; tuple->raddr = policy->raddr.start.ip4; } if (inbound) { if ((policy->type == IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT || policy->type == IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT) && policy->sa_index != INDEX_INVALID) { ipsec_sa_t *s = ipsec_sa_get (policy->sa_index); tuple->spi = s->spi; if (ipsec_sa_is_set_IS_TUNNEL (s)) { if (tuple->is_ipv6) { tuple->ip6_laddr = s->tunnel.t_dst.ip.ip6; tuple->ip6_raddr = s->tunnel.t_src.ip.ip6; } else { tuple->laddr = s->tunnel.t_dst.ip.ip4; tuple->raddr = s->tunnel.t_src.ip.ip4; } } } else tuple->spi = INDEX_INVALID; tuple->action = policy->type; return; } tuple->protocol = policy->protocol; tuple->lport = policy->lport.start; tuple->rport = policy->rport.start; } static_always_inline int ipsec_fp_mask_type_idx_cmp (ipsec_fp_mask_id_t *mask_id, u32 *idx) { return mask_id->mask_type_idx == *idx; } int ipsec_fp_ip4_add_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd, ipsec_policy_t *policy, u32 *stat_index) { u32 mask_index, searched_idx; ipsec_policy_t *vp; ipsec_fp_mask_type_entry_t *mte; u32 policy_index; clib_bihash_kv_16_8_t kv; clib_bihash_kv_16_8_t result; ipsec_fp_lookup_value_t *result_val = (ipsec_fp_lookup_value_t *) &result.value; ipsec_fp_lookup_value_t *key_val = (ipsec_fp_lookup_value_t *) &kv.value; ipsec_fp_5tuple_t mask, policy_5tuple; int res; bool inbound = ipsec_is_policy_inbound (policy); clib_bihash_16_8_t *bihash_table = inbound ? pool_elt_at_index (im->fp_ip4_lookup_hashes_pool, fp_spd->ip4_in_lookup_hash_idx) : pool_elt_at_index (im->fp_ip4_lookup_hashes_pool, fp_spd->ip4_out_lookup_hash_idx); ipsec_fp_ip4_get_policy_mask (policy, &mask, inbound); pool_get (im->policies, vp); policy_index = vp - im->policies; vlib_validate_combined_counter (&ipsec_spd_policy_counters, policy_index); vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index); *stat_index = policy_index; mask_index = find_mask_type_index (im, &mask); if (mask_index == ~0) { /* mask type not found, we need to create a new entry */ pool_get (im->fp_mask_types, mte); mask_index = mte - im->fp_mask_types; mte->refcount = 0; } else mte = im->fp_mask_types + mask_index; policy->fp_mask_type_id = mask_index; ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound); fill_ip4_hash_policy_kv (&policy_5tuple, &mask, &kv); res = clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result); if (res != 0) { /* key was not found crate a new entry */ vec_add1 (key_val->fp_policies_ids, policy_index); res = clib_bihash_add_del_16_8 (bihash_table, &kv, 1); if (res != 0) goto error; } else { u32 i; u32 *old_fp_policies_ids = result_val->fp_policies_ids; vec_foreach_index (i, result_val->fp_policies_ids) { ipsec_policy_t *p = pool_elt_at_index (im->policies, result_val->fp_policies_ids[i]); if (p->priority <= policy->priority) { break; } } vec_insert_elts (result_val->fp_policies_ids, &policy_index, 1, i); if (result_val->fp_policies_ids != old_fp_policies_ids) { res = clib_bihash_add_del_16_8 (bihash_table, &result, 1); if (res != 0) goto error; } } if (mte->refcount == 0) { clib_memcpy (&mte->mask, &mask, sizeof (mask)); mte->refcount = 0; } searched_idx = vec_search_with_function (fp_spd->fp_mask_ids[policy->type], &mask_index, ipsec_fp_mask_type_idx_cmp); if (~0 == searched_idx) { ipsec_fp_mask_id_t mask_id = { mask_index, 1 }; vec_add1 (fp_spd->fp_mask_ids[policy->type], mask_id); } else (fp_spd->fp_mask_ids[policy->type] + searched_idx)->refcount++; mte->refcount++; clib_memcpy (vp, policy, sizeof (*vp)); return 0; error: pool_put (im->policies, vp); ipsec_fp_release_mask_type (im, mask_index); return -1; } int ipsec_fp_ip6_add_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd, ipsec_policy_t *policy, u32 *stat_index) { u32 mask_index, searched_idx; ipsec_policy_t *vp; ipsec_fp_mask_type_entry_t *mte; u32 policy_index; clib_bihash_kv_40_8_t kv; clib_bihash_kv_40_8_t result; ipsec_fp_lookup_value_t *result_val = (ipsec_fp_lookup_value_t *) &result.value; ipsec_fp_lookup_value_t *key_val = (ipsec_fp_lookup_value_t *) &kv.value; ipsec_fp_5tuple_t mask, policy_5tuple; int res; bool inbound = ipsec_is_policy_inbound (policy); ipsec_fp_ip6_get_policy_mask (policy, &mask, inbound); pool_get (im->policies, vp); policy_index = vp - im->policies; vlib_validate_combined_counter (&ipsec_spd_policy_counters, policy_index); vlib_zero_combined_counter (&ipsec_spd_policy_counters, policy_index); *stat_index = policy_index; mask_index = find_mask_type_index (im, &mask); clib_bihash_40_8_t *bihash_table = inbound ? pool_elt_at_index (im->fp_ip6_lookup_hashes_pool, fp_spd->ip6_in_lookup_hash_idx) : pool_elt_at_index (im->fp_ip6_lookup_hashes_pool, fp_spd->ip6_out_lookup_hash_idx); if (mask_index == ~0) { /* mask type not found, we need to create a new entry */ pool_get (im->fp_mask_types, mte); mask_index = mte - im->fp_mask_types; mte->refcount = 0; } else mte = im->fp_mask_types + mask_index; policy->fp_mask_type_id = mask_index; ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound); fill_ip6_hash_policy_kv (&policy_5tuple, &mask, &kv); res = clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result); if (res != 0) { /* key was not found crate a new entry */ vec_add1 (key_val->fp_policies_ids, policy_index); res = clib_bihash_add_del_40_8 (bihash_table, &kv, 1); if (res != 0) goto error; } else { u32 i; u32 *old_fp_policies_ids = result_val->fp_policies_ids; vec_foreach_index (i, result_val->fp_policies_ids) { ipsec_policy_t *p = pool_elt_at_index (im->policies, result_val->fp_policies_ids[i]); if (p->priority <= policy->priority) { break; } } vec_insert_elts (result_val->fp_policies_ids, &policy_index, 1, i); if (result_val->fp_policies_ids != old_fp_policies_ids) { res = clib_bihash_add_del_40_8 (bihash_table, &result, 1); if (res != 0) goto error; } } if (mte->refcount == 0) { clib_memcpy (&mte->mask, &mask, sizeof (mask)); mte->refcount = 0; } searched_idx = vec_search_with_function (fp_spd->fp_mask_ids[policy->type], &mask_index, ipsec_fp_mask_type_idx_cmp); if (~0 == searched_idx) { ipsec_fp_mask_id_t mask_id = { mask_index, 1 }; vec_add1 (fp_spd->fp_mask_ids[policy->type], mask_id); } else (fp_spd->fp_mask_ids[policy->type] + searched_idx)->refcount++; mte->refcount++; clib_memcpy (vp, policy, sizeof (*vp)); return 0; error: pool_put (im->policies, vp); ipsec_fp_release_mask_type (im, mask_index); return -1; } int ipsec_fp_ip6_del_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd, ipsec_policy_t *policy) { int res; ipsec_fp_5tuple_t mask = { 0 }, policy_5tuple; clib_bihash_kv_40_8_t kv; clib_bihash_kv_40_8_t result; ipsec_fp_lookup_value_t *result_val = (ipsec_fp_lookup_value_t *) &result.value; bool inbound = ipsec_is_policy_inbound (policy); clib_bihash_40_8_t *bihash_table = inbound ? pool_elt_at_index (im->fp_ip6_lookup_hashes_pool, fp_spd->ip6_in_lookup_hash_idx) : pool_elt_at_index (im->fp_ip6_lookup_hashes_pool, fp_spd->ip6_out_lookup_hash_idx); ipsec_policy_t *vp; u32 ii, imt; ipsec_fp_ip6_get_policy_mask (policy, &mask, inbound); ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound); fill_ip6_hash_policy_kv (&policy_5tuple, &mask, &kv); res = clib_bihash_search_inline_2_40_8 (bihash_table, &kv, &result); if (res != 0) return -1; vec_foreach_index (ii, result_val->fp_policies_ids) { vp = pool_elt_at_index (im->policies, *(result_val->fp_policies_ids + ii)); if (ipsec_policy_is_equal (vp, policy)) { if (vec_len (result_val->fp_policies_ids) == 1) { vec_free (result_val->fp_policies_ids); clib_bihash_add_del_40_8 (bihash_table, &result, 0); } else vec_delete (result_val->fp_policies_ids, 1, ii); vec_foreach_index (imt, fp_spd->fp_mask_ids[policy->type]) { if ((fp_spd->fp_mask_ids[policy->type] + imt)->mask_type_idx == vp->fp_mask_type_id) { if ((fp_spd->fp_mask_ids[policy->type] + imt)->refcount-- == 1) vec_del1 (fp_spd->fp_mask_ids[policy->type], imt); break; } } ipsec_fp_release_mask_type (im, vp->fp_mask_type_id); ipsec_sa_unlock (vp->sa_index); pool_put (im->policies, vp); return 0; } } return -1; } int ipsec_fp_ip4_del_policy (ipsec_main_t *im, ipsec_spd_fp_t *fp_spd, ipsec_policy_t *policy) { int res; ipsec_fp_5tuple_t mask = { 0 }, policy_5tuple; clib_bihash_kv_16_8_t kv; clib_bihash_kv_16_8_t result; ipsec_fp_lookup_value_t *result_val = (ipsec_fp_lookup_value_t *) &result.value; bool inbound = ipsec_is_policy_inbound (policy); ipsec_policy_t *vp; u32 ii, imt; clib_bihash_16_8_t *bihash_table = inbound ? pool_elt_at_index (im->fp_ip4_lookup_hashes_pool, fp_spd->ip4_in_lookup_hash_idx) : pool_elt_at_index (im->fp_ip4_lookup_hashes_pool, fp_spd->ip4_out_lookup_hash_idx); ipsec_fp_ip4_get_policy_mask (policy, &mask, inbound); ipsec_fp_get_policy_5tuple (policy, &policy_5tuple, inbound); fill_ip4_hash_policy_kv (&policy_5tuple, &mask, &kv); res = clib_bihash_search_inline_2_16_8 (bihash_table, &kv, &result); if (res != 0) return -1; vec_foreach_index (ii, result_val->fp_policies_ids) { vp = pool_elt_at_index (im->policies, *(result_val->fp_policies_ids + ii)); if (ipsec_policy_is_equal (vp, policy)) { if (vec_len (result_val->fp_policies_ids) == 1) { vec_free (result_val->fp_policies_ids); clib_bihash_add_del_16_8 (bihash_table, &result, 0); } else vec_delete (result_val->fp_policies_ids, 1, ii); vec_foreach_index (imt, fp_spd->fp_mask_ids[policy->type]) { if ((fp_spd->fp_mask_ids[policy->type] + imt)->mask_type_idx == vp->fp_mask_type_id) { if ((fp_spd->fp_mask_ids[policy->type] + imt)->refcount-- == 1) vec_del1 (fp_spd->fp_mask_ids[policy->type], imt); break; } } ipsec_fp_release_mask_type (im, vp->fp_mask_type_id); ipsec_sa_unlock (vp->sa_index); pool_put (im->policies, vp); return 0; } } return -1; } int ipsec_fp_add_del_policy (void *fp_spd, ipsec_policy_t *policy, int is_add, u32 *stat_index) { ipsec_main_t *im = &ipsec_main; if (is_add) if (policy->is_ipv6) return ipsec_fp_ip6_add_policy (im, (ipsec_spd_fp_t *) fp_spd, policy, stat_index); else return ipsec_fp_ip4_add_policy (im, (ipsec_spd_fp_t *) fp_spd, policy, stat_index); else if (policy->is_ipv6) return ipsec_fp_ip6_del_policy (im, (ipsec_spd_fp_t *) fp_spd, policy); else return ipsec_fp_ip4_del_policy (im, (ipsec_spd_fp_t *) fp_spd, policy); } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */