VPP-286: Add CLI Command documentation via doxygen comments for vnet/vnet/ip.
[vpp.git] / vnet / vnet / ip / ip4_source_check.c
index 47e22f2..b791384 100644 (file)
  */
 
 #include <vnet/ip/ip.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_urpf_list.h>
+#include <vnet/dpo/load_balance.h>
+
+/**
+ * @file
+ * @brief IPv4 Unicast Source Check.
+ *
+ * This file contains the IPv4 interface unicast source check.
+ */
+
 
 typedef struct {
   u8 packet_data[64];
+    index_t urpf;
 } ip4_source_check_trace_t;
 
 static u8 * format_ip4_source_check_trace (u8 * s, va_list * va)
@@ -67,11 +79,7 @@ typedef enum {
 } ip4_source_check_type_t;
 
 typedef union {
-  struct {
-    u32 no_default_route : 1;
-    u32 fib_index : 31;
-  };
-  u32 as_u32[1];
+  u32 fib_index;
 } ip4_source_check_config_t;
 
 always_inline uword
@@ -82,7 +90,7 @@ ip4_source_check_inline (vlib_main_t * vm,
 {
   ip4_main_t * im = &ip4_main;
   ip_lookup_main_t * lm = &im->lookup_main;
-  ip_config_main_t * cm = &lm->rx_config_mains[VNET_UNICAST];
+  ip_config_main_t * cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
   u32 n_left_from, * from, * to_next;
   u32 next_index;
   vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, ip4_input_node.index);
@@ -110,9 +118,9 @@ ip4_source_check_inline (vlib_main_t * vm,
          ip4_fib_mtrie_t * mtrie0, * mtrie1;
          ip4_fib_mtrie_leaf_t leaf0, leaf1;
          ip4_source_check_config_t * c0, * c1;
-         ip_adjacency_t * adj0, * adj1;
-         u32 pi0, next0, pass0, adj_index0;
-         u32 pi1, next1, pass1, adj_index1;
+         const load_balance_t * lb0, * lb1;
+         u32 pi0, next0, pass0, lb_index0;
+         u32 pi1, next1, pass1, lb_index1;
 
          /* Prefetch next iteration. */
          {
@@ -142,16 +150,16 @@ ip4_source_check_inline (vlib_main_t * vm,
          ip1 = vlib_buffer_get_current (p1);
 
          c0 = vnet_get_config_data (&cm->config_main,
-                                    &vnet_buffer (p0)->ip.current_config_index,
+                                    &p0->current_config_index,
                                     &next0,
                                     sizeof (c0[0]));
          c1 = vnet_get_config_data (&cm->config_main,
-                                    &vnet_buffer (p1)->ip.current_config_index,
+                                    &p1->current_config_index,
                                     &next1,
                                     sizeof (c1[0]));
 
-         mtrie0 = &vec_elt_at_index (im->fibs, c0->fib_index)->mtrie;
-         mtrie1 = &vec_elt_at_index (im->fibs, c1->fib_index)->mtrie;
+         mtrie0 = &ip4_fib_get (c0->fib_index)->mtrie;
+         mtrie1 = &ip4_fib_get (c1->fib_index)->mtrie;
 
          leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
 
@@ -167,30 +175,28 @@ ip4_source_check_inline (vlib_main_t * vm,
          leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, &ip0->src_address, 3);
          leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, &ip1->src_address, 3);
 
-         adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
-         adj_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
-
-         ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, c0->fib_index,
-                                                          &ip0->src_address,
-                                                          c0->no_default_route));
-         ASSERT (adj_index1 == ip4_fib_lookup_with_table (im, c1->fib_index,
-                                                          &ip1->src_address,
-                                                          c1->no_default_route));
+         lb_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+         lb_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
 
-         adj0 = ip_get_adjacency (lm, adj_index0);
-         adj1 = ip_get_adjacency (lm, adj_index1);
+         lb0 = load_balance_get(lb_index0);
+         lb1 = load_balance_get(lb_index1);
 
          /* Pass multicast. */
          pass0 = ip4_address_is_multicast (&ip0->src_address) || ip0->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF);
          pass1 = ip4_address_is_multicast (&ip1->src_address) || ip1->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF);
 
