LCOV - code coverage report
Current view: top level - plugins/srv6-ad-flow - ad-flow.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 147 166 88.6 %
Date: 2023-10-26 01:39:38 Functions: 15 16 93.8 %

          Line data    Source code
       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             : /*
      17             :  *------------------------------------------------------------------
      18             :  * ad-flow.c - SRv6 Flow-based Dynamic Proxy (AD.Flow) behavior
      19             :  *------------------------------------------------------------------
      20             :  */
      21             : 
      22             : #include <vnet/vnet.h>
      23             : #include <vnet/adj/adj.h>
      24             : #include <vnet/plugin/plugin.h>
      25             : #include <vpp/app/version.h>
      26             : #include <srv6-ad-flow/ad-flow.h>
      27             : 
      28             : #include <vppinfra/bihash_template.c>
      29             : 
      30             : #define SID_CREATE_IFACE_FEATURE_ERROR -1
      31             : #define SID_CREATE_INVALID_IFACE_TYPE  -3
      32             : #define SID_CREATE_INVALID_IFACE_INDEX -4
      33             : #define SID_CREATE_INVALID_ADJ_INDEX   -5
      34             : 
      35             : unsigned char function_name[] = "SRv6-AD-Flow-plugin";
      36             : unsigned char keyword_str[] = "End.AD.Flow";
      37             : unsigned char def_str[] =
      38             :   "Endpoint with flow-based dynamic proxy to SR-unaware appliance";
      39             : unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
      40             : 
      41             : srv6_ad_flow_main_t srv6_ad_flow_main;
      42             : 
      43             : static u32
      44           2 : ad_flow_calc_bihash_buckets (u32 n_elts)
      45             : {
      46           2 :   return 1 << (max_log2 (n_elts >> 1) + 1);
      47             : }
      48             : 
      49             : static u32
      50           2 : ad_flow_calc_bihash_memory (u32 n_buckets, uword kv_size)
      51             : {
      52           2 :   return n_buckets * (8 + kv_size * 4);
      53             : }
      54             : 
      55             : /*****************************************/
      56             : /* SRv6 LocalSID instantiation and removal functions */
      57             : static int
      58           2 : srv6_ad_flow_localsid_creation_fn (ip6_sr_localsid_t *localsid)
      59             : {
      60           2 :   ip6_sr_main_t *srm = &sr_main;
      61           2 :   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
      62           2 :   srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
      63           2 :   u32 localsid_index = localsid - srm->localsids;
      64             : 
      65             :   /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
      66             : 
      67             :   /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
      68           2 :   adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
      69           2 :   if (ls_mem->inner_type == AD_TYPE_IP4)
      70             :     nh_adj_index =
      71           1 :       adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4, &ls_mem->nh_addr,
      72             :                            ls_mem->sw_if_index_out);
      73           1 :   else if (ls_mem->inner_type == AD_TYPE_IP6)
      74             :     nh_adj_index =
      75           1 :       adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6, &ls_mem->nh_addr,
      76             :                            ls_mem->sw_if_index_out);
      77             : 
      78           2 :   if (nh_adj_index == ADJ_INDEX_INVALID)
      79             :     {
      80           0 :       clib_mem_free (ls_mem);
      81           0 :       return SID_CREATE_INVALID_ADJ_INDEX;
      82             :     }
      83             : 
      84           2 :   ls_mem->nh_adj = nh_adj_index;
      85             : 
      86             :   /* Step 2: Prepare inbound policy for packets returning from the VNF */
      87             : 
      88             :   /* Sanitise the SW_IF_INDEX */
      89           2 :   if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
      90           2 :                           ls_mem->sw_if_index_in))
      91             :     {
      92           0 :       adj_unlock (ls_mem->nh_adj);
      93           0 :       clib_mem_free (ls_mem);
      94           0 :       return SID_CREATE_INVALID_IFACE_INDEX;
      95             :     }
      96             : 
      97             : 
      98           2 :   if (ls_mem->inner_type == AD_TYPE_IP4)
      99             :     {
     100             :       /* Enable End.AD4 rewrite node for this interface */
     101             :       int ret =
     102           1 :         vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
     103             :                                      ls_mem->sw_if_index_in, 1, 0, 0);
     104           1 :       if (ret != 0)
     105             :         {
     106           0 :           adj_unlock (ls_mem->nh_adj);
     107           0 :           clib_mem_free (ls_mem);
     108           0 :           return SID_CREATE_IFACE_FEATURE_ERROR;
     109             :         }
     110             : 
     111             :       /* Associate local SID index to this interface (resize vector if needed)
     112             :        */
     113           1 :       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
     114             :         {
     115           1 :           vec_resize (sm->sw_iface_localsid4,
     116             :                       (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
     117             :                        vec_len (sm->sw_iface_localsid4)));
     118             :         }
     119           1 :       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
     120             :     }
     121           1 :   else if (ls_mem->inner_type == AD_TYPE_IP6)
     122             :     {
     123             :       /* Enable End.AD6 rewrite node for this interface */
     124             :       int ret =
     125           1 :         vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
     126             :                                      ls_mem->sw_if_index_in, 1, 0, 0);
     127           1 :       if (ret != 0)
     128             :         {
     129           0 :           adj_unlock (ls_mem->nh_adj);
     130           0 :           clib_mem_free (ls_mem);
     131           0 :           return SID_CREATE_IFACE_FEATURE_ERROR;
     132             :         }
     133             : 
     134             :       /* Associate local SID index to this interface (resize vector if needed)
     135             :        */
     136           1 :       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
     137             :         {
     138           1 :           vec_resize (sm->sw_iface_localsid6,
     139             :                       (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
     140             :                        vec_len (sm->sw_iface_localsid6)));
     141             :         }
     142           1 :       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
     143             :     }
     144             : 
     145             :   /* Initialize flow and cache tables */
     146           2 :   ls_mem->cache_size = SRV6_AD_FLOW_DEFAULT_CACHE_SIZE;
     147           2 :   ls_mem->cache_buckets = ad_flow_calc_bihash_buckets (ls_mem->cache_size);
     148           2 :   ls_mem->cache_memory_size = ad_flow_calc_bihash_memory (
     149             :     ls_mem->cache_buckets, sizeof (clib_bihash_40_8_t));
     150             : 
     151           2 :   pool_alloc (ls_mem->cache, ls_mem->cache_size);
     152           2 :   pool_alloc (ls_mem->lru_pool, ls_mem->cache_size);
     153             : 
     154             :   dlist_elt_t *head;
     155           2 :   pool_get (ls_mem->lru_pool, head);
     156           2 :   ls_mem->lru_head_index = head - ls_mem->lru_pool;
     157           2 :   clib_dlist_init (ls_mem->lru_pool, ls_mem->lru_head_index);
     158             : 
     159           2 :   clib_bihash_init_40_8 (&ls_mem->ftable, "ad-flow", ls_mem->cache_buckets,
     160             :                          ls_mem->cache_memory_size);
     161             : 
     162             :   /* Step 3: Initialize rewrite counters */
     163             :   srv6_ad_flow_localsid_t **ls_p;
     164           2 :   pool_get (sm->sids, ls_p);
     165           2 :   *ls_p = ls_mem;
     166           2 :   ls_mem->index = ls_p - sm->sids;
     167             : 
     168           2 :   vlib_validate_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
     169           2 :   vlib_validate_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
     170           2 :   vlib_validate_combined_counter (&(sm->sid_cache_full_counters),
     171             :                                   ls_mem->index);
     172           2 :   vlib_validate_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
     173           2 :   vlib_validate_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);
     174             : 
     175           2 :   vlib_zero_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
     176           2 :   vlib_zero_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
     177           2 :   vlib_zero_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index);
     178           2 :   vlib_zero_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
     179           2 :   vlib_zero_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);
     180             : 
     181           2 :   return 0;
     182             : }
     183             : 
     184             : static int
     185           2 : srv6_ad_flow_localsid_removal_fn (ip6_sr_localsid_t *localsid)
     186             : {
     187           2 :   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
     188           2 :   srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
     189             : 
     190           2 :   if (ls_mem->inner_type == AD_TYPE_IP4)
     191             :     {
     192             :       /* Disable End.AD4 rewrite node for this interface */
     193             :       int ret =
     194           1 :         vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
     195             :                                      ls_mem->sw_if_index_in, 0, 0, 0);
     196           1 :       if (ret != 0)
     197           0 :         return -1;
     198             : 
     199             :       /* Remove local SID pointer from interface table */
     200           1 :       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
     201             :     }
     202           1 :   else if (ls_mem->inner_type == AD_TYPE_IP6)
     203             :     {
     204             :       /* Disable End.AD6 rewrite node for this interface */
     205             :       int ret =
     206           1 :         vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
     207             :                                      ls_mem->sw_if_index_in, 0, 0, 0);
     208           1 :       if (ret != 0)
     209           0 :         return -1;
     210             : 
     211             :       /* Remove local SID pointer from interface table */
     212           1 :       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
     213             :     }
     214             : 
     215             :   /* Unlock (OIF, NHOP) adjacency */
     216           2 :   adj_unlock (ls_mem->nh_adj);
     217             : 
     218             :   /* Delete SID entry */
     219           2 :   pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
     220             : 
     221             :   /* Clean up local SID memory */
     222             :   srv6_ad_flow_entry_t *e;
     223           4 :   pool_foreach (e, ls_mem->cache)
     224             :     {
     225           2 :       vec_free (e->rw_data);
     226             :     }
     227           2 :   pool_free (ls_mem->cache);
     228           2 :   pool_free (ls_mem->lru_pool);
     229           2 :   clib_bihash_free_40_8 (&ls_mem->ftable);
     230           2 :   clib_mem_free (localsid->plugin_mem);
     231             : 
     232           2 :   return 0;
     233             : }
     234             : 
     235             : /**********************************/
     236             : /* SRv6 LocalSID format functions */
     237             : /*
     238             :  * Prints nicely the parameters of a localsid
     239             :  * Example: print "Table 5"
     240             :  */
     241             : u8 *
     242           6 : format_srv6_ad_flow_localsid (u8 *s, va_list *args)
     243             : {
     244           6 :   srv6_ad_flow_localsid_t *ls_mem = va_arg (*args, void *);
     245             : 
     246           6 :   vnet_main_t *vnm = vnet_get_main ();
     247           6 :   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
     248             : 
     249           6 :   if (ls_mem->inner_type == AD_TYPE_IP4)
     250             :     {
     251           3 :       s = format (s, "Next-hop:\t%U\n\t", format_ip4_address,
     252             :                   &ls_mem->nh_addr.ip4);
     253             :     }
     254           3 :   else if (ls_mem->inner_type == AD_TYPE_IP6)
     255             :     {
     256           3 :       s = format (s, "Next-hop:\t%U\n\t", format_ip6_address,
     257             :                   &ls_mem->nh_addr.ip6);
     258             :     }
     259             : 
     260           6 :   s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
     261             :               ls_mem->sw_if_index_out);
     262           6 :   s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
     263             :               ls_mem->sw_if_index_in);
     264             : 
     265             :   vlib_counter_t sid_bypass, sid_punt, sid_full, rw_valid, rw_invalid;
     266           6 :   vlib_get_combined_counter (&(sm->sid_bypass_counters), ls_mem->index,
     267             :                              &sid_bypass);
     268           6 :   vlib_get_combined_counter (&(sm->sid_punt_counters), ls_mem->index,
     269             :                              &sid_punt);
     270           6 :   vlib_get_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index,
     271             :                              &sid_full);
     272           6 :   vlib_get_combined_counter (&(sm->rw_valid_counters), ls_mem->index,
     273             :                              &rw_valid);
     274           6 :   vlib_get_combined_counter (&(sm->rw_invalid_counters), ls_mem->index,
     275             :                              &rw_invalid);
     276             : 
     277             :   s =
     278           6 :     format (s, "\tTraffic that bypassed the NF: \t[%Ld packets : %Ld bytes]\n",
     279             :             sid_bypass.packets, sid_bypass.bytes);
     280           6 :   s = format (s, "\tPunted traffic: \t[%Ld packets : %Ld bytes]\n",
     281             :               sid_punt.packets, sid_punt.bytes);
     282             :   s =
     283           6 :     format (s, "\tDropped traffic (cache full): \t[%Ld packets : %Ld bytes]\n",
     284             :             sid_full.packets, sid_full.bytes);
     285           6 :   s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
     286             :               rw_valid.packets, rw_valid.bytes);
     287           6 :   s = format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
     288             :               rw_invalid.packets, rw_invalid.bytes);
     289             : 
     290           6 :   return s;
     291             : }
     292             : 
     293             : /*
     294             :  * Process the parameters of a localsid
     295             :  * Example: process from:
     296             :  * sr localsid address cafe::1 behavior new_srv6_localsid 5
     297             :  * everything from behavior on... so in this case 'new_srv6_localsid 5'
     298             :  * Notice that it MUST match the keyword_str and params_str defined above.
     299             :  */
     300             : uword
     301          11 : unformat_srv6_ad_flow_localsid (unformat_input_t *input, va_list *args)
     302             : {
     303          11 :   void **plugin_mem_p = va_arg (*args, void **);
     304             :   srv6_ad_flow_localsid_t *ls_mem;
     305             : 
     306          11 :   vnet_main_t *vnm = vnet_get_main ();
     307             : 
     308          11 :   u8 inner_type = AD_TYPE_IP4;
     309             :   ip46_address_t nh_addr;
     310             :   u32 sw_if_index_out;
     311             :   u32 sw_if_index_in;
     312             : 
     313          11 :   u8 params = 0;
     314             : #define PARAM_AD_NH  (1 << 0)
     315             : #define PARAM_AD_OIF (1 << 1)
     316             : #define PARAM_AD_IIF (1 << 2)
     317             : 
     318          11 :   if (!unformat (input, "end.ad.flow"))
     319           9 :     return 0;
     320             : 
     321           7 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     322             :     {
     323           7 :       if (!(params & PARAM_AD_NH) &&
     324           2 :           unformat (input, "nh %U", unformat_ip4_address, &nh_addr.ip4))
     325             :         {
     326           1 :           inner_type = AD_TYPE_IP4;
     327           1 :           params |= PARAM_AD_NH;
     328             :         }
     329           6 :       if (!(params & PARAM_AD_NH) &&
     330           1 :           unformat (input, "nh %U", unformat_ip6_address, &nh_addr.ip6))
     331             :         {
     332           1 :           inner_type = AD_TYPE_IP6;
     333           1 :           params |= PARAM_AD_NH;
     334             :         }
     335           6 :       else if (!(params & PARAM_AD_OIF) &&
     336           2 :                unformat (input, "oif %U", unformat_vnet_sw_interface, vnm,
     337             :                          &sw_if_index_out))
     338             :         {
     339           2 :           params |= PARAM_AD_OIF;
     340             :         }
     341           4 :       else if (!(params & PARAM_AD_IIF) &&
     342           2 :                unformat (input, "iif %U", unformat_vnet_sw_interface, vnm,
     343             :                          &sw_if_index_in))
     344             :         {
     345           2 :           params |= PARAM_AD_IIF;
     346             :         }
     347             :       else
     348             :         {
     349             :           break;
     350             :         }
     351             :     }
     352             : 
     353             :   /* Make sure that all parameters are supplied */
     354           2 :   u8 params_chk = (PARAM_AD_NH | PARAM_AD_OIF | PARAM_AD_IIF);
     355           2 :   if ((params & params_chk) != params_chk)
     356             :     {
     357           0 :       return 0;
     358             :     }
     359             : 
     360             :   /* Allocate and initialize memory block for local SID parameters */
     361           2 :   ls_mem = clib_mem_alloc (sizeof *ls_mem);
     362           2 :   clib_memset (ls_mem, 0, sizeof *ls_mem);
     363           2 :   *plugin_mem_p = ls_mem;
     364             : 
     365             :   /* Set local SID parameters */
     366           2 :   ls_mem->inner_type = inner_type;
     367           2 :   if (inner_type == AD_TYPE_IP4)
     368           1 :     ls_mem->nh_addr.ip4 = nh_addr.ip4;
     369           1 :   else if (inner_type == AD_TYPE_IP6)
     370           1 :     ls_mem->nh_addr.ip6 = nh_addr.ip6;
     371           2 :   ls_mem->sw_if_index_out = sw_if_index_out;
     372           2 :   ls_mem->sw_if_index_in = sw_if_index_in;
     373             : 
     374           2 :   return 1;
     375             : }
     376             : 
     377             : /*************************/
     378             : /* SRv6 LocalSID FIB DPO */
     379             : static u8 *
     380           0 : format_srv6_ad_flow_dpo (u8 *s, va_list *args)
     381             : {
     382           0 :   index_t index = va_arg (*args, index_t);
     383           0 :   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
     384             : 
     385           0 :   return (format (s, "SR: dynamic_proxy_index:[%u]", index));
     386             : }
     387             : 
     388             : void
     389          12 : srv6_ad_flow_dpo_lock (dpo_id_t *dpo)
     390             : {
     391          12 : }
     392             : 
     393             : void
     394          12 : srv6_ad_flow_dpo_unlock (dpo_id_t *dpo)
     395             : {
     396          12 : }
     397             : 
     398             : const static dpo_vft_t srv6_ad_flow_vft = {
     399             :   .dv_lock = srv6_ad_flow_dpo_lock,
     400             :   .dv_unlock = srv6_ad_flow_dpo_unlock,
     401             :   .dv_format = format_srv6_ad_flow_dpo,
     402             : };
     403             : 
     404             : const static char *const srv6_ad_flow_ip6_nodes[] = {
     405             :   "srv6-ad-flow-localsid",
     406             :   NULL,
     407             : };
     408             : 
     409             : const static char *const *const srv6_ad_flow_nodes[DPO_PROTO_NUM] = {
     410             :   [DPO_PROTO_IP6] = srv6_ad_flow_ip6_nodes,
     411             : };
     412             : 
     413             : /**********************/
     414             : static clib_error_t *
     415         575 : srv6_ad_flow_init (vlib_main_t *vm)
     416             : {
     417         575 :   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
     418         575 :   int rv = 0;
     419             : 
     420         575 :   sm->vlib_main = vm;
     421         575 :   sm->vnet_main = vnet_get_main ();
     422             : 
     423             :   /* Create DPO */
     424         575 :   sm->srv6_ad_flow_dpo_type =
     425         575 :     dpo_register_new_type (&srv6_ad_flow_vft, srv6_ad_flow_nodes);
     426             : 
     427             :   /* Register SRv6 LocalSID */
     428         575 :   rv = sr_localsid_register_function (
     429             :     vm, function_name, keyword_str, def_str, params_str, 128,
     430             :     &sm->srv6_ad_flow_dpo_type, format_srv6_ad_flow_localsid,
     431             :     unformat_srv6_ad_flow_localsid, srv6_ad_flow_localsid_creation_fn,
     432             :     srv6_ad_flow_localsid_removal_fn);
     433         575 :   if (rv < 0)
     434           0 :     clib_error_return (0, "SRv6 LocalSID function could not be registered.");
     435             :   else
     436         575 :     sm->srv6_localsid_behavior_id = rv;
     437             : 
     438         575 :   return 0;
     439             : }
     440             : 
     441       20735 : VNET_FEATURE_INIT (srv6_ad4_flow_rewrite, static) = {
     442             :   .arc_name = "ip4-unicast",
     443             :   .node_name = "srv6-ad4-flow-rewrite",
     444             :   .runs_before = 0,
     445             : };
     446             : 
     447       20735 : VNET_FEATURE_INIT (srv6_ad6_flow_rewrite, static) = {
     448             :   .arc_name = "ip6-unicast",
     449             :   .node_name = "srv6-ad6-flow-rewrite",
     450             :   .runs_before = 0,
     451             : };
     452             : 
     453        1151 : VLIB_INIT_FUNCTION (srv6_ad_flow_init);
     454             : 
     455             : VLIB_PLUGIN_REGISTER () = {
     456             :   .version = VPP_BUILD_VER,
     457             :   .description = "Dynamic Segment Routing for IPv6 (SRv6) Proxy",
     458             : };
     459             : 
     460             : /*
     461             :  * fd.io coding-style-patch-verification: ON
     462             :  *
     463             :  * Local Variables:
     464             :  * eval: (c-set-style "gnu")
     465             :  * End:
     466             :  */

Generated by: LCOV version 1.14