/* *------------------------------------------------------------------ * Copyright (c) 2022 Intel 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. *------------------------------------------------------------------ */ #ifndef IPSEC_SPD_FP_LOOKUP_H #define IPSEC_SPD_FP_LOOKUP_H #include /** * @brief function handler to perform lookup in fastpath SPD * for inbound traffic burst of n packets **/ inline u32 ipsec_fp_in_policy_match_n (void *spd_fp, u8 is_ipv6, ipsec_fp_5tuple_t *tuples, ipsec_policy_t **policies, u32 *policy_ids, u32 n) { return 0; } static_always_inline int single_rule_match_5tuple (ipsec_policy_t *policy, ipsec_fp_5tuple_t *match) { if (PREDICT_FALSE (policy->is_ipv6 != match->is_ipv6)) return (0); if (PREDICT_FALSE (policy->protocol != IPSEC_POLICY_PROTOCOL_ANY && (policy->protocol != match->protocol))) return (0); if (!policy->is_ipv6) { if (PREDICT_FALSE ( clib_net_to_host_u32 (match->laddr.as_u32) < clib_net_to_host_u32 (policy->laddr.start.ip4.as_u32))) return (0); if (PREDICT_FALSE (clib_net_to_host_u32 (match->laddr.as_u32) > clib_net_to_host_u32 (policy->laddr.stop.ip4.as_u32))) return (0); if (PREDICT_FALSE ( clib_net_to_host_u32 (match->raddr.as_u32) < clib_net_to_host_u32 (policy->raddr.start.ip4.as_u32))) return (0); if (PREDICT_FALSE (clib_net_to_host_u32 (match->raddr.as_u32) > clib_net_to_host_u32 (policy->raddr.stop.ip4.as_u32))) return (0); } else { if (ip6_address_compare (&match->ip6_laddr, &policy->laddr.start.ip6) < 0) return (0); if (ip6_address_compare (&policy->laddr.stop.ip6, &match->ip6_laddr) < 0) return (0); if (ip6_address_compare (&match->ip6_raddr, &policy->raddr.start.ip6) < 0) return (0); if (ip6_address_compare (&policy->raddr.stop.ip6, &match->ip6_raddr) < 0) return (0); } if (PREDICT_FALSE ((match->protocol != IP_PROTOCOL_TCP) && (match->protocol != IP_PROTOCOL_UDP) && (match->protocol != IP_PROTOCOL_SCTP))) { return (1); } if (match->lport < policy->lport.start) return (0); if (match->lport > policy->lport.stop) return (0); if (match->rport < policy->rport.start) return (0); if (match->rport > policy->rport.stop) return (0); return (1); } static_always_inline u32 ipsec_fp_ip6_out_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples, ipsec_policy_t **policies, u32 *ids, u32 n) { u32 last_priority[n]; u32 i = 0; u32 counter = 0; ipsec_fp_mask_type_entry_t *mte; u32 *mti; ipsec_fp_5tuple_t *match = tuples; ipsec_policy_t *policy; u32 n_left = n; clib_bihash_kv_40_8_t kv; /* result of the lookup */ clib_bihash_kv_40_8_t result; ipsec_fp_lookup_value_t *result_val = (ipsec_fp_lookup_value_t *) &result.value; u64 *pkey, *pmatch, *pmask; ipsec_main_t *im = &ipsec_main; ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp; u32 *mask_type_ids = pspd_fp->fp_mask_types[IPSEC_SPD_POLICY_IP6_OUTBOUND]; /*clear the list of matched policies pointers */ clib_memset (policies, 0, n * sizeof (*policies)); clib_memset (last_priority, 0, n * sizeof (u32)); n_left = n; while (n_left) { vec_foreach (mti, mask_type_ids) { mte = im->fp_mask_types + *mti; pmatch = (u64 *) match->kv_40_8.key; pmask = (u64 *) mte->mask.kv_40_8.key; pkey = (u64 *) kv.key; *pkey++ = *pmatch++ & *pmask++; *pkey++ = *pmatch++ & *pmask++; *pkey++ = *pmatch++ & *pmask++; *pkey++ = *pmatch++ & *pmask++; *pkey = *pmatch & *pmask; int res = clib_bihash_search_inline_2_40_8 ( &pspd_fp->fp_ip6_lookup_hash, &kv, &result); /* lookup the hash by each packet in the burst for this mask. */ if (res == 0) { /* There is a hit in the hash table. */ /* Find the policy with highest priority. */ /* Store the lookup results in a dedicated array. */ if (vec_len (result_val->fp_policies_ids) > 1) { u32 *policy_id; vec_foreach (policy_id, result_val->fp_policies_ids) { policy = im->policies + *policy_id; if (single_rule_match_5tuple (policy, match)) { if (last_priority[i] < policy->priority) { last_priority[i] = policy->priority; if (policies[i] == 0) counter++; policies[i] = policy; ids[i] = *policy_id; } } } } else { u32 *policy_id; ASSERT (vec_len (result_val->fp_policies_ids) == 1); policy_id = result_val->fp_policies_ids; policy = im->policies + *policy_id; if (single_rule_match_5tuple (policy, match)) { if (last_priority[i] < policy->priority) { last_priority[i] = policy->priority; if (policies[i] == 0) counter++; policies[i] = policy; ids[i] = *policy_id; } } } } } n_left--; match++; i++; } return counter; } static_always_inline u32 ipsec_fp_ip4_out_policy_match_n (void *spd_fp, ipsec_fp_5tuple_t *tuples, ipsec_policy_t **policies, u32 *ids, u32 n) { u32 last_priority[n]; u32 i = 0; u32 counter = 0; ipsec_fp_mask_type_entry_t *mte; u32 *mti; ipsec_fp_5tuple_t *match = tuples; ipsec_policy_t *policy; u32 n_left = n; clib_bihash_kv_16_8_t kv; /* result of the lookup */ clib_bihash_kv_16_8_t result; ipsec_fp_lookup_value_t *result_val = (ipsec_fp_lookup_value_t *) &result.value; u64 *pkey, *pmatch, *pmask; ipsec_main_t *im = &ipsec_main; ipsec_spd_fp_t *pspd_fp = (ipsec_spd_fp_t *) spd_fp; u32 *mask_type_ids = pspd_fp->fp_mask_types[IPSEC_SPD_POLICY_IP4_OUTBOUND]; /* clear the list of matched policies pointers */ clib_memset (policies, 0, n * sizeof (*policies)); clib_memset (last_priority, 0, n * sizeof (u32)); n_left = n; while (n_left) { vec_foreach (mti, mask_type_ids) { mte = im->fp_mask_types + *mti; pmatch = (u64 *) match->kv_16_8.key; pmask = (u64 *) mte->mask.kv_16_8.key; pkey = (u64 *) kv.key; *pkey++ = *pmatch++ & *pmask++; *pkey = *pmatch & *pmask; int res = clib_bihash_search_inline_2_16_8 ( &pspd_fp->fp_ip4_lookup_hash, &kv, &result); /* lookup the hash by each packet in the burst for this mask. */ if (res == 0) { /* There is a hit in the hash table. */ /* Find the policy with highest priority. */ /* Store the lookup results in a dedicated array. */ if (vec_len (result_val->fp_policies_ids) > 1) { u32 *policy_id; vec_foreach (policy_id, result_val->fp_policies_ids) { policy = im->policies + *policy_id; if ((last_priority[i] < policy->priority) && (single_rule_match_5tuple (policy, match))) { last_priority[i] = policy->priority; if (policies[i] == 0) counter++; policies[i] = policy; ids[i] = *policy_id; } } } else { u32 *policy_id; ASSERT (vec_len (result_val->fp_policies_ids) == 1); policy_id = result_val->fp_policies_ids; policy = im->policies + *policy_id; if ((last_priority[i] < policy->priority) && (single_rule_match_5tuple (policy, match))) { last_priority[i] = policy->priority; if (policies[i] == 0) counter++; policies[i] = policy; ids[i] = *policy_id; } } } } i++; n_left--; match++; } return counter; } /** * @brief function handler to perform lookup in fastpath SPD * for outbound traffic burst of n packets * returns number of successfully matched policies **/ static_always_inline u32 ipsec_fp_out_policy_match_n (void *spd_fp, u8 is_ipv6, ipsec_fp_5tuple_t *tuples, ipsec_policy_t **policies, u32 *ids, u32 n) { if (is_ipv6) return ipsec_fp_ip6_out_policy_match_n (spd_fp, tuples, policies, ids, n); else return ipsec_fp_ip4_out_policy_match_n (spd_fp, tuples, policies, ids, n); } #endif /* !IPSEC_SPD_FP_LOOKUP_H */