LCOV - code coverage report
Current view: top level - plugins/urpf - urpf.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 43 114 37.7 %
Date: 2023-10-26 01:39:38 Functions: 10 14 71.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 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             : #include <urpf/urpf.h>
      17             : 
      18             : #include <vnet/fib/fib_table.h>
      19             : 
      20             : /* *INDENT-OFF* */
      21             : static const char *urpf_feat_arcs[N_AF][VLIB_N_DIR] =
      22             : {
      23             :   [AF_IP4] = {
      24             :     [VLIB_RX] = "ip4-unicast",
      25             :     [VLIB_TX] = "ip4-output",
      26             :   },
      27             :   [AF_IP6] = {
      28             :     [VLIB_RX] = "ip6-unicast",
      29             :     [VLIB_TX] = "ip6-output",
      30             :   },
      31             : };
      32             : 
      33             : static const char *urpf_feats[N_AF][VLIB_N_DIR][URPF_N_MODES] =
      34             : {
      35             :   [AF_IP4] = {
      36             :     [VLIB_RX] = {
      37             :       [URPF_MODE_STRICT] = "ip4-rx-urpf-strict",
      38             :       [URPF_MODE_LOOSE] = "ip4-rx-urpf-loose",
      39             :     },
      40             :     [VLIB_TX] = {
      41             :       [URPF_MODE_STRICT] = "ip4-tx-urpf-strict",
      42             :       [URPF_MODE_LOOSE] = "ip4-tx-urpf-loose",
      43             :     },
      44             :   },
      45             :   [AF_IP6] = {
      46             :     [VLIB_RX] = {
      47             :       [URPF_MODE_STRICT] = "ip6-rx-urpf-strict",
      48             :       [URPF_MODE_LOOSE] = "ip6-rx-urpf-loose",
      49             :     },
      50             :     [VLIB_TX] = {
      51             :       [URPF_MODE_STRICT] = "ip6-tx-urpf-strict",
      52             :       [URPF_MODE_LOOSE] = "ip6-tx-urpf-loose",
      53             :     },
      54             :   },
      55             : };
      56             : /* *INDENT-ON* */
      57             : 
      58             : /**
      59             :  * Per-af, per-direction, per-interface uRPF configs
      60             :  */
      61             : 
      62             : urpf_data_t *urpf_cfgs[N_AF][VLIB_N_DIR];
      63             : 
      64             : u8 *
      65           0 : format_urpf_mode (u8 * s, va_list * a)
      66             : {
      67           0 :   urpf_mode_t mode = va_arg (*a, int);
      68             : 
      69           0 :   switch (mode)
      70             :     {
      71             : #define _(a,b)                                  \
      72             :     case URPF_MODE_##a:                         \
      73             :       return (format (s, "%s", b));
      74           0 :       foreach_urpf_mode
      75             : #undef _
      76             :     }
      77             : 
      78           0 :   return (format (s, "unknown"));
      79             : }
      80             : 
      81             : static uword
      82           0 : unformat_urpf_mode (unformat_input_t * input, va_list * args)
      83             : {
      84           0 :   urpf_mode_t *mode = va_arg (*args, urpf_mode_t *);
      85             : 
      86             :   if (0)
      87             :     ;
      88             : #define _(a,b)                                                  \
      89             :   else if (unformat (input, b))                                 \
      90             :     {                                                           \
      91             :     *mode = URPF_MODE_##a;                                      \
      92             :     return (1);                                                 \
      93             :     }
      94           0 :   foreach_urpf_mode
      95             : #undef _
      96           0 :     return 0;
      97             : }
      98             : 
      99             : int
     100          12 : urpf_update (urpf_mode_t mode, u32 sw_if_index, ip_address_family_t af,
     101             :              vlib_dir_t dir, u32 table_id)
     102             : {
     103             :   fib_protocol_t proto;
     104             :   u32 fib_index;
     105          12 :   if (table_id != ~0)
     106             :     {
     107          12 :       proto = ip_address_family_to_fib_proto (af);
     108          12 :       fib_index = fib_table_find (proto, table_id);
     109          12 :       if (fib_index == (~0))
     110           0 :         return VNET_API_ERROR_INVALID_VALUE;
     111             :     }
     112             :   else
     113             :     {
     114           0 :       bool is_ip4 = (AF_IP4 == af);
     115           0 :       u32 *fib_index_by_sw_if_index = is_ip4 ?
     116           0 :                                               ip4_main.fib_index_by_sw_if_index :
     117             :                                               ip6_main.fib_index_by_sw_if_index;
     118             : 
     119           0 :       fib_index = fib_index_by_sw_if_index[sw_if_index];
     120             :     }
     121             :   urpf_data_t old;
     122          12 :   urpf_mode_t off = URPF_MODE_OFF;
     123          12 :   urpf_data_t empty = { .fib_index = 0, .mode = off };
     124          22 :   vec_validate_init_empty (urpf_cfgs[af][dir], sw_if_index, empty);
     125          12 :   old = urpf_cfgs[af][dir][sw_if_index];
     126             : 
     127          12 :   urpf_data_t data = { .fib_index = fib_index,
     128             :                        .mode = mode,
     129          12 :                        .fib_index_is_custom = (table_id != ~0) };
     130          12 :   urpf_cfgs[af][dir][sw_if_index] = data;
     131          12 :   if (data.mode != old.mode || data.fib_index != old.fib_index)
     132             :     {
     133          12 :       if (URPF_MODE_OFF != old.mode)
     134             :         /* disable what we have */
     135           8 :         vnet_feature_enable_disable (urpf_feat_arcs[af][dir],
     136           8 :                                      urpf_feats[af][dir][old.mode],
     137             :                                      sw_if_index, 0, 0, 0);
     138             : 
     139          12 :       if (URPF_MODE_OFF != data.mode)
     140             :         /* enable what's new */
     141           8 :         vnet_feature_enable_disable (urpf_feat_arcs[af][dir],
     142           8 :                                      urpf_feats[af][dir][data.mode],
     143             :                                      sw_if_index, 1, 0, 0);
     144             :     }
     145             :   /* else - no change to existing config */
     146          12 :   return 0;
     147             : }
     148             : 
     149             : static void
     150         946 : urpf_table_bind_v4 (ip4_main_t *im, uword opaque, u32 sw_if_index,
     151             :                     u32 new_fib_index, u32 old_fib_index)
     152             : {
     153             :   vlib_dir_t dir;
     154         946 :   urpf_data_t empty = { .fib_index = 0, .mode = URPF_MODE_OFF };
     155        2838 :   FOREACH_VLIB_DIR (dir)
     156             :   {
     157        2364 :     vec_validate_init_empty (urpf_cfgs[AF_IP4][dir], sw_if_index, empty);
     158        1892 :     if (!urpf_cfgs[AF_IP4][dir][sw_if_index].fib_index_is_custom)
     159             :       {
     160        1892 :         urpf_cfgs[AF_IP4][dir][sw_if_index].fib_index = new_fib_index;
     161             :       }
     162             :   }
     163         946 : }
     164             : 
     165             : static void
     166         777 : urpf_table_bind_v6 (ip6_main_t *im, uword opaque, u32 sw_if_index,
     167             :                     u32 new_fib_index, u32 old_fib_index)
     168             : {
     169             :   vlib_dir_t dir;
     170         777 :   urpf_data_t empty = { .fib_index = 0, .mode = URPF_MODE_OFF };
     171        2331 :   FOREACH_VLIB_DIR (dir)
     172             :   {
     173        1768 :     vec_validate_init_empty (urpf_cfgs[AF_IP6][dir], sw_if_index, empty);
     174        1554 :     if (!urpf_cfgs[AF_IP6][dir][sw_if_index].fib_index_is_custom)
     175             :       {
     176        1554 :         urpf_cfgs[AF_IP6][dir][sw_if_index].fib_index = new_fib_index;
     177             :       }
     178             :   }
     179         777 : }
     180             : 
     181             : static clib_error_t *
     182         575 : urpf_init (vlib_main_t *vm)
     183             : {
     184         575 :   ip4_table_bind_callback_t cb4 = {
     185             :     .function = urpf_table_bind_v4,
     186             :   };
     187         575 :   vec_add1 (ip4_main.table_bind_callbacks, cb4);
     188             : 
     189         575 :   ip6_table_bind_callback_t cb6 = {
     190             :     .function = urpf_table_bind_v6,
     191             :   };
     192         575 :   vec_add1 (ip6_main.table_bind_callbacks, cb6);
     193         575 :   return (NULL);
     194             : }
     195             : 
     196        1151 : VLIB_INIT_FUNCTION (urpf_init);
     197             : 
     198             : static clib_error_t *
     199           0 : urpf_cli_update (vlib_main_t * vm,
     200             :                  unformat_input_t * input, vlib_cli_command_t * cmd)
     201             : {
     202           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     203           0 :   vnet_main_t *vnm = vnet_get_main ();
     204           0 :   clib_error_t *error = NULL;
     205             :   ip_address_family_t af;
     206             :   urpf_mode_t mode;
     207             :   u32 sw_if_index;
     208             :   vlib_dir_t dir;
     209             :   u32 table_id;
     210             : 
     211           0 :   sw_if_index = ~0;
     212           0 :   af = AF_IP4;
     213           0 :   dir = VLIB_RX;
     214           0 :   mode = URPF_MODE_STRICT;
     215           0 :   table_id = ~0;
     216             : 
     217           0 :   if (!unformat_user (input, unformat_line_input, line_input))
     218           0 :     return 0;
     219             : 
     220           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     221             :     {
     222           0 :       if (unformat (line_input, "%U",
     223             :                     unformat_vnet_sw_interface, vnm, &sw_if_index))
     224             :         ;
     225           0 :       else if (unformat (line_input, "%U", unformat_urpf_mode, &mode))
     226             :         ;
     227           0 :       else if (unformat (line_input, "table %d", &table_id))
     228             :         ;
     229           0 :       else if (unformat (line_input, "%U", unformat_ip_address_family, &af))
     230             :         ;
     231           0 :       else if (unformat (line_input, "%U", unformat_vlib_rx_tx, &dir))
     232             :         ;
     233             :       else
     234             :         {
     235           0 :           error = unformat_parse_error (line_input);
     236           0 :           goto done;
     237             :         }
     238             :     }
     239             : 
     240           0 :   if (~0 == sw_if_index)
     241             :     {
     242           0 :       error = clib_error_return (0, "unknown interface `%U'",
     243             :                                  format_unformat_error, line_input);
     244           0 :       goto done;
     245             :     }
     246             : 
     247           0 :   int rv = 0;
     248           0 :   rv = urpf_update (mode, sw_if_index, af, dir, table_id);
     249           0 :   if (rv)
     250             :     {
     251           0 :       error = clib_error_return (0, "unknown table id");
     252           0 :       goto done;
     253             :     }
     254           0 : done:
     255           0 :   unformat_free (line_input);
     256             : 
     257           0 :   return error;
     258             : }
     259             : 
     260             : /*?
     261             :  * This command configures uRPF on an interface.
     262             :  * Two flavours are supported (the default is strict):
     263             :  * - loose: accept ingress packet if there is a route to reach the source
     264             :  * - strict: accept ingress packet if it arrived on an interface which
     265             :  *          the route to the source uses. i.e. an interface that the source
     266             :  *          is reachable via.
     267             :  *
     268             :  * @cliexpar
     269             :  * @parblock
     270             :  * Example of graph node before range checking is enabled:
     271             :  * @cliexstart{show vlib graph ip4-rx-urpf-strict}
     272             :  *            Name                      Next                    Previous
     273             :  * ip4-rx-urpf-strict         ip4-drop [0]
     274             :  * @cliexend
     275             :  *
     276             :  * Example of how to enable unicast source checking on an interface:
     277             :  * @cliexcmd{set urpf ip4 rx GigabitEthernet2/0/0 loose}
     278             :  *
     279             :  * Example of graph node after range checking is enabled:
     280             :  * @cliexstart{show vlib graph ip4-rx-urpf-loose}
     281             :  *       Name                    Next                  Previous
     282             :  * ip4-rx-urpf-loose          ip4-drop [0]        ip4-input-no-checksum
     283             :  *                    ip4-source-and-port-range-       ip4-input
     284             :  * @cliexend
     285             :  *
     286             :  * Example of how to display the feature enabled on an interface:
     287             :  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
     288             :  * IP feature paths configured on GigabitEthernet2/0/0...
     289             :  *
     290             :  * ipv4 unicast:
     291             :  *   ip4-rx-urpf-loose
     292             :  *   ip4-lookup
     293             :  *
     294             :  * ipv4 multicast:
     295             :  *   ip4-lookup-multicast
     296             :  *
     297             :  * ipv4 multicast:
     298             :  *   interface-output
     299             :  *
     300             :  * ipv6 unicast:
     301             :  *   ip6-lookup
     302             :  *
     303             :  * ipv6 multicast:
     304             :  *   ip6-lookup
     305             :  *
     306             :  * ipv6 multicast:
     307             :  *   interface-output
     308             :  * @cliexend
     309             :  *
     310             :  * Example of how to disable unicast source checking on an interface:
     311             :  * @cliexcmd{set urpf ip4 off GigabitEthernet2/0/0}
     312             :  * @endparblock
     313             : ?*/
     314             : /* *INDENT-OFF* */
     315       15551 : VLIB_CLI_COMMAND (set_interface_ip_source_check_command, static) = {
     316             :   .path = "set urpf",
     317             :   .function = urpf_cli_update,
     318             :   .short_help = "set urpf [ip4|ip6] [rx|tx] [off|strict|loose] "
     319             :                 "<INTERFACE> [table <table>]",
     320             : };
     321             : /* *INDENT-ON* */
     322             : 
     323             : static clib_error_t *
     324           0 : urpf_cli_accept (vlib_main_t * vm,
     325             :                  unformat_input_t * input, vlib_cli_command_t * cmd)
     326             : {
     327           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     328           0 :   clib_error_t *error = NULL;
     329             :   fib_prefix_t fpfx;
     330             :   ip_prefix_t pfx;
     331             :   u32 table_id, is_add, fib_index;
     332             : 
     333           0 :   is_add = 1;
     334           0 :   table_id = 0;
     335             : 
     336             :   /* Get a line of input. */
     337           0 :   if (!unformat_user (input, unformat_line_input, line_input))
     338           0 :     return 0;
     339             : 
     340           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     341             :     {
     342           0 :       if (unformat (line_input, "table %d", &table_id))
     343             :         ;
     344           0 :       else if (unformat (line_input, "del"))
     345           0 :         is_add = 0;
     346           0 :       else if (unformat (line_input, "add"))
     347           0 :         is_add = 1;
     348           0 :       else if (unformat (line_input, "%U", unformat_ip_prefix, &pfx))
     349             :         ;
     350             :       else
     351             :         {
     352           0 :           error = unformat_parse_error (line_input);
     353           0 :           goto done;
     354             :         }
     355             :     }
     356             : 
     357           0 :   ip_prefix_to_fib_prefix (&pfx, &fpfx);
     358             : 
     359           0 :   fib_index = fib_table_find (fpfx.fp_proto, table_id);
     360             : 
     361           0 :   if (~0 == fib_index)
     362             :     {
     363           0 :       error = clib_error_return (0, "Nonexistent table id %d", table_id);
     364           0 :       goto done;
     365             :     }
     366             : 
     367           0 :   if (is_add)
     368           0 :     fib_table_entry_special_add (fib_index,
     369             :                                  &fpfx,
     370             :                                  FIB_SOURCE_URPF_EXEMPT, FIB_ENTRY_FLAG_DROP);
     371             :   else
     372           0 :     fib_table_entry_special_remove (fib_index, &fpfx, FIB_SOURCE_URPF_EXEMPT);
     373             : 
     374           0 : done:
     375           0 :   unformat_free (line_input);
     376             : 
     377           0 :   return (error);
     378             : }
     379             : 
     380             : /*?
     381             :  * Add an exemption for a prefix to pass the Unicast Reverse Path
     382             :  * Forwarding (uRPF) loose check. This is for testing purposes only.
     383             :  * If the '<em>table</em>' is not enter it is defaulted to 0. Default
     384             :  * is to '<em>add</em>'. VPP always performs a loose uRPF check for
     385             :  * for-us traffic.
     386             :  *
     387             :  * @cliexpar
     388             :  * Example of how to add a uRPF exception to a FIB table to pass the
     389             :  * loose RPF tests:
     390             :  * @cliexcmd{set urpf-accept table 7 10.0.0.0/8 add}
     391             : ?*/
     392             : /* *INDENT-OFF* */
     393       15551 : VLIB_CLI_COMMAND (urpf_accept_command, static) = {
     394             :   .path = "set urpf-accept",
     395             :   .function = urpf_cli_accept,
     396             :   .short_help = "urpf-accept [table <table-id>] [add|del] <PREFIX>",
     397             : };
     398             : /* *INDENT-ON* */
     399             : 
     400             : /*
     401             :  * fd.io coding-style-patch-verification: ON
     402             :  *
     403             :  * Local Variables:
     404             :  * eval: (c-set-style "gnu")
     405             :  * End:
     406             :  */

Generated by: LCOV version 1.14