+always_inline void
+ipsec4_input_spd_add_flow_cache_entry (ipsec_main_t *im, u32 sa, u32 da,
+ ipsec_spd_policy_type_t policy_type,
+ u32 pol_id)
+{
+ u64 hash;
+ u8 is_overwrite = 0, is_stale_overwrite = 0;
+ /* Store in network byte order to avoid conversion on lookup */
+ ipsec4_inbound_spd_tuple_t ip4_tuple = {
+ .ip4_src_addr = (ip4_address_t) clib_host_to_net_u32 (sa),
+ .ip4_dest_addr = (ip4_address_t) clib_host_to_net_u32 (da),
+ .policy_type = policy_type
+ };
+
+ ip4_tuple.kv_16_8.value =
+ (((u64) pol_id) << 32) | ((u64) im->input_epoch_count);
+
+ hash = ipsec4_hash_16_8 (&ip4_tuple.kv_16_8);
+ hash &= (im->ipsec4_in_spd_hash_num_buckets - 1);
+
+ ipsec_spinlock_lock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
+ /* Check if we are overwriting an existing entry so we know
+ whether to increment the flow cache counter. Since flow
+ cache counter is reset on any policy add/remove, but
+ hash table values are not, we need to check if the entry
+ we are overwriting is stale or not. If it's a stale entry
+ overwrite, we still want to increment flow cache counter */
+ is_overwrite = (im->ipsec4_in_spd_hash_tbl[hash].value != 0);
+ /* Check if we are overwriting a stale entry by comparing
+ with current epoch count */
+ if (PREDICT_FALSE (is_overwrite))
+ is_stale_overwrite =
+ (im->input_epoch_count !=
+ ((u32) (im->ipsec4_in_spd_hash_tbl[hash].value & 0xFFFFFFFF)));
+ clib_memcpy_fast (&im->ipsec4_in_spd_hash_tbl[hash], &ip4_tuple.kv_16_8,
+ sizeof (ip4_tuple.kv_16_8));
+ ipsec_spinlock_unlock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
+
+ /* Increment the counter to track active flow cache entries
+ when entering a fresh entry or overwriting a stale one */
+ if (!is_overwrite || is_stale_overwrite)
+ clib_atomic_fetch_add_relax (&im->ipsec4_in_spd_flow_cache_entries, 1);
+
+ return;
+}
+
+always_inline ipsec_policy_t *
+ipsec4_input_spd_find_flow_cache_entry (ipsec_main_t *im, u32 sa, u32 da,
+ ipsec_spd_policy_type_t policy_type)
+{
+ ipsec_policy_t *p = NULL;
+ ipsec4_hash_kv_16_8_t kv_result;
+ u64 hash;
+ ipsec4_inbound_spd_tuple_t ip4_tuple = { .ip4_src_addr = (ip4_address_t) sa,
+ .ip4_dest_addr = (ip4_address_t) da,
+ .policy_type = policy_type };
+
+ hash = ipsec4_hash_16_8 (&ip4_tuple.kv_16_8);
+ hash &= (im->ipsec4_in_spd_hash_num_buckets - 1);
+
+ ipsec_spinlock_lock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
+ kv_result = im->ipsec4_in_spd_hash_tbl[hash];
+ ipsec_spinlock_unlock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
+
+ if (ipsec4_hash_key_compare_16_8 ((u64 *) &ip4_tuple.kv_16_8,
+ (u64 *) &kv_result))
+ {
+ if (im->input_epoch_count == ((u32) (kv_result.value & 0xFFFFFFFF)))
+ {
+ /* Get the policy based on the index */
+ p =
+ pool_elt_at_index (im->policies, ((u32) (kv_result.value >> 32)));
+ }
+ }
+
+ return p;
+}
+
+always_inline void
+ipsec_fp_in_5tuple_from_ip4_range (ipsec_fp_5tuple_t *tuple, u32 sa, u32 da,
+ u32 spi, u8 action)
+{
+ clib_memset (tuple->l3_zero_pad, 0, sizeof (tuple->l3_zero_pad));
+ tuple->laddr.as_u32 = da;
+ tuple->raddr.as_u32 = sa;
+ tuple->spi = spi;
+ tuple->action = action;
+ tuple->is_ipv6 = 0;
+}
+
+always_inline void
+ipsec_fp_in_5tuple_from_ip6_range (ipsec_fp_5tuple_t *tuple, ip6_address_t *sa,
+ ip6_address_t *da, u32 spi, u8 action)
+
+{
+ clib_memcpy (&tuple->ip6_laddr, da, sizeof (ip6_address_t));
+ clib_memcpy (&tuple->ip6_raddr, sa, sizeof (ip6_address_t));
+
+ tuple->spi = spi;
+ tuple->action = action;
+ tuple->is_ipv6 = 1;
+}
+
+always_inline ipsec_policy_t *
+ipsec_input_policy_match (ipsec_spd_t *spd, u32 sa, u32 da,
+ ipsec_spd_policy_type_t policy_type)
+{
+ ipsec_main_t *im = &ipsec_main;
+ ipsec_policy_t *p;
+ u32 *i;
+
+ vec_foreach (i, spd->policies[policy_type])
+ {
+ p = pool_elt_at_index (im->policies, *i);
+
+ if (da < clib_net_to_host_u32 (p->laddr.start.ip4.as_u32))
+ continue;
+
+ if (da > clib_net_to_host_u32 (p->laddr.stop.ip4.as_u32))
+ continue;
+
+ if (sa < clib_net_to_host_u32 (p->raddr.start.ip4.as_u32))
+ continue;
+
+ if (sa > clib_net_to_host_u32 (p->raddr.stop.ip4.as_u32))
+ continue;
+
+ if (im->input_flow_cache_flag)
+ {
+ /* Add an Entry in Flow cache */
+ ipsec4_input_spd_add_flow_cache_entry (im, sa, da, policy_type, *i);
+ }
+ return p;
+ }
+ return 0;
+}
+