-         pass0 |= (adj0->lookup_next_index == IP_LOOKUP_NEXT_REWRITE
-                   && (source_check_type == IP4_SOURCE_CHECK_REACHABLE_VIA_ANY
-                       || vnet_buffer (p0)->sw_if_index[VLIB_RX] == adj0->rewrite_header.sw_if_index));
-         pass1 |= (adj1->lookup_next_index == IP_LOOKUP_NEXT_REWRITE
-                   && (source_check_type == IP4_SOURCE_CHECK_REACHABLE_VIA_ANY
-                       || vnet_buffer (p1)->sw_if_index[VLIB_RX] == adj1->rewrite_header.sw_if_index));
-
+         if (IP4_SOURCE_CHECK_REACHABLE_VIA_RX == source_check_type)
+         {
+             pass0 |= fib_urpf_check(lb0->lb_urpf,
+                                     vnet_buffer (p0)->sw_if_index[VLIB_RX]);
+             pass1 |= fib_urpf_check(lb1->lb_urpf,
+                                     vnet_buffer (p1)->sw_if_index[VLIB_RX]);
+         }
+         else
+         {
+             pass0 |= fib_urpf_check_size(lb0->lb_urpf);
+             pass1 |= fib_urpf_check_size(lb1->lb_urpf);
+         }
          next0 = (pass0 ? next0 : IP4_SOURCE_CHECK_NEXT_DROP);
          next1 = (pass1 ? next1 : IP4_SOURCE_CHECK_NEXT_DROP);
 
