LCOV - code coverage report
Current view: top level - vnet/ipsec - ipsec_input.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 317 369 85.9 %
Date: 2023-10-26 01:39:38 Functions: 23 29 79.3 %

          Line data    Source code
       1             : /*
       2             :  * decap.c : IPSec tunnel decapsulation
       3             :  *
       4             :  * Copyright (c) 2015 Cisco and/or its affiliates.
       5             :  * Licensed under the Apache License, Version 2.0 (the "License");
       6             :  * you may not use this file except in compliance with the License.
       7             :  * You may obtain a copy of the License at:
       8             :  *
       9             :  *     http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             :  */
      17             : 
      18             : #include <vnet/vnet.h>
      19             : #include <vnet/api_errno.h>
      20             : #include <vnet/ip/ip.h>
      21             : #include <vnet/feature/feature.h>
      22             : #include <vnet/ipsec/ipsec_spd_fp_lookup.h>
      23             : 
      24             : #include <vnet/ipsec/ipsec.h>
      25             : #include <vnet/ipsec/esp.h>
      26             : #include <vnet/ipsec/ah.h>
      27             : #include <vnet/ipsec/ipsec_io.h>
      28             : 
      29             : #define foreach_ipsec_input_error                       \
      30             : _(RX_PKTS, "IPSec pkts received")                     \
      31             : _(RX_POLICY_MATCH, "IPSec policy match")              \
      32             : _(RX_POLICY_NO_MATCH, "IPSec policy not matched")     \
      33             : _(RX_POLICY_BYPASS, "IPSec policy bypass")            \
      34             : _(RX_POLICY_DISCARD, "IPSec policy discard")
      35             : 
      36             : typedef enum
      37             : {
      38             : #define _(sym,str) IPSEC_INPUT_ERROR_##sym,
      39             :   foreach_ipsec_input_error
      40             : #undef _
      41             :     IPSEC_INPUT_N_ERROR,
      42             : } ipsec_input_error_t;
      43             : 
      44             : static char *ipsec_input_error_strings[] = {
      45             : #define _(sym,string) string,
      46             :   foreach_ipsec_input_error
      47             : #undef _
      48             : };
      49             : 
      50             : typedef struct
      51             : {
      52             :   ip_protocol_t proto;
      53             :   u32 spd;
      54             :   u32 policy_index;
      55             :   u32 policy_type;
      56             :   u32 sa_id;
      57             :   u32 spi;
      58             :   u32 seq;
      59             : } ipsec_input_trace_t;
      60             : 
      61             : /* packet trace format function */
      62             : static u8 *
      63      219852 : format_ipsec_input_trace (u8 * s, va_list * args)
      64             : {
      65      219852 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
      66      219852 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
      67      219852 :   ipsec_input_trace_t *t = va_arg (*args, ipsec_input_trace_t *);
      68             : 
      69             :   s =
      70      219852 :     format (s, "%U: sa_id %u type: %u spd %u policy %d spi %u (0x%08x) seq %u",
      71      219852 :             format_ip_protocol, t->proto, t->sa_id, t->policy_type, t->spd,
      72             :             t->policy_index, t->spi, t->spi, t->seq);
      73             : 
      74      219852 :   return s;
      75             : }
      76             : 
      77             : always_inline void
      78          18 : ipsec4_input_spd_add_flow_cache_entry (ipsec_main_t *im, u32 sa, u32 da,
      79             :                                        ipsec_spd_policy_type_t policy_type,
      80             :                                        u32 pol_id)
      81             : {
      82             :   u64 hash;
      83          18 :   u8 is_overwrite = 0, is_stale_overwrite = 0;
      84             :   /* Store in network byte order to avoid conversion on lookup */
      85          54 :   ipsec4_inbound_spd_tuple_t ip4_tuple = {
      86          18 :     .ip4_src_addr = (ip4_address_t) clib_host_to_net_u32 (sa),
      87          18 :     .ip4_dest_addr = (ip4_address_t) clib_host_to_net_u32 (da),
      88             :     .policy_type = policy_type
      89             :   };
      90             : 
      91          18 :   ip4_tuple.kv_16_8.value =
      92          18 :     (((u64) pol_id) << 32) | ((u64) im->input_epoch_count);
      93             : 
      94          18 :   hash = ipsec4_hash_16_8 (&ip4_tuple.kv_16_8);
      95          18 :   hash &= (im->ipsec4_in_spd_hash_num_buckets - 1);
      96             : 
      97          18 :   ipsec_spinlock_lock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
      98             :   /* Check if we are overwriting an existing entry so we know
      99             :     whether to increment the flow cache counter. Since flow
     100             :     cache counter is reset on any policy add/remove, but
     101             :     hash table values are not, we need to check if the entry
     102             :     we are overwriting is stale or not. If it's a stale entry
     103             :     overwrite, we still want to increment flow cache counter */
     104          18 :   is_overwrite = (im->ipsec4_in_spd_hash_tbl[hash].value != 0);
     105             :   /* Check if we are overwriting a stale entry by comparing
     106             :      with current epoch count */
     107          18 :   if (PREDICT_FALSE (is_overwrite))
     108           5 :     is_stale_overwrite =
     109           5 :       (im->input_epoch_count !=
     110           5 :        ((u32) (im->ipsec4_in_spd_hash_tbl[hash].value & 0xFFFFFFFF)));
     111          18 :   clib_memcpy_fast (&im->ipsec4_in_spd_hash_tbl[hash], &ip4_tuple.kv_16_8,
     112             :                     sizeof (ip4_tuple.kv_16_8));
     113          18 :   ipsec_spinlock_unlock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
     114             : 
     115             :   /* Increment the counter to track active flow cache entries
     116             :     when entering a fresh entry or overwriting a stale one */
     117          18 :   if (!is_overwrite || is_stale_overwrite)
     118          17 :     clib_atomic_fetch_add_relax (&im->ipsec4_in_spd_flow_cache_entries, 1);
     119             : 
     120          18 :   return;
     121             : }
     122             : 
     123             : always_inline ipsec_policy_t *
     124         222 : ipsec4_input_spd_find_flow_cache_entry (ipsec_main_t *im, u32 sa, u32 da,
     125             :                                         ipsec_spd_policy_type_t policy_type)
     126             : {
     127         222 :   ipsec_policy_t *p = NULL;
     128             :   ipsec4_hash_kv_16_8_t kv_result;
     129             :   u64 hash;
     130         222 :   ipsec4_inbound_spd_tuple_t ip4_tuple = { .ip4_src_addr = (ip4_address_t) sa,
     131             :                                            .ip4_dest_addr = (ip4_address_t) da,
     132             :                                            .policy_type = policy_type };
     133             : 
     134         222 :   hash = ipsec4_hash_16_8 (&ip4_tuple.kv_16_8);
     135         222 :   hash &= (im->ipsec4_in_spd_hash_num_buckets - 1);
     136             : 
     137         222 :   ipsec_spinlock_lock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
     138         222 :   kv_result = im->ipsec4_in_spd_hash_tbl[hash];
     139         222 :   ipsec_spinlock_unlock (&im->ipsec4_in_spd_hash_tbl[hash].bucket_lock);
     140             : 
     141         222 :   if (ipsec4_hash_key_compare_16_8 ((u64 *) &ip4_tuple.kv_16_8,
     142             :                                     (u64 *) &kv_result))
     143             :     {
     144          87 :       if (im->input_epoch_count == ((u32) (kv_result.value & 0xFFFFFFFF)))
     145             :         {
     146             :           /* Get the policy based on the index */
     147          72 :           p =
     148          72 :             pool_elt_at_index (im->policies, ((u32) (kv_result.value >> 32)));
     149             :         }
     150             :     }
     151             : 
     152         222 :   return p;
     153             : }
     154             : 
     155             : always_inline void
     156          70 : ipsec_fp_in_5tuple_from_ip4_range (ipsec_fp_5tuple_t *tuple, u32 sa, u32 da,
     157             :                                    u32 spi, u8 action)
     158             : {
     159          70 :   clib_memset (tuple->l3_zero_pad, 0, sizeof (tuple->l3_zero_pad));
     160          70 :   tuple->laddr.as_u32 = da;
     161          70 :   tuple->raddr.as_u32 = sa;
     162          70 :   tuple->spi = spi;
     163          70 :   tuple->action = action;
     164          70 :   tuple->is_ipv6 = 0;
     165          70 : }
     166             : 
     167             : always_inline void
     168           5 : ipsec_fp_in_5tuple_from_ip6_range (ipsec_fp_5tuple_t *tuple, ip6_address_t *sa,
     169             :                                    ip6_address_t *da, u32 spi, u8 action)
     170             : 
     171             : {
     172           5 :   clib_memcpy (&tuple->ip6_laddr, da, sizeof (ip6_address_t));
     173           5 :   clib_memcpy (&tuple->ip6_raddr, sa, sizeof (ip6_address_t));
     174             : 
     175           5 :   tuple->spi = spi;
     176           5 :   tuple->action = action;
     177           5 :   tuple->is_ipv6 = 1;
     178           5 : }
     179             : 
     180             : always_inline ipsec_policy_t *
     181         102 : ipsec_input_policy_match (ipsec_spd_t *spd, u32 sa, u32 da,
     182             :                           ipsec_spd_policy_type_t policy_type)
     183             : {
     184         102 :   ipsec_main_t *im = &ipsec_main;
     185             :   ipsec_policy_t *p;
     186             :   u32 *i;
     187             : 
     188         118 :   vec_foreach (i, spd->policies[policy_type])
     189             :   {
     190         102 :     p = pool_elt_at_index (im->policies, *i);
     191             : 
     192         102 :     if (da < clib_net_to_host_u32 (p->laddr.start.ip4.as_u32))
     193           9 :       continue;
     194             : 
     195          93 :     if (da > clib_net_to_host_u32 (p->laddr.stop.ip4.as_u32))
     196           5 :       continue;
     197             : 
     198          88 :     if (sa < clib_net_to_host_u32 (p->raddr.start.ip4.as_u32))
     199           0 :       continue;
     200             : 
     201          88 :     if (sa > clib_net_to_host_u32 (p->raddr.stop.ip4.as_u32))
     202           2 :       continue;
     203             : 
     204          86 :     if (im->input_flow_cache_flag)
     205             :       {
     206             :         /* Add an Entry in Flow cache */
     207          18 :         ipsec4_input_spd_add_flow_cache_entry (im, sa, da, policy_type, *i);
     208             :       }
     209          86 :     return p;
     210             :   }
     211          16 :   return 0;
     212             : }
     213             : 
     214             : always_inline ipsec_policy_t *
     215      167994 : ipsec_input_protect_policy_match (ipsec_spd_t *spd, u32 sa, u32 da, u32 spi)
     216             : {
     217      167994 :   ipsec_main_t *im = &ipsec_main;
     218             :   ipsec_policy_t *p;
     219             :   ipsec_sa_t *s;
     220             :   u32 *i;
     221             : 
     222      167995 :   vec_foreach (i, spd->policies[IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT])
     223             :   {
     224      167904 :     p = pool_elt_at_index (im->policies, *i);
     225      167904 :     s = ipsec_sa_get (p->sa_index);
     226             : 
     227      167904 :     if (spi != s->spi)
     228           1 :       continue;
     229             : 
     230      167903 :     if (ipsec_sa_is_set_IS_TUNNEL (s))
     231             :       {
     232       32933 :         if (da != clib_net_to_host_u32 (s->tunnel.t_dst.ip.ip4.as_u32))
     233           0 :           continue;
     234             : 
     235       32933 :         if (sa != clib_net_to_host_u32 (s->tunnel.t_src.ip.ip4.as_u32))
     236           0 :           continue;
     237             : 
     238       32933 :         goto return_policy;
     239             :       }
     240             : 
     241      134970 :     if (da < clib_net_to_host_u32 (p->laddr.start.ip4.as_u32))
     242           0 :       continue;
     243             : 
     244      134970 :     if (da > clib_net_to_host_u32 (p->laddr.stop.ip4.as_u32))
     245           0 :       continue;
     246             : 
     247      134970 :     if (sa < clib_net_to_host_u32 (p->raddr.start.ip4.as_u32))
     248           0 :       continue;
     249             : 
     250      134970 :     if (sa > clib_net_to_host_u32 (p->raddr.stop.ip4.as_u32))
     251           0 :       continue;
     252             : 
     253      134970 :   return_policy:
     254      167903 :     if (im->input_flow_cache_flag)
     255             :       {
     256             :         /* Add an Entry in Flow cache */
     257           0 :         ipsec4_input_spd_add_flow_cache_entry (
     258             :           im, sa, da, IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT, *i);
     259             :       }
     260             : 
     261      167903 :     return p;
     262             :   }
     263          91 :   return 0;
     264             : }
     265             : 
     266             : always_inline uword
     267       64686 : ip6_addr_match_range (ip6_address_t * a, ip6_address_t * la,
     268             :                       ip6_address_t * ua)
     269             : {
     270       64686 :   if ((memcmp (a->as_u64, la->as_u64, 2 * sizeof (u64)) >= 0) &&
     271       64686 :       (memcmp (a->as_u64, ua->as_u64, 2 * sizeof (u64)) <= 0))
     272       64686 :     return 1;
     273           0 :   return 0;
     274             : }
     275             : 
     276             : always_inline ipsec_policy_t *
     277       65844 : ipsec6_input_protect_policy_match (ipsec_spd_t * spd,
     278             :                                    ip6_address_t * sa,
     279             :                                    ip6_address_t * da, u32 spi)
     280             : {
     281       65844 :   ipsec_main_t *im = &ipsec_main;
     282             :   ipsec_policy_t *p;
     283             :   ipsec_sa_t *s;
     284             :   u32 *i;
     285             : 
     286       65844 :   vec_foreach (i, spd->policies[IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT])
     287             :   {
     288       65844 :     p = pool_elt_at_index (im->policies, *i);
     289       65844 :     s = ipsec_sa_get (p->sa_index);
     290             : 
     291       65844 :     if (spi != s->spi)
     292           0 :       continue;
     293             : 
     294       65844 :     if (ipsec_sa_is_set_IS_TUNNEL (s))
     295             :       {
     296       33501 :         if (!ip6_address_is_equal (sa, &s->tunnel.t_src.ip.ip6))
     297           0 :           continue;
     298             : 
     299       33501 :         if (!ip6_address_is_equal (da, &s->tunnel.t_dst.ip.ip6))
     300           0 :           continue;
     301             : 
     302       33501 :         return p;
     303             :       }
     304             : 
     305       32343 :     if (!ip6_addr_match_range (sa, &p->raddr.start.ip6, &p->raddr.stop.ip6))
     306           0 :       continue;
     307             : 
     308       32343 :     if (!ip6_addr_match_range (da, &p->laddr.start.ip6, &p->laddr.stop.ip6))
     309           0 :       continue;
     310             : 
     311       32343 :     return p;
     312             :   }
     313           0 :   return 0;
     314             : }
     315             : 
     316             : extern vlib_node_registration_t ipsec4_input_node;
     317             : 
     318       12188 : VLIB_NODE_FN (ipsec4_input_node) (vlib_main_t * vm,
     319             :                                   vlib_node_runtime_t * node,
     320             :                                   vlib_frame_t * frame)
     321             : {
     322             :   u32 n_left_from, *from, thread_index;
     323        9888 :   ipsec_main_t *im = &ipsec_main;
     324        9888 :   u64 ipsec_unprocessed = 0, ipsec_matched = 0;
     325        9888 :   u64 ipsec_dropped = 0, ipsec_bypassed = 0;
     326             :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
     327        9888 :   vlib_buffer_t **b = bufs;
     328             :   u16 nexts[VLIB_FRAME_SIZE], *next;
     329             : 
     330        9888 :   from = vlib_frame_vector_args (frame);
     331        9888 :   n_left_from = frame->n_vectors;
     332        9888 :   next = nexts;
     333        9888 :   vlib_get_buffers (vm, from, bufs, n_left_from);
     334        9888 :   thread_index = vm->thread_index;
     335             : 
     336             : 
     337      337527 :   while (n_left_from > 0)
     338             :     {
     339             :       u32 next32, pi0;
     340             :       ip4_header_t *ip0;
     341      327639 :       esp_header_t *esp0 = NULL;
     342             :       ah_header_t *ah0;
     343             :       ip4_ipsec_config_t *c0;
     344             :       ipsec_spd_t *spd0;
     345      327639 :       ipsec_policy_t *p0 = NULL;
     346             :       u8 has_space0;
     347      327639 :       bool search_flow_cache = false;
     348             :       ipsec_policy_t *policies[1];
     349             :       ipsec_fp_5tuple_t tuples[1];
     350      327639 :       bool ip_v6 = true;
     351             : 
     352      327639 :       if (n_left_from > 2)
     353             :         {
     354      310771 :           vlib_prefetch_buffer_data (b[1], LOAD);
     355             :         }
     356             : 
     357      327639 :       b[0]->flags |= VNET_BUFFER_F_IS_IP4;
     358      327639 :       b[0]->flags &= ~VNET_BUFFER_F_IS_IP6;
     359      327639 :       c0 = vnet_feature_next_with_data (&next32, b[0], sizeof (c0[0]));
     360      327639 :       next[0] = (u16) next32;
     361             : 
     362      327639 :       spd0 = pool_elt_at_index (im->spds, c0->spd_index);
     363             : 
     364      327639 :       ip0 = vlib_buffer_get_current (b[0]);
     365             : 
     366      327639 :       if (PREDICT_TRUE
     367             :           (ip0->protocol == IP_PROTOCOL_IPSEC_ESP
     368             :            || ip0->protocol == IP_PROTOCOL_UDP))
     369             :         {
     370             : 
     371      166127 :           esp0 = (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
     372      166127 :           if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_UDP))
     373             :             {
     374             :               /* FIXME Skip, if not a UDP encapsulated packet */
     375        1157 :               esp0 = (esp_header_t *) ((u8 *) esp0 + sizeof (udp_header_t));
     376             :             }
     377             : 
     378             :           // if flow cache is enabled, first search through flow cache for a
     379             :           // policy match for either protect, bypass or discard rules, in that
     380             :           // order. if no match is found search_flow_cache is set to false (1)
     381             :           // and we revert back to linear search
     382      166127 :           search_flow_cache = im->input_flow_cache_flag;
     383             : 
     384      166145 :         esp_or_udp:
     385      166145 :           if (im->fp_spd_ipv4_in_is_enabled &&
     386          70 :               PREDICT_TRUE (INDEX_INVALID !=
     387             :                             spd0->fp_spd.ip4_in_lookup_hash_idx))
     388             :             {
     389          70 :               ipsec_fp_in_5tuple_from_ip4_range (
     390             :                 &tuples[0], ip0->src_address.as_u32, ip0->dst_address.as_u32,
     391             :                 clib_net_to_host_u32 (esp0->spi),
     392             :                 IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT);
     393          70 :               ipsec_fp_in_policy_match_n (&spd0->fp_spd, !ip_v6, tuples,
     394             :                                           policies, 1);
     395          70 :               p0 = policies[0];
     396             :             }
     397      166075 :           else if (search_flow_cache) // attempt to match policy in flow cache
     398             :             {
     399          90 :               p0 = ipsec4_input_spd_find_flow_cache_entry (
     400             :                 im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
     401             :                 IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT);
     402             :             }
     403             : 
     404             :           else // linear search if flow cache is not enabled,
     405             :                // or flow cache search just failed
     406             :             {
     407      165985 :               p0 = ipsec_input_protect_policy_match (
     408             :                 spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
     409             :                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
     410             :                 clib_net_to_host_u32 (esp0->spi));
     411             :             }
     412             : 
     413             :           has_space0 =
     414      166145 :             vlib_buffer_has_space (b[0],
     415      166145 :                                    (clib_address_t) (esp0 + 1) -
     416             :                                    (clib_address_t) ip0);
     417             : 
     418      166145 :           if (PREDICT_TRUE ((p0 != NULL) & (has_space0)))
     419             :             {
     420      165899 :               ipsec_matched += 1;
     421             : 
     422      165899 :               pi0 = p0 - im->policies;
     423      165899 :               vlib_increment_combined_counter
     424             :                 (&ipsec_spd_policy_counters,
     425      165899 :                  thread_index, pi0, 1, clib_net_to_host_u16 (ip0->length));
     426             : 
     427      165899 :               vnet_buffer (b[0])->ipsec.sad_index = p0->sa_index;
     428      165899 :               next[0] = im->esp4_decrypt_next_index;
     429      165899 :               vlib_buffer_advance (b[0], ((u8 *) esp0 - (u8 *) ip0));
     430      165899 :               goto trace0;
     431             :             }
     432             :           else
     433             :             {
     434         246 :               p0 = 0;
     435         246 :               pi0 = ~0;
     436             :             };
     437             : 
     438         246 :           if (im->fp_spd_ipv4_in_is_enabled &&
     439          65 :               PREDICT_TRUE (INDEX_INVALID !=
     440             :                             spd0->fp_spd.ip4_in_lookup_hash_idx))
     441             :             {
     442          65 :               tuples->action = IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS;
     443          65 :               ipsec_fp_in_policy_match_n (&spd0->fp_spd, !ip_v6, tuples,
     444             :                                           policies, 1);
     445          65 :               p0 = policies[0];
     446             :             }
     447         181 :           else if (search_flow_cache)
     448             :             {
     449          90 :               p0 = ipsec4_input_spd_find_flow_cache_entry (
     450             :                 im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
     451             :                 IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
     452             :             }
     453             : 
     454             :           else
     455             :             {
     456          91 :               p0 = ipsec_input_policy_match (
     457             :                 spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
     458             :                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
     459             :                 IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
     460             :             }
     461             : 
     462         246 :           if (PREDICT_TRUE ((p0 != NULL)))
     463             :             {
     464         173 :               ipsec_bypassed += 1;
     465             : 
     466         173 :               pi0 = p0 - im->policies;
     467         173 :               vlib_increment_combined_counter (
     468             :                 &ipsec_spd_policy_counters, thread_index, pi0, 1,
     469         173 :                 clib_net_to_host_u16 (ip0->length));
     470             : 
     471         173 :               goto trace0;
     472             :             }
     473             :           else
     474             :             {
     475          73 :               p0 = 0;
     476          73 :               pi0 = ~0;
     477             :             };
     478             : 
     479          73 :           if (im->fp_spd_ipv4_in_is_enabled &&
     480          20 :               PREDICT_TRUE (INDEX_INVALID !=
     481             :                             spd0->fp_spd.ip4_in_lookup_hash_idx))
     482             :             {
     483          20 :               tuples->action = IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD;
     484          20 :               ipsec_fp_in_policy_match_n (&spd0->fp_spd, !ip_v6, tuples,
     485             :                                           policies, 1);
     486          20 :               p0 = policies[0];
     487             :             }
     488             :           else
     489             : 
     490          53 :             if (search_flow_cache)
     491             :             {
     492          42 :               p0 = ipsec4_input_spd_find_flow_cache_entry (
     493             :                 im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
     494             :                 IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
     495             :             }
     496             : 
     497             :           else
     498             :             {
     499          11 :               p0 = ipsec_input_policy_match (
     500             :                 spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
     501             :                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
     502             :                 IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
     503             :             }
     504             : 
     505          73 :           if (PREDICT_TRUE ((p0 != NULL)))
     506             :             {
     507          50 :               ipsec_dropped += 1;
     508             : 
     509          50 :               pi0 = p0 - im->policies;
     510          50 :               vlib_increment_combined_counter (
     511             :                 &ipsec_spd_policy_counters, thread_index, pi0, 1,
     512          50 :                 clib_net_to_host_u16 (ip0->length));
     513             : 
     514          50 :               next[0] = IPSEC_INPUT_NEXT_DROP;
     515          50 :               goto trace0;
     516             :             }
     517             :           else
     518             :             {
     519          23 :               p0 = 0;
     520          23 :               pi0 = ~0;
     521             :             };
     522             : 
     523             :           // flow cache search failed, try again with linear search
     524          23 :           if (search_flow_cache && p0 == NULL)
     525             :             {
     526          18 :               search_flow_cache = false;
     527          18 :               goto esp_or_udp;
     528             :             }
     529             : 
     530             :           /* Drop by default if no match on PROTECT, BYPASS or DISCARD */
     531           5 :           ipsec_unprocessed += 1;
     532           5 :           next[0] = IPSEC_INPUT_NEXT_DROP;
     533             : 
     534      166127 :         trace0:
     535      166127 :           if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
     536      165926 :               PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
     537             :             {
     538             :               ipsec_input_trace_t *tr =
     539      165926 :                 vlib_add_trace (vm, node, b[0], sizeof (*tr));
     540             : 
     541      165926 :               tr->proto = ip0->protocol;
     542      165926 :               tr->sa_id = p0 ? p0->sa_id : ~0;
     543      165926 :               tr->spi = has_space0 ? clib_net_to_host_u32 (esp0->spi) : ~0;
     544      165926 :               tr->seq = has_space0 ? clib_net_to_host_u32 (esp0->seq) : ~0;
     545      165926 :               tr->spd = spd0->id;
     546      165926 :               tr->policy_index = pi0;
     547             :             }
     548             :         }
     549      161512 :       else if (ip0->protocol == IP_PROTOCOL_IPSEC_AH)
     550             :         {
     551        2009 :           ah0 = (ah_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
     552             : 
     553             :           // if flow cache is enabled, first search through flow cache for a
     554             :           // policy match and revert back to linear search on failure
     555        2009 :           search_flow_cache = im->input_flow_cache_flag;
     556             : 
     557        2009 :         ah:
     558        2009 :           if (search_flow_cache)
     559             :             {
     560           0 :               p0 = ipsec4_input_spd_find_flow_cache_entry (
     561             :                 im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
     562             :                 IPSEC_SPD_POLICY_IP4_INBOUND_PROTECT);
     563             :             }
     564             : 
     565             :           else
     566             :             {
     567        2009 :               p0 = ipsec_input_protect_policy_match (
     568             :                 spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
     569             :                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
     570             :                 clib_net_to_host_u32 (ah0->spi));
     571             :             }
     572             : 
     573             :           has_space0 =
     574        2009 :             vlib_buffer_has_space (b[0],
     575        2009 :                                    (clib_address_t) (ah0 + 1) -
     576             :                                    (clib_address_t) ip0);
     577             : 
     578        2009 :           if (PREDICT_TRUE ((p0 != NULL) & (has_space0)))
     579             :             {
     580        2009 :               ipsec_matched += 1;
     581             : 
     582        2009 :               pi0 = p0 - im->policies;
     583        2009 :               vlib_increment_combined_counter
     584             :                 (&ipsec_spd_policy_counters,
     585        2009 :                  thread_index, pi0, 1, clib_net_to_host_u16 (ip0->length));
     586             : 
     587        2009 :               vnet_buffer (b[0])->ipsec.sad_index = p0->sa_index;
     588        2009 :               next[0] = im->ah4_decrypt_next_index;
     589        2009 :               goto trace1;
     590             :             }
     591             :           else
     592             :             {
     593           0 :               p0 = 0;
     594           0 :               pi0 = ~0;
     595             :             }
     596             : 
     597           0 :           if (search_flow_cache)
     598             :             {
     599           0 :               p0 = ipsec4_input_spd_find_flow_cache_entry (
     600             :                 im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
     601             :                 IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
     602             :             }
     603             : 
     604             :           else
     605             :             {
     606           0 :               p0 = ipsec_input_policy_match (
     607             :                 spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
     608             :                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
     609             :                 IPSEC_SPD_POLICY_IP4_INBOUND_BYPASS);
     610             :             }
     611             : 
     612           0 :           if (PREDICT_TRUE ((p0 != NULL)))
     613             :             {
     614           0 :               ipsec_bypassed += 1;
     615             : 
     616           0 :               pi0 = p0 - im->policies;
     617           0 :               vlib_increment_combined_counter (
     618             :                 &ipsec_spd_policy_counters, thread_index, pi0, 1,
     619           0 :                 clib_net_to_host_u16 (ip0->length));
     620             : 
     621           0 :               goto trace1;
     622             :             }
     623             :           else
     624             :             {
     625           0 :               p0 = 0;
     626           0 :               pi0 = ~0;
     627             :             };
     628             : 
     629           0 :           if (search_flow_cache)
     630             :             {
     631           0 :               p0 = ipsec4_input_spd_find_flow_cache_entry (
     632             :                 im, ip0->src_address.as_u32, ip0->dst_address.as_u32,
     633             :                 IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
     634             :             }
     635             : 
     636             :           else
     637             :             {
     638           0 :               p0 = ipsec_input_policy_match (
     639             :                 spd0, clib_net_to_host_u32 (ip0->src_address.as_u32),
     640             :                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
     641             :                 IPSEC_SPD_POLICY_IP4_INBOUND_DISCARD);
     642             :             }
     643             : 
     644           0 :           if (PREDICT_TRUE ((p0 != NULL)))
     645             :             {
     646           0 :               ipsec_dropped += 1;
     647             : 
     648           0 :               pi0 = p0 - im->policies;
     649           0 :               vlib_increment_combined_counter (
     650             :                 &ipsec_spd_policy_counters, thread_index, pi0, 1,
     651           0 :                 clib_net_to_host_u16 (ip0->length));
     652             : 
     653           0 :               next[0] = IPSEC_INPUT_NEXT_DROP;
     654           0 :               goto trace1;
     655             :             }
     656             :           else
     657             :             {
     658           0 :               p0 = 0;
     659           0 :               pi0 = ~0;
     660             :             };
     661             : 
     662             :           // flow cache search failed, retry with linear search
     663           0 :           if (search_flow_cache && p0 == NULL)
     664             :             {
     665           0 :               search_flow_cache = false;
     666           0 :               goto ah;
     667             :             }
     668             : 
     669             :           /* Drop by default if no match on PROTECT, BYPASS or DISCARD */
     670           0 :           ipsec_unprocessed += 1;
     671           0 :           next[0] = IPSEC_INPUT_NEXT_DROP;
     672             : 
     673        2009 :         trace1:
     674        2009 :           if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
     675        2009 :               PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
     676             :             {
     677             :               ipsec_input_trace_t *tr =
     678        2009 :                 vlib_add_trace (vm, node, b[0], sizeof (*tr));
     679             : 
     680        2009 :               tr->proto = ip0->protocol;
     681        2009 :               tr->sa_id = p0 ? p0->sa_id : ~0;
     682        2009 :               tr->spi = has_space0 ? clib_net_to_host_u32 (ah0->spi) : ~0;
     683        2009 :               tr->seq = has_space0 ? clib_net_to_host_u32 (ah0->seq_no) : ~0;
     684        2009 :               tr->spd = spd0->id;
     685        2009 :               tr->policy_index = pi0;
     686             :             }
     687             :         }
     688             :       else
     689             :         {
     690      159503 :           ipsec_unprocessed += 1;
     691             :         }
     692      327639 :       n_left_from -= 1;
     693      327639 :       b += 1;
     694      327639 :       next += 1;
     695             :     }
     696             : 
     697        9888 :   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
     698             : 
     699        9888 :   vlib_node_increment_counter (vm, ipsec4_input_node.index,
     700        9888 :                                IPSEC_INPUT_ERROR_RX_PKTS, frame->n_vectors);
     701             : 
     702        9888 :   vlib_node_increment_counter (vm, ipsec4_input_node.index,
     703             :                                IPSEC_INPUT_ERROR_RX_POLICY_MATCH,
     704             :                                ipsec_matched);
     705             : 
     706        9888 :   vlib_node_increment_counter (vm, ipsec4_input_node.index,
     707             :                                IPSEC_INPUT_ERROR_RX_POLICY_NO_MATCH,
     708             :                                ipsec_unprocessed);
     709             : 
     710        9888 :   vlib_node_increment_counter (vm, ipsec4_input_node.index,
     711             :                                IPSEC_INPUT_ERROR_RX_POLICY_DISCARD,
     712             :                                ipsec_dropped);
     713             : 
     714        9888 :   vlib_node_increment_counter (vm, ipsec4_input_node.index,
     715             :                                IPSEC_INPUT_ERROR_RX_POLICY_BYPASS,
     716             :                                ipsec_bypassed);
     717             : 
     718        9888 :   return frame->n_vectors;
     719             : }
     720             : 
     721             : 
     722             : /* *INDENT-OFF* */
     723      183788 : VLIB_REGISTER_NODE (ipsec4_input_node) = {
     724             :   .name = "ipsec4-input-feature",
     725             :   .vector_size = sizeof (u32),
     726             :   .format_trace = format_ipsec_input_trace,
     727             :   .type = VLIB_NODE_TYPE_INTERNAL,
     728             :   .n_errors = ARRAY_LEN(ipsec_input_error_strings),
     729             :   .error_strings = ipsec_input_error_strings,
     730             :   .n_next_nodes = IPSEC_INPUT_N_NEXT,
     731             :   .next_nodes = {
     732             : #define _(s,n) [IPSEC_INPUT_NEXT_##s] = n,
     733             :     foreach_ipsec_input_next
     734             : #undef _
     735             :   },
     736             : };
     737             : /* *INDENT-ON* */
     738             : 
     739             : extern vlib_node_registration_t ipsec6_input_node;
     740             : 
     741             : 
     742        4554 : VLIB_NODE_FN (ipsec6_input_node) (vlib_main_t * vm,
     743             :                                   vlib_node_runtime_t * node,
     744             :                                   vlib_frame_t * from_frame)
     745             : {
     746             :   u32 n_left_from, *from, next_index, *to_next, thread_index;
     747        2254 :   ipsec_main_t *im = &ipsec_main;
     748        2254 :   u32 ipsec_unprocessed = 0;
     749        2254 :   u32 ipsec_matched = 0;
     750             :   ipsec_policy_t *policies[1];
     751             :   ipsec_fp_5tuple_t tuples[1];
     752        2254 :   bool ip_v6 = true;
     753             : 
     754        2254 :   from = vlib_frame_vector_args (from_frame);
     755        2254 :   n_left_from = from_frame->n_vectors;
     756        2254 :   thread_index = vm->thread_index;
     757             : 
     758        2254 :   next_index = node->cached_next_index;
     759             : 
     760        4508 :   while (n_left_from > 0)
     761             :     {
     762             :       u32 n_left_to_next;
     763             : 
     764        2254 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     765             : 
     766      133970 :       while (n_left_from > 0 && n_left_to_next > 0)
     767             :         {
     768      131716 :           u32 bi0, next0, pi0 = ~0;
     769             :           vlib_buffer_t *b0;
     770             :           ip6_header_t *ip0;
     771             :           esp_header_t *esp0;
     772             :           ip4_ipsec_config_t *c0;
     773             :           ipsec_spd_t *spd0;
     774      131716 :           ipsec_policy_t *p0 = 0;
     775             :           ah_header_t *ah0;
     776      131716 :           u32 header_size = sizeof (ip0[0]);
     777             : 
     778      131716 :           bi0 = to_next[0] = from[0];
     779      131716 :           from += 1;
     780      131716 :           n_left_from -= 1;
     781      131716 :           to_next += 1;
     782      131716 :           n_left_to_next -= 1;
     783             : 
     784      131716 :           b0 = vlib_get_buffer (vm, bi0);
     785      131716 :           b0->flags |= VNET_BUFFER_F_IS_IP6;
     786      131716 :           b0->flags &= ~VNET_BUFFER_F_IS_IP4;
     787      131716 :           c0 = vnet_feature_next_with_data (&next0, b0, sizeof (c0[0]));
     788             : 
     789      131716 :           spd0 = pool_elt_at_index (im->spds, c0->spd_index);
     790             : 
     791      131716 :           ip0 = vlib_buffer_get_current (b0);
     792      131716 :           esp0 = (esp_header_t *) ((u8 *) ip0 + header_size);
     793      131716 :           ah0 = (ah_header_t *) ((u8 *) ip0 + header_size);
     794             : 
     795      131716 :           if (PREDICT_TRUE (ip0->protocol == IP_PROTOCOL_IPSEC_ESP))
     796             :             {
     797             : #if 0
     798             :               clib_warning
     799             :                 ("packet received from %U to %U spi %u size %u spd_id %u",
     800             :                  format_ip6_address, &ip0->src_address, format_ip6_address,
     801             :                  &ip0->dst_address, clib_net_to_host_u32 (esp0->spi),
     802             :                  clib_net_to_host_u16 (ip0->payload_length) + header_size,
     803             :                  spd0->id);
     804             : #endif
     805       63938 :               if (im->fp_spd_ipv6_in_is_enabled &&
     806           5 :                   PREDICT_TRUE (INDEX_INVALID !=
     807             :                                 spd0->fp_spd.ip6_in_lookup_hash_idx))
     808             :                 {
     809           5 :                   ipsec_fp_in_5tuple_from_ip6_range (
     810             :                     &tuples[0], &ip0->src_address, &ip0->dst_address,
     811             :                     clib_net_to_host_u32 (esp0->spi),
     812             :                     IPSEC_SPD_POLICY_IP6_INBOUND_PROTECT);
     813           5 :                   ipsec_fp_in_policy_match_n (&spd0->fp_spd, ip_v6, tuples,
     814             :                                               policies, 1);
     815           5 :                   p0 = policies[0];
     816             :                 }
     817             :               else
     818       63933 :                 p0 = ipsec6_input_protect_policy_match (
     819             :                   spd0, &ip0->src_address, &ip0->dst_address,
     820             :                   clib_net_to_host_u32 (esp0->spi));
     821             : 
     822       63938 :               if (PREDICT_TRUE (p0 != 0))
     823             :                 {
     824       63938 :                   ipsec_matched += 1;
     825             : 
     826       63938 :                   pi0 = p0 - im->policies;
     827       63938 :                   vlib_increment_combined_counter
     828             :                     (&ipsec_spd_policy_counters,
     829             :                      thread_index, pi0, 1,
     830       63938 :                      clib_net_to_host_u16 (ip0->payload_length) +
     831             :                      header_size);
     832             : 
     833       63938 :                   vnet_buffer (b0)->ipsec.sad_index = p0->sa_index;
     834       63938 :                   next0 = im->esp6_decrypt_next_index;
     835       63938 :                   vlib_buffer_advance (b0, header_size);
     836             :                   /* TODO Add policy matching for bypass and discard policy
     837             :                    * type */
     838       63938 :                   goto trace0;
     839             :                 }
     840             :               else
     841             :                 {
     842           0 :                   pi0 = ~0;
     843           0 :                   ipsec_unprocessed += 1;
     844           0 :                   next0 = IPSEC_INPUT_NEXT_DROP;
     845             :                 }
     846             :             }
     847       67778 :           else if (ip0->protocol == IP_PROTOCOL_IPSEC_AH)
     848             :             {
     849        1911 :               p0 = ipsec6_input_protect_policy_match (spd0,
     850             :                                                       &ip0->src_address,
     851             :                                                       &ip0->dst_address,
     852             :                                                       clib_net_to_host_u32
     853             :                                                       (ah0->spi));
     854             : 
     855        1911 :               if (PREDICT_TRUE (p0 != 0))
     856             :                 {
     857        1911 :                   ipsec_matched += 1;
     858        1911 :                   pi0 = p0 - im->policies;
     859        1911 :                   vlib_increment_combined_counter
     860             :                     (&ipsec_spd_policy_counters,
     861             :                      thread_index, pi0, 1,
     862        1911 :                      clib_net_to_host_u16 (ip0->payload_length) +
     863             :                      header_size);
     864             : 
     865        1911 :                   vnet_buffer (b0)->ipsec.sad_index = p0->sa_index;
     866        1911 :                   next0 = im->ah6_decrypt_next_index;
     867        1911 :                   goto trace0;
     868             :                 }
     869             :               else
     870             :                 {
     871           0 :                   pi0 = ~0;
     872           0 :                   ipsec_unprocessed += 1;
     873           0 :                   next0 = IPSEC_INPUT_NEXT_DROP;
     874             :                 }
     875             :             }
     876             :           else
     877             :             {
     878       65867 :               ipsec_unprocessed += 1;
     879             :             }
     880             : 
     881      131716 :         trace0:
     882      131716 :           if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
     883      118316 :               PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
     884             :             {
     885             :               ipsec_input_trace_t *tr =
     886      118316 :                 vlib_add_trace (vm, node, b0, sizeof (*tr));
     887             : 
     888      118316 :               if (p0)
     889             :                 {
     890       65849 :                   tr->sa_id = p0->sa_id;
     891       65849 :                   tr->policy_type = p0->type;
     892             :                 }
     893             : 
     894      118316 :               tr->proto = ip0->protocol;
     895      118316 :               tr->spi = clib_net_to_host_u32 (esp0->spi);
     896      118316 :               tr->seq = clib_net_to_host_u32 (esp0->seq);
     897      118316 :               tr->spd = spd0->id;
     898      118316 :               tr->policy_index = pi0;
     899             :             }
     900             : 
     901      131716 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     902             :                                            n_left_to_next, bi0, next0);
     903             :         }
     904        2254 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     905             :     }
     906             : 
     907        2254 :   vlib_node_increment_counter (vm, ipsec6_input_node.index,
     908             :                                IPSEC_INPUT_ERROR_RX_PKTS,
     909        2254 :                                from_frame->n_vectors - ipsec_unprocessed);
     910             : 
     911        2254 :   vlib_node_increment_counter (vm, ipsec6_input_node.index,
     912             :                                IPSEC_INPUT_ERROR_RX_POLICY_MATCH,
     913             :                                ipsec_matched);
     914             : 
     915        2254 :   return from_frame->n_vectors;
     916             : }
     917             : 
     918             : 
     919             : /* *INDENT-OFF* */
     920      183788 : VLIB_REGISTER_NODE (ipsec6_input_node) = {
     921             :   .name = "ipsec6-input-feature",
     922             :   .vector_size = sizeof (u32),
     923             :   .format_trace = format_ipsec_input_trace,
     924             :   .type = VLIB_NODE_TYPE_INTERNAL,
     925             :   .n_errors = ARRAY_LEN(ipsec_input_error_strings),
     926             :   .error_strings = ipsec_input_error_strings,
     927             :   .n_next_nodes = IPSEC_INPUT_N_NEXT,
     928             :   .next_nodes = {
     929             : #define _(s,n) [IPSEC_INPUT_NEXT_##s] = n,
     930             :     foreach_ipsec_input_next
     931             : #undef _
     932             :   },
     933             : };
     934             : /* *INDENT-ON* */
     935             : 
     936             : /*
     937             :  * fd.io coding-style-patch-verification: ON
     938             :  *
     939             :  * Local Variables:
     940             :  * eval: (c-set-style "gnu")
     941             :  * End:
     942             :  */

Generated by: LCOV version 1.14