LCOV - code coverage report
Current view: top level - plugins/ip_session_redirect - redirect.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 69 212 32.5 %
Date: 2023-07-05 22:20:52 Functions: 11 19 57.9 %

          Line data    Source code
       1             : /* Copyright (c) 2021-2022 Cisco and/or its affiliates.
       2             :  * Licensed under the Apache License, Version 2.0 (the "License");
       3             :  * you may not use this file except in compliance with the License.
       4             :  * You may obtain a copy of the License at:
       5             :  *
       6             :  *     http://www.apache.org/licenses/LICENSE-2.0
       7             :  *
       8             :  * Unless required by applicable law or agreed to in writing, software
       9             :  * distributed under the License is distributed on an "AS IS" BASIS,
      10             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      11             :  * See the License for the specific language governing permissions and
      12             :  * limitations under the License. */
      13             : #include <vlib/vlib.h>
      14             : #include <vnet/fib/fib_path_list.h>
      15             : #include <vnet/classify/vnet_classify.h>
      16             : #include <vnet/classify/in_out_acl.h>
      17             : #include <vnet/plugin/plugin.h>
      18             : #include <vpp/app/version.h>
      19             : #include "ip_session_redirect.h"
      20             : 
      21             : typedef struct
      22             : {
      23             :   u8 *match_and_table_index;
      24             :   dpo_id_t dpo;    /* forwarding dpo */
      25             :   fib_node_t node; /* linkage into the FIB graph */
      26             :   fib_node_index_t pl;
      27             :   u32 sibling;
      28             :   u32 parent_node_index;
      29             :   u32 opaque_index;
      30             :   u32 table_index;
      31             :   fib_forward_chain_type_t payload_type;
      32             :   u8 is_punt : 1;
      33             :   u8 is_ip6 : 1;
      34             : } ip_session_redirect_t;
      35             : 
      36             : typedef struct
      37             : {
      38             :   ip_session_redirect_t *pool;
      39             :   u32 *session_by_match_and_table_index;
      40             :   fib_node_type_t fib_node_type;
      41             : } ip_session_redirect_main_t;
      42             : 
      43             : static ip_session_redirect_main_t ip_session_redirect_main;
      44             : 
      45             : static int
      46          12 : ip_session_redirect_stack (ip_session_redirect_t *ipr)
      47             : {
      48          12 :   dpo_id_t dpo = DPO_INVALID;
      49             : 
      50          12 :   fib_path_list_contribute_forwarding (ipr->pl, ipr->payload_type,
      51          12 :                                        fib_path_list_is_popular (ipr->pl) ?
      52             :                                                FIB_PATH_LIST_FWD_FLAG_NONE :
      53             :                                                FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
      54             :                                        &dpo);
      55          12 :   dpo_stack_from_node (ipr->parent_node_index, &ipr->dpo, &dpo);
      56          12 :   dpo_reset (&dpo);
      57             : 
      58             :   /* update session with new next_index */
      59          24 :   return vnet_classify_add_del_session (
      60          12 :     &vnet_classify_main, ipr->table_index, ipr->match_and_table_index,
      61          12 :     ipr->dpo.dpoi_next_node /* hit_next_index */, ipr->opaque_index,
      62             :     0 /* advance */, CLASSIFY_ACTION_SET_METADATA,
      63             :     ipr->dpo.dpoi_index /* metadata */, 1 /* is_add */);
      64             : }
      65             : 
      66             : static ip_session_redirect_t *
      67          20 : ip_session_redirect_find (ip_session_redirect_main_t *im, u32 table_index,
      68             :                           const u8 *match)
      69             : {
      70             :   /* we are adding the table index at the end of the match string so we
      71             :    * can disambiguiate identical matches in different tables in
      72             :    * im->session_by_match_and_table_index */
      73          20 :   u8 *match_and_table_index = vec_dup (match);
      74          20 :   vec_add (match_and_table_index, (void *) &table_index, 4);
      75             :   uword *p =
      76          40 :     hash_get_mem (im->session_by_match_and_table_index, match_and_table_index);
      77          20 :   vec_free (match_and_table_index);
      78          20 :   if (!p)
      79           8 :     return 0;
      80          12 :   return pool_elt_at_index (im->pool, p[0]);
      81             : }
      82             : 
      83             : int
      84          12 : ip_session_redirect_add (vlib_main_t *vm, u32 table_index, u32 opaque_index,
      85             :                          dpo_proto_t proto, int is_punt, const u8 *match,
      86             :                          const fib_route_path_t *rpaths)
      87             : {
      88          12 :   ip_session_redirect_main_t *im = &ip_session_redirect_main;
      89             :   fib_forward_chain_type_t payload_type;
      90             :   ip_session_redirect_t *ipr;
      91             :   const char *pname;
      92             : 
      93          12 :   payload_type = fib_forw_chain_type_from_dpo_proto (proto);
      94          12 :   switch (payload_type)
      95             :     {
      96           6 :     case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
      97           6 :       pname = is_punt ? "ip4-punt-acl" : "ip4-inacl";
      98           6 :       break;
      99           6 :     case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
     100           6 :       pname = is_punt ? "ip6-punt-acl" : "ip6-inacl";
     101           6 :       break;
     102           0 :     default:
     103           0 :       return VNET_API_ERROR_INVALID_ADDRESS_FAMILY;
     104             :     }
     105             : 
     106          12 :   ipr = ip_session_redirect_find (im, table_index, match);
     107          12 :   if (ipr)
     108             :     {
     109             :       /* update to an existing session */
     110           4 :       fib_path_list_child_remove (ipr->pl, ipr->sibling);
     111           4 :       dpo_reset (&ipr->dpo);
     112             :     }
     113             :   else
     114             :     {
     115             :       /* allocate a new entry */
     116           8 :       pool_get (im->pool, ipr);
     117           8 :       fib_node_init (&ipr->node, im->fib_node_type);
     118           8 :       ipr->match_and_table_index = vec_dup ((u8 *) match);
     119             :       /* we are adding the table index at the end of the match string so we
     120             :        * can disambiguiate identical matches in different tables in
     121             :        * im->session_by_match_and_table_index */
     122           8 :       vec_add (ipr->match_and_table_index, (void *) &table_index, 4);
     123           8 :       ipr->table_index = table_index;
     124          16 :       hash_set_mem (im->session_by_match_and_table_index,
     125             :                     ipr->match_and_table_index, ipr - im->pool);
     126             :     }
     127             : 
     128          12 :   ipr->payload_type = payload_type;
     129          12 :   ipr->pl = fib_path_list_create (
     130             :     FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF, rpaths);
     131          24 :   ipr->sibling =
     132          12 :     fib_path_list_child_add (ipr->pl, im->fib_node_type, ipr - im->pool);
     133          12 :   ipr->parent_node_index = vlib_get_node_by_name (vm, (u8 *) pname)->index;
     134          12 :   ipr->opaque_index = opaque_index;
     135          12 :   ipr->is_punt = is_punt;
     136          12 :   ipr->is_ip6 = payload_type == FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
     137             : 
     138          12 :   return ip_session_redirect_stack (ipr);
     139             : }
     140             : 
     141             : int
     142           8 : ip_session_redirect_del (vlib_main_t *vm, u32 table_index, const u8 *match)
     143             : {
     144           8 :   ip_session_redirect_main_t *im = &ip_session_redirect_main;
     145           8 :   vnet_classify_main_t *cm = &vnet_classify_main;
     146             :   ip_session_redirect_t *ipr;
     147             :   int rv;
     148             : 
     149           8 :   ipr = ip_session_redirect_find (im, table_index, match);
     150           8 :   if (!ipr)
     151           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
     152             : 
     153           8 :   rv = vnet_classify_add_del_session (
     154           8 :     cm, ipr->table_index, ipr->match_and_table_index, 0 /* hit_next_index */,
     155             :     0 /* opaque_index */, 0 /* advance */, 0 /* action */, 0 /* metadata */,
     156             :     0 /* is_add */);
     157           8 :   if (rv)
     158           0 :     return rv;
     159             : 
     160          16 :   hash_unset_mem (im->session_by_match_and_table_index,
     161             :                   ipr->match_and_table_index);
     162           8 :   vec_free (ipr->match_and_table_index);
     163           8 :   fib_path_list_child_remove (ipr->pl, ipr->sibling);
     164           8 :   dpo_reset (&ipr->dpo);
     165           8 :   pool_put (im->pool, ipr);
     166           8 :   return 0;
     167             : }
     168             : 
     169             : static int
     170           0 : ip_session_redirect_show_yield (vlib_main_t *vm, f64 *start)
     171             : {
     172             :   /* yields for 2 clock ticks every 1 tick to avoid blocking the main thread
     173             :    * when dumping huge data structures */
     174           0 :   f64 now = vlib_time_now (vm);
     175           0 :   if (now - *start > 11e-6)
     176             :     {
     177           0 :       vlib_process_suspend (vm, 21e-6);
     178           0 :       *start = vlib_time_now (vm);
     179           0 :       return 1;
     180             :     }
     181             : 
     182           0 :   return 0;
     183             : }
     184             : 
     185             : static u8 *
     186           0 : format_ip_session_redirect (u8 *s, va_list *args)
     187             : {
     188           0 :   const ip_session_redirect_main_t *im = &ip_session_redirect_main;
     189           0 :   const ip_session_redirect_t *ipr =
     190             :     va_arg (*args, const ip_session_redirect_t *);
     191           0 :   index_t ipri = ipr - im->pool;
     192           0 :   const char *type = ipr->is_punt ? "[punt]" : "[acl]";
     193           0 :   const char *ip = ipr->is_ip6 ? "[ip6]" : "[ip4]";
     194             :   s =
     195           0 :     format (s, "[%u] %s %s table %d key %U opaque_index 0x%x\n", ipri, type,
     196             :             ip, ipr->table_index, format_hex_bytes, ipr->match_and_table_index,
     197           0 :             vec_len (ipr->match_and_table_index) - 4, ipr->opaque_index);
     198           0 :   s = format (s, " via:\n");
     199           0 :   s = format (s, "  %U", format_fib_path_list, ipr->pl, 2);
     200           0 :   s = format (s, " forwarding\n");
     201           0 :   s = format (s, "  %U", format_dpo_id, &ipr->dpo, 0);
     202           0 :   return s;
     203             : }
     204             : 
     205             : static clib_error_t *
     206           0 : ip_session_redirect_show_cmd (vlib_main_t *vm, unformat_input_t *main_input,
     207             :                               vlib_cli_command_t *cmd)
     208             : {
     209           0 :   ip_session_redirect_main_t *im = &ip_session_redirect_main;
     210           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     211           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
     212             :   ip_session_redirect_t *ipr;
     213           0 :   clib_error_t *error = 0;
     214           0 :   u32 table_index = ~0;
     215           0 :   int is_punt = -1;
     216           0 :   int is_ip6 = -1;
     217           0 :   u8 *match = 0;
     218           0 :   int max = 50;
     219           0 :   u8 *s = 0;
     220             : 
     221           0 :   if (unformat_is_eof (main_input))
     222           0 :     unformat_init (line_input, 0,
     223             :                    0); /* support straight "sh ip session redirect" */
     224           0 :   else if (!unformat_user (main_input, unformat_line_input, line_input))
     225           0 :     return 0;
     226             : 
     227           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     228             :     {
     229           0 :       if (unformat (line_input, "all"))
     230             :         ;
     231           0 :       else if (unformat (line_input, "punt"))
     232           0 :         is_punt = 1;
     233           0 :       else if (unformat (line_input, "acl"))
     234           0 :         is_punt = 0;
     235           0 :       else if (unformat (line_input, "ip4"))
     236           0 :         is_ip6 = 0;
     237           0 :       else if (unformat (line_input, "ip6"))
     238           0 :         is_ip6 = 1;
     239           0 :       else if (unformat (line_input, "table %u", &table_index))
     240             :         ;
     241           0 :       else if (unformat (line_input, "match %U", unformat_classify_match, cm,
     242             :                          &match, table_index))
     243             :         ;
     244           0 :       else if (unformat (line_input, "max %d", &max))
     245             :         ;
     246             :       else
     247             :         {
     248           0 :           error = unformat_parse_error (line_input);
     249           0 :           goto out;
     250             :         }
     251             :     }
     252             : 
     253           0 :   if (match)
     254             :     {
     255           0 :       ipr = ip_session_redirect_find (im, table_index, match);
     256           0 :       if (!ipr)
     257           0 :         vlib_cli_output (vm, "none");
     258             :       else
     259           0 :         vlib_cli_output (vm, "%U", format_ip_session_redirect, ipr);
     260             :     }
     261             :   else
     262             :     {
     263           0 :       f64 start = vlib_time_now (vm);
     264           0 :       ip_session_redirect_t *iprs = im->pool;
     265           0 :       int n = 0;
     266           0 :       pool_foreach (ipr, iprs)
     267             :         {
     268           0 :           if (n >= max)
     269             :             {
     270           0 :               n = -1; /* signal overflow */
     271           0 :               break;
     272             :             }
     273           0 :           if ((~0 == table_index || ipr->table_index == table_index) &&
     274           0 :               (-1 == is_punt || ipr->is_punt == is_punt) &&
     275           0 :               (-1 == is_ip6 || ipr->is_ip6 == is_ip6))
     276             :             {
     277           0 :               s = format (s, "%U\n", format_ip_session_redirect, ipr);
     278           0 :               n++;
     279             :             }
     280           0 :           if (ip_session_redirect_show_yield (vm, &start))
     281             :             {
     282             :               /* we must reload the pool as it might have moved */
     283           0 :               u32 ii = ipr - iprs;
     284           0 :               iprs = im->pool;
     285           0 :               ipr = iprs + ii;
     286             :             }
     287             :         }
     288           0 :       vec_add1 (s, 0);
     289           0 :       vlib_cli_output (vm, (char *) s);
     290           0 :       vec_free (s);
     291           0 :       if (-1 == n)
     292             :         {
     293           0 :           vlib_cli_output (
     294             :             vm,
     295             :             "\nPlease note: only the first %d entries displayed. "
     296             :             "To display more, specify max.",
     297             :             max);
     298             :         }
     299             :     }
     300             : 
     301           0 : out:
     302           0 :   vec_free (match);
     303           0 :   unformat_free (line_input);
     304           0 :   return error;
     305             : }
     306             : 
     307      175447 : VLIB_CLI_COMMAND (ip_session_redirect_show_command, static) = {
     308             :   .path = "show ip session redirect",
     309             :   .function = ip_session_redirect_show_cmd,
     310             :   .short_help = "show ip session redirect [all|[table <table-index>] "
     311             :                 "[punt|acl] [ip4|ip6] [match]]",
     312             : };
     313             : 
     314             : static clib_error_t *
     315           0 : ip_session_redirect_cmd (vlib_main_t *vm, unformat_input_t *main_input,
     316             :                          vlib_cli_command_t *cmd)
     317             : {
     318           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     319           0 :   vnet_classify_main_t *cm = &vnet_classify_main;
     320           0 :   dpo_proto_t proto = DPO_PROTO_IP4;
     321           0 :   fib_route_path_t *rpaths = 0, rpath;
     322           0 :   clib_error_t *error = 0;
     323           0 :   u32 opaque_index = ~0;
     324           0 :   u32 table_index = ~0;
     325           0 :   int is_punt = 0;
     326           0 :   int is_add = 1;
     327           0 :   u8 *match = 0;
     328             :   int rv;
     329             : 
     330           0 :   if (!unformat_user (main_input, unformat_line_input, line_input))
     331           0 :     return 0;
     332             : 
     333           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     334             :     {
     335           0 :       if (unformat (line_input, "del"))
     336           0 :         is_add = 0;
     337           0 :       else if (unformat (line_input, "add"))
     338           0 :         is_add = 1;
     339           0 :       else if (unformat (line_input, "punt"))
     340           0 :         is_punt = 1;
     341           0 :       else if (unformat (line_input, "table %u", &table_index))
     342             :         ;
     343           0 :       else if (unformat (line_input, "opaque-index %u", &opaque_index))
     344             :         ;
     345           0 :       else if (unformat (line_input, "match %U", unformat_classify_match, cm,
     346             :                          &match, table_index))
     347             :         ;
     348           0 :       else if (unformat (line_input, "via %U", unformat_fib_route_path, &rpath,
     349             :                          &proto))
     350           0 :         vec_add1 (rpaths, rpath);
     351             :       else
     352             :         {
     353           0 :           error = unformat_parse_error (line_input);
     354           0 :           goto out;
     355             :         }
     356             :     }
     357             : 
     358           0 :   if (~0 == table_index || 0 == match)
     359             :     {
     360           0 :       error = clib_error_create ("missing table index or match");
     361           0 :       goto out;
     362             :     }
     363             : 
     364           0 :   if (is_add)
     365             :     {
     366           0 :       if (0 == rpaths)
     367             :         {
     368           0 :           error = clib_error_create ("missing path");
     369           0 :           goto out;
     370             :         }
     371           0 :       rv = ip_session_redirect_add (vm, table_index, opaque_index, proto,
     372             :                                     is_punt, match, rpaths);
     373             :     }
     374             :   else
     375             :     {
     376           0 :       rv = ip_session_redirect_del (vm, table_index, match);
     377             :     }
     378             : 
     379           0 :   if (rv)
     380           0 :     error = clib_error_create ("failed with error %d", rv);
     381             : 
     382           0 : out:
     383           0 :   vec_free (rpaths);
     384           0 :   vec_free (match);
     385           0 :   unformat_free (line_input);
     386           0 :   return error;
     387             : }
     388             : 
     389      175447 : VLIB_CLI_COMMAND (ip_session_redirect_command, static) = {
     390             :   .path = "ip session redirect",
     391             :   .function = ip_session_redirect_cmd,
     392             :   .short_help = "ip session redirect [add] [punt] table <index> match <match> "
     393             :                 "via <path> | del table <index> match <match>"
     394             : };
     395             : 
     396             : static fib_node_t *
     397           0 : ip_session_redirect_get_node (fib_node_index_t index)
     398             : {
     399           0 :   ip_session_redirect_main_t *im = &ip_session_redirect_main;
     400           0 :   ip_session_redirect_t *ipr = pool_elt_at_index (im->pool, index);
     401           0 :   return &ipr->node;
     402             : }
     403             : 
     404             : static ip_session_redirect_t *
     405           0 : ip_session_redirect_get_from_node (fib_node_t *node)
     406             : {
     407           0 :   return (
     408             :     ip_session_redirect_t *) (((char *) node) -
     409             :                               STRUCT_OFFSET_OF (ip_session_redirect_t, node));
     410             : }
     411             : 
     412             : static void
     413           0 : ip_session_redirect_last_lock_gone (fib_node_t *node)
     414             : {
     415             :   /* the lifetime of the entry is managed by the table. */
     416           0 :   ASSERT (0);
     417           0 : }
     418             : 
     419             : /* A back walk has reached this entry */
     420             : static fib_node_back_walk_rc_t
     421           0 : ip_session_redirect_back_walk_notify (fib_node_t *node,
     422             :                                       fib_node_back_walk_ctx_t *ctx)
     423             : {
     424             :   int rv;
     425           0 :   ip_session_redirect_t *ipr = ip_session_redirect_get_from_node (node);
     426           0 :   rv = ip_session_redirect_stack (ipr);
     427           0 :   ASSERT (0 == rv);
     428           0 :   if (rv)
     429           0 :     clib_warning ("ip_session_redirect_stack() error %d", rv);
     430           0 :   return FIB_NODE_BACK_WALK_CONTINUE;
     431             : }
     432             : 
     433             : static const fib_node_vft_t ip_session_redirect_vft = {
     434             :   .fnv_get = ip_session_redirect_get_node,
     435             :   .fnv_last_lock = ip_session_redirect_last_lock_gone,
     436             :   .fnv_back_walk = ip_session_redirect_back_walk_notify,
     437             : };
     438             : 
     439             : static clib_error_t *
     440         559 : ip_session_redirect_init (vlib_main_t *vm)
     441             : {
     442         559 :   ip_session_redirect_main_t *im = &ip_session_redirect_main;
     443         559 :   im->session_by_match_and_table_index =
     444         559 :     hash_create_vec (0, sizeof (u8), sizeof (u32));
     445         559 :   im->fib_node_type = fib_node_register_new_type ("ip-session-redirect",
     446             :                                                   &ip_session_redirect_vft);
     447         559 :   return 0;
     448             : }
     449             : 
     450        1119 : VLIB_INIT_FUNCTION (ip_session_redirect_init);
     451             : 
     452             : VLIB_PLUGIN_REGISTER () = {
     453             :   .version = VPP_BUILD_VER,
     454             :   .description = "IP session redirect",
     455             : };
     456             : 
     457             : /*
     458             :  * fd.io coding-style-patch-verification: ON
     459             :  *
     460             :  * Local Variables:
     461             :  * eval: (c-set-style "gnu")
     462             :  * End:
     463             :  */

Generated by: LCOV version 1.14