@@ -209,8 +215,8 @@ ip4_source_check_inline (vlib_main_t * vm,
          ip4_fib_mtrie_t * mtrie0;
          ip4_fib_mtrie_leaf_t leaf0;
          ip4_source_check_config_t * c0;
-         ip_adjacency_t * adj0;
-         u32 pi0, next0, pass0, adj_index0;
+         u32 pi0, next0, pass0, lb_index0;
+         const load_balance_t * lb0;
 
          pi0 = from[0];
          to_next[0] = pi0;
@@ -223,11 +229,11 @@ ip4_source_check_inline (vlib_main_t * vm,
          ip0 = vlib_buffer_get_current (p0);
 
          c0 = vnet_get_config_data (&cm->config_main,
-                                    &vnet_buffer (p0)->ip.current_config_index,
+                                    &p0->current_config_index,
                                     &next0,
                                     sizeof (c0[0]));
 
-         mtrie0 = &vec_elt_at_index (im->fibs, c0->fib_index)->mtrie;
+         mtrie0 = &ip4_fib_get (c0->fib_index)->mtrie;
 
          leaf0 = IP4_FIB_MTRIE_LEAF_ROOT;
 
@@ -239,19 +245,22 @@ ip4_source_check_inline (vlib_main_t * vm,
 
          leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, &ip0->src_address, 3);
 
-         adj_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+         lb_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
 
-         ASSERT (adj_index0 == ip4_fib_lookup_with_table (im, c0->fib_index,
-                                                          &ip0->src_address,
-                                                          c0->no_default_route));
-         adj0 = ip_get_adjacency (lm, adj_index0);
+         lb0 = load_balance_get(lb_index0);
 
          /* Pass multicast. */
          pass0 = ip4_address_is_multicast (&ip0->src_address) || ip0->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF);
 
-         pass0 |= (adj0->lookup_next_index == IP_LOOKUP_NEXT_REWRITE
-                   && (source_check_type == IP4_SOURCE_CHECK_REACHABLE_VIA_ANY
-                       || vnet_buffer (p0)->sw_if_index[VLIB_RX] == adj0->rewrite_header.sw_if_index));
+         if (IP4_SOURCE_CHECK_REACHABLE_VIA_RX == source_check_type)
+         {
+             pass0 |= fib_urpf_check(lb0->lb_urpf,
+                                     vnet_buffer (p0)->sw_if_index[VLIB_RX]);
+         }
+         else
+         {
+             pass0 |= fib_urpf_check_size(lb0->lb_urpf);
+         }
 
          next0 = (pass0 ? next0 : IP4_SOURCE_CHECK_NEXT_DROP);
          p0->error = error_node->errors[IP4_ERROR_UNICAST_SOURCE_CHECK_FAILS];
@@ -297,6 +306,9 @@ VLIB_REGISTER_NODE (ip4_check_source_reachable_via_any) = {
   .format_trace = format_ip4_source_check_trace,
 };
 
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_check_source_reachable_via_any,
+                             ip4_source_check_reachable_via_any)
+
 VLIB_REGISTER_NODE (ip4_check_source_reachable_via_rx) = {
   .function = ip4_source_check_reachable_via_rx,
   .name = "ip4-source-check-via-rx",
@@ -311,43 +323,63 @@ VLIB_REGISTER_NODE (ip4_check_source_reachable_via_rx) = {
   .format_trace = format_ip4_source_check_trace,
 };
 
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_check_source_reachable_via_rx,
+                             ip4_source_check_reachable_via_rx)
+
 static clib_error_t *
 set_ip_source_check (vlib_main_t * vm,
                     unformat_input_t * input,
                     vlib_cli_command_t * cmd)
 {
+  unformat_input_t _line_input, * line_input = &_line_input;
   vnet_main_t * vnm = vnet_get_main();
   ip4_main_t * im = &ip4_main;
   ip_lookup_main_t * lm = &im->lookup_main;
-  ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
+  ip_config_main_t * rx_cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
   clib_error_t * error = 0;
   u32 sw_if_index, is_del, ci;
   ip4_source_check_config_t config;
-  ip4_rx_feature_type_t type;
+  u32 feature_index;
 
   sw_if_index = ~0;
+  is_del = 0;
+  feature_index = im->ip4_unicast_rx_feature_source_reachable_via_rx;
+
+  if (! unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat_user (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
+         ;
+      else if (unformat (line_input, "del"))
+        is_del = 1;
+      else if (unformat (line_input, "strict"))
+       feature_index = im->ip4_unicast_rx_feature_source_reachable_via_rx;
+      else if (unformat (line_input, "loose"))
+        feature_index = im->ip4_unicast_rx_feature_source_reachable_via_any;
+      else
+        {
+         error = unformat_parse_error (line_input);
+         goto done;
+       }
+    }
 
-  if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
+  if (~0 == sw_if_index)
     {
       error = clib_error_return (0, "unknown interface `%U'",
-                                format_unformat_error, input);
+                                format_unformat_error, line_input);
       goto done;
     }
 
-  is_del = 0;
-  config.no_default_route = 0;
   config.fib_index = im->fib_index_by_sw_if_index[sw_if_index];
-  type = IP4_RX_FEATURE_SOURCE_CHECK_REACHABLE_VIA_RX;
-  if (unformat (input, "del"))
-    is_del = 1;
-
   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
   ci = (is_del
        ? vnet_config_del_feature
        : vnet_config_add_feature)
     (vm, &rx_cm->config_main,
      ci,
-     type,
+     feature_index,
      &config,
      sizeof (config));
   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
@@ -356,11 +388,165 @@ set_ip_source_check (vlib_main_t * vm,
   return error;
 }
 
+/*?
+ * This command adds the 'ip4-source-check-via-rx' graph node for
+ * a given interface. By adding the IPv4 source check graph node to
+ * an interface, the code verifies that the source address of incoming
+ * unicast packets are reachable over the incoming interface. Two flavours
+ * are supported (the default is strict):
+ * - loose: accept ingress packet if there is a route to reach the source
+ * - strict: accept ingress packet if it arrived on an interface which
+ *          the route to the source uses. i.e. an interface that the source
+ *          is reachable via.
+ *
+ * @cliexpar
+ * @parblock
+ * Example of graph node before range checking is enabled:
+ * @cliexstart{show vlib graph ip4-source-check-via-rx}
+ *            Name                      Next                    Previous
+ * ip4-source-check-via-rx         error-drop [0]
+ * @cliexend
+ *
+ * Example of how to enable unicast source checking on an interface:
+ * @cliexcmd{set interface ip source-check GigabitEthernet2/0/0 loose}
+ *
+ * Example of graph node after range checking is enabled:
+ * @cliexstart{show vlib graph ip4-source-check-via-rx}
+ *            Name                      Next                    Previous
+ * ip4-source-check-via-rx         error-drop [0]         ip4-input-no-checksum
+ *                           ip4-source-and-port-range-         ip4-input
+ * @cliexend
+ *
+ * Example of how to display the feature enabed on an interface:
+ * @cliexstart{show ip interface features GigabitEthernet2/0/0}
+ * IP feature paths configured on GigabitEthernet2/0/0...
+ *
+ * ipv4 unicast:
+ *   ip4-source-check-via-rx
+ *   ip4-lookup
+ *
+ * ipv4 multicast:
+ *   ip4-lookup-multicast
+ *
+ * ipv4 multicast:
+ *   interface-output
+ *
+ * ipv6 unicast:
+ *   ip6-lookup
+ *
+ * ipv6 multicast:
+ *   ip6-lookup
+ *
+ * ipv6 multicast:
+ *   interface-output
+ * @cliexend
+ *
+ * Example of how to disable unicast source checking on an interface:
+ * @cliexcmd{set interface ip source-check GigabitEthernet2/0/0 del}
+ * @endparblock
+?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (set_interface_ip_source_check_command, static) = {
   .path = "set interface ip source-check",
   .function = set_ip_source_check,
-  .short_help = "Set IP4/IP6 interface unicast source check",
+  .short_help = "set interface ip source-check <interface> [strict|loose] [del]",
 };
+/* *INDENT-ON* */
+
+static clib_error_t *
+ip_source_check_accept (vlib_main_t * vm,
+                       unformat_input_t * input,
+                       vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, * line_input = &_line_input;
+  fib_prefix_t pfx = {
+      .fp_proto = FIB_PROTOCOL_IP4,
+  };
+  clib_error_t * error = NULL;
+  u32 table_id, is_add, fib_index;
+
+  is_add = 1;
+  table_id = ~0;
+
+  /* Get a line of input. */
+  if (! unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "table %d", &table_id))
+       ;
+      else if (unformat (line_input, "del"))
+       is_add = 0;
+      else if (unformat (line_input, "add"))
+       is_add = 1;
+      else if (unformat (line_input, "%U/%d",
+                        unformat_ip4_address,
+                        &pfx.fp_addr.ip4,
+                        &pfx.fp_len))
+         pfx.fp_proto = FIB_PROTOCOL_IP4;
+      else
+      {
+         error = unformat_parse_error (line_input);
+         goto done;
+      }
+    }
+
+  if (~0 != table_id)
+    {
+      fib_index = fib_table_id_find_fib_index(pfx.fp_proto, table_id);
+      if (~0 == fib_index)
+        {
+         error = clib_error_return (0,
+                                    "Nonexistent table id %d",
+                                    table_id);
+         goto done;
+       }
+    }
+  else
+    {
+      fib_index = 0;
+    }
+
+  if (is_add)
+    {
+      fib_table_entry_special_add(fib_index,
+                                 &pfx,
+                                 FIB_SOURCE_URPF_EXEMPT,
+                                 FIB_ENTRY_FLAG_DROP,
+                                 ADJ_INDEX_INVALID);
+    }
+  else
+  {
+      fib_table_entry_special_remove(fib_index,
+                                    &pfx,
+                                    FIB_SOURCE_URPF_EXEMPT);
+  }
+
+done:
+  return (error);
+}
+
+/*?
+ * Add an exemption for a prefix to pass the Unicast Reverse Path
+ * Forwarding (uRPF) loose check. This is for testing purposes only.
+ * If the '<em>table</em>' is not enter it is defaulted to 0. Default
+ * is to '<em>add</em>'. VPP always performs a loose uRPF check for
+ * for-us traffic.
+ *
+ * @cliexpar
+ * Example of how to add a uRPF exception to a FIB table to pass the
+ * loose RPF tests:
+ * @cliexcmd{ip urpf-accept table 7 add}
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ip_source_check_accept_command, static) = {
+  .path = "ip urpf-accept",
+  .function = ip_source_check_accept,
+  .short_help = "ip urpf-accept [table <table-id>] [add|del]",
+};
+/* *INDENT-ON* */
+
 
 /* Dummy init function to get us linked in. */
 clib_error_t * ip4_source_check_init (vlib_main_t * vm)