unicast RPF for FIB2.0 02/3302/5
authorNeale Ranns <nranns@cisco.com>
Mon, 3 Oct 2016 12:05:48 +0000 (13:05 +0100)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 7 Oct 2016 21:32:24 +0000 (21:32 +0000)
In a heirarchical FIB performing a unicast RPF check would require the traversal of the data-plane graph to seek out all the adjacency objects and then read those to find their interface. This is not efficient. Instead, for each path-list we construct a list of unique input interfaces and link this uRPF-list against the entry in the prefix table. In the data-plane the uRPF list can be retrieved from the load-balance lookup result and the RPF check is a simple and efficient walk across the minimal interface list. The uRPF-list is maintained as the routing heirarchy changes, in a similar way to the data-plane object graph.
We also provide a knob to allow an arbitrary prefix to pass the loose check.

Change-Id: Ie7c0ae3c4483ef467cfd5b136ee0315ff98ec15b
Signed-off-by: Neale Ranns <nranns@cisco.com>
23 files changed:
vlib/vlib/trace.c
vnet/Makefile.am
vnet/etc/scripts/urpf [new file with mode: 0644]
vnet/vnet/dpo/load_balance.c
vnet/vnet/dpo/load_balance.h
vnet/vnet/dpo/load_balance_map.c
vnet/vnet/dpo/load_balance_map.h
vnet/vnet/fib/fib_entry.c
vnet/vnet/fib/fib_entry.h
vnet/vnet/fib/fib_entry_src.c
vnet/vnet/fib/fib_entry_src_rr.c
vnet/vnet/fib/fib_path.c
vnet/vnet/fib/fib_path.h
vnet/vnet/fib/fib_path_list.c
vnet/vnet/fib/fib_path_list.h
vnet/vnet/fib/fib_test.c
vnet/vnet/fib/fib_types.h
vnet/vnet/fib/fib_urpf_list.c [new file with mode: 0644]
vnet/vnet/fib/fib_urpf_list.h [new file with mode: 0644]
vnet/vnet/gre/gre.c
vnet/vnet/ip/ip4_forward.c
vnet/vnet/ip/ip4_source_check.c
vnet/vnet/ip/lookup.c

index 5b64c55..8fe3dac 100644 (file)
@@ -367,35 +367,41 @@ static clib_error_t *
 cli_add_trace_buffer (vlib_main_t * vm,
                      unformat_input_t * input, vlib_cli_command_t * cmd)
 {
 cli_add_trace_buffer (vlib_main_t * vm,
                      unformat_input_t * input, vlib_cli_command_t * cmd)
 {
+  unformat_input_t _line_input, *line_input = &_line_input;
   vlib_trace_main_t *tm;
   vlib_trace_node_t *tn;
   u32 node_index, add;
   u8 verbose = 0;
 
   vlib_trace_main_t *tm;
   vlib_trace_node_t *tn;
   u32 node_index, add;
   u8 verbose = 0;
 
-  while (unformat_check_input (input) != (uword) UNFORMAT_END_OF_INPUT)
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != (uword) UNFORMAT_END_OF_INPUT)
     {
     {
-      if (unformat
-         (input, "%U %d", unformat_vlib_node, vm, &node_index, &add))
+      if (unformat (line_input, "%U %d",
+                   unformat_vlib_node, vm, &node_index, &add))
        ;
        ;
-      else if (unformat (input, "verbose"))
+      else if (unformat (line_input, "verbose"))
        verbose = 1;
       else
        return clib_error_create ("expected NODE COUNT, got `%U'",
        verbose = 1;
       else
        return clib_error_create ("expected NODE COUNT, got `%U'",
-                                 format_unformat_error, input);
+                                 format_unformat_error, line_input);
     }
 
     }
 
+  /* *INDENT-OFF* */
   foreach_vlib_main ((
   foreach_vlib_main ((
-                      {
-                      void *oldheap;
-                      tm = &this_vlib_main->trace_main;
-                      tm->trace_active_hint = 1;
-                      tm->verbose = verbose;
-                      oldheap =
-                      clib_mem_set_heap (this_vlib_main->heap_base);
-                      vec_validate (tm->nodes, node_index);
-                      tn = tm->nodes + node_index;
-                      tn->limit += add; clib_mem_set_heap (oldheap);
-                      }));
+    {
+      void *oldheap;
+      tm = &this_vlib_main->trace_main;
+      tm->trace_active_hint = 1;
+      tm->verbose = verbose;
+      oldheap =
+       clib_mem_set_heap (this_vlib_main->heap_base);
+      vec_validate (tm->nodes, node_index);
+      tn = tm->nodes + node_index;
+      tn->limit += add; clib_mem_set_heap (oldheap);
+    }));
+  /* *INDENT-ON* */
 
   return 0;
 }
 
   return 0;
 }
index 88d5564..c0ae70d 100644 (file)
@@ -763,6 +763,7 @@ libvnet_la_SOURCES +=                               \
   vnet/fib/fib_path_list.c                      \
   vnet/fib/fib_path.c                          \
   vnet/fib/fib_path_ext.c                      \
   vnet/fib/fib_path_list.c                      \
   vnet/fib/fib_path.c                          \
   vnet/fib/fib_path_ext.c                      \
+  vnet/fib/fib_urpf_list.c                     \
   vnet/fib/fib_attached_export.c
 
 nobase_include_HEADERS +=                      \
   vnet/fib/fib_attached_export.c
 
 nobase_include_HEADERS +=                      \
diff --git a/vnet/etc/scripts/urpf b/vnet/etc/scripts/urpf
new file mode 100644 (file)
index 0000000..a4d8752
--- /dev/null
@@ -0,0 +1,86 @@
+
+create loop int
+
+set int state loop0 up
+set int ip addr loop0 10.10.10.10/24
+
+packet-generator new {
+  name transit-deny
+  limit 1
+  node ip4-input
+  size 64-64
+  no-recycle
+  data {
+    UDP: 1.2.3.4 -> 2.2.2.2
+    UDP: 3000 -> 3001
+    length 128 checksum 0 incrementing 1
+  }
+}
+
+packet-generator new {
+  name transit-allow
+  limit 1
+  node ip4-input
+  size 64-64
+  no-recycle
+  data {
+    UDP: 1.1.1.1 -> 2.2.2.2
+    UDP: 3000 -> 3001
+    length 128 checksum 0 incrementing 1
+  }
+}
+
+packet-generator new {
+  name transit-allow-from-excemption
+  limit 1
+  node ip4-input
+  size 64-64
+  no-recycle
+  data {
+    UDP: 11.11.12.13 -> 2.2.2.2
+    UDP: 6000 -> 6001
+    length 128 checksum 0 incrementing 1
+  }
+}
+
+packet-generator new {
+  name for-us-allow-from-excemption
+  limit 1
+  node ip4-input
+  size 64-64
+  no-recycle
+  data {
+    UDP: 11.11.12.13 -> 10.10.10.10
+    UDP: 6000 -> 6001
+    length 128 checksum 0 incrementing 1
+  }
+}
+
+packet-generator new {
+  name for-us-allow
+  limit 1
+  node ip4-input
+  size 64-64
+  no-recycle
+  data {
+    UDP: 1.1.1.1 -> 10.10.10.10
+    UDP: 3000 -> 3001
+    length 128 checksum 0 incrementing 1
+  }
+}
+
+tr add pg-input 100
+
+set int ip addr pg0 10.10.11.10/24
+
+set interface ip source-check pg0 strict
+
+ip route add 1.1.1.1/32 via 10.10.11.11 pg0
+ip route add 2.2.2.2/32 via 10.10.10.11 loop0
+
+ip urpf-accept 11.11.0.0/16
+
+#set interface ip source-check pg0 strict del
+#set interface ip source-check pg0 loose
+
+#ip urpf-accept del 11.11.0.0/16
index 093661d..fc78850 100644 (file)
@@ -20,6 +20,7 @@
 #include <vppinfra/math.h>              /* for fabs */
 #include <vnet/adj/adj.h>
 #include <vnet/adj/adj_internal.h>
 #include <vppinfra/math.h>              /* for fabs */
 #include <vnet/adj/adj.h>
 #include <vnet/adj/adj_internal.h>
+#include <vnet/fib/fib_urpf_list.h>
 
 /*
  * distribution error tolerance for load-balancing
 
 /*
  * distribution error tolerance for load-balancing
@@ -87,6 +88,7 @@ load_balance_alloc_i (void)
     memset(lb, 0, sizeof(*lb));
 
     lb->lb_map = INDEX_INVALID;
     memset(lb, 0, sizeof(*lb));
 
     lb->lb_map = INDEX_INVALID;
+    lb->lb_urpf = INDEX_INVALID;
     vlib_validate_combined_counter(&(load_balance_main.lbm_to_counters),
                                    load_balance_get_index(lb));
     vlib_validate_combined_counter(&(load_balance_main.lbm_via_counters),
     vlib_validate_combined_counter(&(load_balance_main.lbm_to_counters),
                                    load_balance_get_index(lb));
     vlib_validate_combined_counter(&(load_balance_main.lbm_via_counters),
@@ -117,7 +119,7 @@ load_balance_format (index_t lbi,
 
     s = format(s, "%U: ", format_dpo_type, DPO_LOAD_BALANCE);
     s = format(s, "[index:%d buckets:%d ", lbi, lb->lb_n_buckets);
 
     s = format(s, "%U: ", format_dpo_type, DPO_LOAD_BALANCE);
     s = format(s, "[index:%d buckets:%d ", lbi, lb->lb_n_buckets);
-    s = format(s, "locks:%d ", lb->lb_locks);
+    s = format(s, "uRPF:%d ", lb->lb_urpf);
     s = format(s, "to:[%Ld:%Ld]", to.packets, to.bytes);
     if (0 != via.packets)
     {
     s = format(s, "to:[%Ld:%Ld]", to.packets, to.bytes);
     if (0 != via.packets)
     {
@@ -236,6 +238,35 @@ load_balance_is_drop (const dpo_id_t *dpo)
     return (0);
 }
 
     return (0);
 }
 
+void
+load_balance_set_urpf (index_t lbi,
+                      index_t urpf)
+{
+    load_balance_t *lb;
+    index_t old;
+
+    lb = load_balance_get(lbi);
+
+    /*
+     * packets in flight we see this change. but it's atomic, so :P
+     */
+    old = lb->lb_urpf;
+    lb->lb_urpf = urpf;
+
+    fib_urpf_list_unlock(old);
+    fib_urpf_list_lock(urpf);
+}
+
+index_t
+load_balance_get_urpf (index_t lbi)
+{
+    load_balance_t *lb;
+
+    lb = load_balance_get(lbi);
+
+    return (lb->lb_urpf);
+}
+
 const dpo_id_t *
 load_balance_get_bucket (index_t lbi,
                          u32 bucket)
 const dpo_id_t *
 load_balance_get_bucket (index_t lbi,
                          u32 bucket)
@@ -652,6 +683,9 @@ load_balance_destroy (load_balance_t *lb)
         vec_free(lb->lb_buckets);
     }
 
         vec_free(lb->lb_buckets);
     }
 
+    fib_urpf_list_unlock(lb->lb_urpf);
+    load_balance_map_unlock(lb->lb_map);
+
     pool_put(load_balance_pool, lb);
 }
 
     pool_put(load_balance_pool, lb);
 }
 
@@ -677,6 +711,7 @@ load_balance_mem_show (void)
                          pool_elts(load_balance_pool),
                          pool_len(load_balance_pool),
                          sizeof(load_balance_t));
                          pool_elts(load_balance_pool),
                          pool_len(load_balance_pool),
                          sizeof(load_balance_t));
+    load_balance_map_show_mem();
 }
 
 const static dpo_vft_t lb_vft = {
 }
 
 const static dpo_vft_t lb_vft = {
index d630a2c..4bf3ecf 100644 (file)
@@ -111,6 +111,11 @@ typedef struct load_balance_t_ {
      */
     index_t lb_map;
 
      */
     index_t lb_map;
 
+    /**
+     * This is the index of the uRPF list for this LB
+     */
+    index_t lb_urpf;
+
     /**
      * the hash config to use when selecting a bucket. this is a u16
      */
     /**
      * the hash config to use when selecting a bucket. this is a u16
      */
@@ -160,6 +165,9 @@ extern void load_balance_multipath_update(
 extern void load_balance_set_bucket(index_t lbi,
                                    u32 bucket,
                                    const dpo_id_t *next);
 extern void load_balance_set_bucket(index_t lbi,
                                    u32 bucket,
                                    const dpo_id_t *next);
+extern void load_balance_set_urpf(index_t lbi,
+                                 index_t urpf);
+extern index_t load_balance_get_urpf(index_t lbi);
 
 extern u8* format_load_balance(u8 * s, va_list * args);
 
 
 extern u8* format_load_balance(u8 * s, va_list * args);
 
index f08801f..70ce1bf 100644 (file)
@@ -526,6 +526,15 @@ load_balance_map_module_init (void)
     lb_maps_by_path_index = hash_create(0, sizeof(fib_node_list_t));
 }
 
     lb_maps_by_path_index = hash_create(0, sizeof(fib_node_list_t));
 }
 
+void
+load_balance_map_show_mem (void)
+{
+    fib_show_memory_usage("Load-Balance Map",
+                         pool_elts(load_balance_map_pool),
+                         pool_len(load_balance_map_pool),
+                         sizeof(load_balance_map_t));
+}
+
 static clib_error_t *
 load_balance_map_show (vlib_main_t * vm,
                        unformat_input_t * input,
 static clib_error_t *
 load_balance_map_show (vlib_main_t * vm,
                        unformat_input_t * input,
index f080e97..454bf4b 100644 (file)
@@ -60,6 +60,7 @@ extern void load_balance_map_unlock(index_t lbmi);
 extern void load_balance_map_path_state_change(fib_node_index_t path_index);
 
 extern u8* format_load_balance_map(u8 *s, va_list ap);
 extern void load_balance_map_path_state_change(fib_node_index_t path_index);
 
 extern u8* format_load_balance_map(u8 *s, va_list ap);
+extern void load_balance_map_show_mem(void);
 
 /**
  * The encapsulation breakages are for fast DP access
 
 /**
  * The encapsulation breakages are for fast DP access
index 1821319..0692200 100644 (file)
@@ -473,6 +473,21 @@ static const fib_node_vft_t fib_entry_vft = {
     .fnv_mem_show = fib_entry_show_memory,
 };
 
     .fnv_mem_show = fib_entry_show_memory,
 };
 
+/**
+ * @brief Contribute the set of Adjacencies that this entry forwards with
+ * to build the uRPF list of its children
+ */
+void
+fib_entry_contribute_urpf (fib_node_index_t entry_index,
+                          index_t urpf)
+{
+    fib_entry_t *fib_entry;
+
+    fib_entry = fib_entry_get(entry_index);
+
+    return (fib_path_list_contribute_urpf(fib_entry->fe_parent, urpf));
+}
+
 /*
  * fib_entry_contribute_forwarding
  *
 /*
  * fib_entry_contribute_forwarding
  *
index 1016bb2..bfebe5d 100644 (file)
@@ -99,9 +99,14 @@ typedef enum fib_source_t_ {
     FIB_SOURCE_AE,
     /**
      * Recursive resolution source.
     FIB_SOURCE_AE,
     /**
      * Recursive resolution source.
-     * Used to install an entry that is thre resolution traget of another.
+     * Used to install an entry that is the resolution traget of another.
      */
     FIB_SOURCE_RR,
      */
     FIB_SOURCE_RR,
+    /**
+     * uRPF bypass/exemption.
+     * Used to install an entry that is exempt from the loose uRPF check
+     */
+    FIB_SOURCE_URPF_EXEMPT,
     /**
      * The default route source.
      * The default route is always added to the FIB table (like the
     /**
      * The default route source.
      * The default route is always added to the FIB table (like the
@@ -138,6 +143,7 @@ _Static_assert (sizeof(fib_source_t) == 1,
     [FIB_SOURCE_RR] = "recursive-resolution",          \
     [FIB_SOURCE_AE] = "attached_export",               \
     [FIB_SOURCE_MPLS] = "mpls",                        \
     [FIB_SOURCE_RR] = "recursive-resolution",          \
     [FIB_SOURCE_AE] = "attached_export",               \
     [FIB_SOURCE_MPLS] = "mpls",                        \
+    [FIB_SOURCE_URPF_EXEMPT] = "urpf-exempt",          \
     [FIB_SOURCE_DEFAULT_ROUTE] = "default-route",      \
 }
 
     [FIB_SOURCE_DEFAULT_ROUTE] = "default-route",      \
 }
 
@@ -376,7 +382,7 @@ typedef struct fib_entry_t_ {
      *     paint the header straight on without the need to check the packet
      *     type to derive the EOS bit value.
      */
      *     paint the header straight on without the need to check the packet
      *     type to derive the EOS bit value.
      */
-    dpo_id_t fe_lb[FIB_FORW_CHAIN_NUM];
+    dpo_id_t fe_lb[FIB_FORW_CHAIN_MPLS_NUM];
     /**
      * Vector of source infos.
      * Most entries will only have 1 source. So we optimise for memory usage,
     /**
      * Vector of source infos.
      * Most entries will only have 1 source. So we optimise for memory usage,
@@ -449,6 +455,8 @@ extern fib_entry_src_flag_t fib_entry_path_remove(fib_node_index_t fib_entry_ind
 extern fib_entry_src_flag_t fib_entry_delete(fib_node_index_t fib_entry_index,
                                             fib_source_t source);
 
 extern fib_entry_src_flag_t fib_entry_delete(fib_node_index_t fib_entry_index,
                                             fib_source_t source);
 
+extern void fib_entry_contribute_urpf(fib_node_index_t path_index,
+                                     index_t urpf);
 extern void fib_entry_contribute_forwarding(
     fib_node_index_t fib_entry_index,
     fib_forward_chain_type_t type,
 extern void fib_entry_contribute_forwarding(
     fib_node_index_t fib_entry_index,
     fib_forward_chain_type_t type,
index 0bca17d..6107e3e 100644 (file)
 #include <vnet/dpo/mpls_label_dpo.h>
 #include <vnet/dpo/drop_dpo.h>
 
 #include <vnet/dpo/mpls_label_dpo.h>
 #include <vnet/dpo/drop_dpo.h>
 
-#include "fib_entry_src.h"
-#include "fib_table.h"
-#include "fib_path_ext.h"
+#include <vnet/fib/fib_entry_src.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_path_ext.h>
+#include <vnet/fib/fib_urpf_list.h>
 
 /*
  * per-source type vft
 
 /*
  * per-source type vft
@@ -368,6 +369,33 @@ fib_entry_src_mk_lb (fib_entry_t *fib_entry,
     load_balance_multipath_update(dpo_lb,
                                   ctx.next_hops,
                                   fib_entry_calc_lb_flags(&ctx));
     load_balance_multipath_update(dpo_lb,
                                   ctx.next_hops,
                                   fib_entry_calc_lb_flags(&ctx));
+
+    /*
+     * if this entry is sourced by the uRPF-exempt source then we
+     * append the always present local0 interface (index 0) to the
+     * uRPF list so it is not empty. that way packets pass the loose check.
+     */
+    index_t ui = fib_path_list_get_urpf(esrc->fes_pl);
+
+    if (fib_entry_is_sourced(fib_entry_get_index(fib_entry),
+                            FIB_SOURCE_URPF_EXEMPT) &&
+       (0 == fib_urpf_check_size(ui)))
+    {
+       /*
+        * The uRPF list we get from the path-list is shared by all
+        * other users of the list, but the uRPF exemption applies
+        * only to this prefix. So we need our own list.
+        */
+       ui = fib_urpf_list_alloc_and_lock();
+       fib_urpf_list_append(ui, 0);
+       fib_urpf_list_bake(ui);
+       load_balance_set_urpf(dpo_lb->dpoi_index, ui);
+       fib_urpf_list_unlock(ui);
+    }
+    else
+    {
+       load_balance_set_urpf(dpo_lb->dpoi_index, ui);
+    }
 }
 
 void
 }
 
 void
@@ -425,17 +453,13 @@ fib_entry_src_action_uninstall (fib_entry_t *fib_entry)
 
     fct = fib_entry_get_default_chain_type(fib_entry);
     /*
 
     fct = fib_entry_get_default_chain_type(fib_entry);
     /*
-     * uninstall the forwarding chain for the given source from the
-     * forwarding tables
+     * uninstall the forwarding chain from the forwarding tables
      */
     FIB_ENTRY_DBG(fib_entry, "uninstall: %d",
                  fib_entry->fe_adj_index);
 
     if (dpo_id_is_valid(&fib_entry->fe_lb[fct]))
     {
      */
     FIB_ENTRY_DBG(fib_entry, "uninstall: %d",
                  fib_entry->fe_adj_index);
 
     if (dpo_id_is_valid(&fib_entry->fe_lb[fct]))
     {
-       /* fib_forward_chain_type_t fct; */
-       /* fib_path_ext_t *path_ext; */
-
        fib_table_fwding_dpo_remove(
            fib_entry->fe_fib_index,
            &fib_entry->fe_prefix,
        fib_table_fwding_dpo_remove(
            fib_entry->fe_fib_index,
            &fib_entry->fe_prefix,
index f6b8960..9219d58 100644 (file)
@@ -244,4 +244,5 @@ void
 fib_entry_src_rr_register (void)
 {
     fib_entry_src_register(FIB_SOURCE_RR, &rr_src_vft);    
 fib_entry_src_rr_register (void)
 {
     fib_entry_src_register(FIB_SOURCE_RR, &rr_src_vft);    
+    fib_entry_src_register(FIB_SOURCE_URPF_EXEMPT, &rr_src_vft);    
 }
 }
index 03cc7be..1b82f46 100644 (file)
 
 #include <vnet/adj/adj.h>
 
 
 #include <vnet/adj/adj.h>
 
-#include "fib_path.h"
-#include "fib_node.h"
-#include "fib_table.h"
-#include "fib_entry.h"
-#include "fib_path_list.h"
-#include "fib_internal.h"
+#include <vnet/fib/fib_path.h>
+#include <vnet/fib/fib_node.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_path_list.h>
+#include <vnet/fib/fib_internal.h>
+#include <vnet/fib/fib_urpf_list.h>
 
 /**
  * Enurmeration of path types
 
 /**
  * Enurmeration of path types
@@ -1551,6 +1552,62 @@ fib_path_get_weight (fib_node_index_t path_index)
     return (path->fp_weight);
 }
 
     return (path->fp_weight);
 }
 
+/**
+ * @brief Contribute the path's adjacency to the list passed.
+ * By calling this function over all paths, recursively, a child
+ * can construct its full set of forwarding adjacencies, and hence its
+ * uRPF list.
+ */
+void
+fib_path_contribute_urpf (fib_node_index_t path_index,
+                         index_t urpf)
+{
+    fib_path_t *path;
+
+    if (!fib_path_is_resolved(path_index))
+       return;
+
+    path = fib_path_get(path_index);
+
+    switch (path->fp_type)
+    {
+    case FIB_PATH_TYPE_ATTACHED_NEXT_HOP:
+       fib_urpf_list_append(urpf, path->attached_next_hop.fp_interface);
+       break;
+
+    case FIB_PATH_TYPE_ATTACHED:
+       fib_urpf_list_append(urpf, path->attached.fp_interface);
+       break;
+
+    case FIB_PATH_TYPE_RECURSIVE:
+       fib_entry_contribute_urpf(path->fp_via_fib, urpf);
+       break;
+
+    case FIB_PATH_TYPE_EXCLUSIVE:
+    case FIB_PATH_TYPE_SPECIAL:
+       /*
+        * these path types may link to an adj, if that's what
+        * the clinet gave
+        */
+       if (dpo_is_adj(&path->fp_dpo))
+       {
+           ip_adjacency_t *adj;
+
+           adj = adj_get(path->fp_dpo.dpoi_index);
+
+           fib_urpf_list_append(urpf, adj->rewrite_header.sw_if_index);
+       }
+       break;
+
+    case FIB_PATH_TYPE_DEAG:
+    case FIB_PATH_TYPE_RECEIVE:
+       /*
+        * these path types don't link to an adj
+        */
+       break;
+    }
+}
+
 void
 fib_path_contribute_forwarding (fib_node_index_t path_index,
                                fib_forward_chain_type_t fct,
 void
 fib_path_contribute_forwarding (fib_node_index_t path_index,
                                fib_forward_chain_type_t fct,
index 16ca358..2c141a4 100644 (file)
@@ -141,6 +141,8 @@ extern load_balance_path_t * fib_path_append_nh_for_multipath_hash(
 extern void fib_path_contribute_forwarding(fib_node_index_t path_index,
                                           fib_forward_chain_type_t type,
                                           dpo_id_t *dpo);
 extern void fib_path_contribute_forwarding(fib_node_index_t path_index,
                                           fib_forward_chain_type_t type,
                                           dpo_id_t *dpo);
+extern void fib_path_contribute_urpf(fib_node_index_t path_index,
+                                    index_t urpf);
 extern adj_index_t fib_path_get_adj(fib_node_index_t path_index);
 extern int fib_path_recursive_loop_detect(fib_node_index_t path_index,
                                          fib_node_index_t **entry_indicies);
 extern adj_index_t fib_path_get_adj(fib_node_index_t path_index);
 extern int fib_path_recursive_loop_detect(fib_node_index_t path_index,
                                          fib_node_index_t **entry_indicies);
index 611fe9f..a582749 100644 (file)
@@ -23,6 +23,7 @@
 #include <vnet/fib/fib_internal.h>
 #include <vnet/fib/fib_node_list.h>
 #include <vnet/fib/fib_walk.h>
 #include <vnet/fib/fib_internal.h>
 #include <vnet/fib/fib_node_list.h>
 #include <vnet/fib/fib_walk.h>
+#include <vnet/fib/fib_urpf_list.h>
 
 /**
  * FIB path-list
 
 /**
  * FIB path-list
@@ -47,10 +48,15 @@ typedef struct fib_path_list_t_ {
     fib_protocol_t fpl_nh_proto;
 
     /**
     fib_protocol_t fpl_nh_proto;
 
     /**
-     * Vector of paths indecies for all configured paths.
+     * Vector of paths indicies for all configured paths.
      * For shareable path-lists this list MUST not change.
      */
     fib_node_index_t *fpl_paths;
      * For shareable path-lists this list MUST not change.
      */
     fib_node_index_t *fpl_paths;
+
+    /**
+     * the RPF list calculated for this path list
+     */
+    fib_node_index_t fpl_urpf;
 } fib_path_list_t;
 
 /*
 } fib_path_list_t;
 
 /*
@@ -138,6 +144,8 @@ format_fib_path_list (u8 * s, va_list * args)
            }
        }
     }
            }
        }
     }
+    s = format (s, " %U\n", format_fib_urpf_list, path_list->fpl_urpf);
+
     vec_foreach (path_index, path_list->fpl_paths)
     {
        s = fib_path_format(*path_index, s);
     vec_foreach (path_index, path_list->fpl_paths)
     {
        s = fib_path_format(*path_index, s);
@@ -321,9 +329,10 @@ fib_path_list_destroy (fib_path_list_t *path_list)
     vec_foreach (path_index, path_list->fpl_paths)
     {
        fib_path_destroy(*path_index);
     vec_foreach (path_index, path_list->fpl_paths)
     {
        fib_path_destroy(*path_index);
-    }    
+    }
 
     vec_free(path_list->fpl_paths);
 
     vec_free(path_list->fpl_paths);
+    fib_urpf_list_unlock(path_list->fpl_urpf);
 
     fib_node_deinit(&path_list->fpl_node);
     pool_put(fib_path_list_pool, path_list);
 
     fib_node_deinit(&path_list->fpl_node);
     pool_put(fib_path_list_pool, path_list);
@@ -396,6 +405,60 @@ fib_path_list_mk_lb (fib_path_list_t *path_list,
     vec_free(hash_key);
 }
 
     vec_free(hash_key);
 }
 
+/**
+ * @brief [re]build the path list's uRPF list
+ */
+static void
+fib_path_list_mk_urpf (fib_path_list_t *path_list)
+{
+    fib_node_index_t *path_index;
+
+    /*
+     * ditch the old one. by iterating through all paths we are going
+     * to re-find all the adjs that were in the old one anyway. If we
+     * keep the old one, then the |sort|uniq requires more work.
+     * All users of the RPF list have their own lock, so we can release
+     * immediately.
+     */
+    fib_urpf_list_unlock(path_list->fpl_urpf);
+    path_list->fpl_urpf = fib_urpf_list_alloc_and_lock();
+
+    vec_foreach (path_index, path_list->fpl_paths)
+    {
+       fib_path_contribute_urpf(*path_index, path_list->fpl_urpf);
+    }
+
+    fib_urpf_list_bake(path_list->fpl_urpf);
+}
+
+/**
+ * @brief Contribute (add) this path list's uRPF list. This allows the child
+ * to construct an aggregate list.
+ */
+void
+fib_path_list_contribute_urpf (fib_node_index_t path_list_index,
+                              index_t urpf)
+{
+    fib_path_list_t *path_list;
+
+    path_list = fib_path_list_get(path_list_index);
+
+    fib_urpf_list_combine(urpf, path_list->fpl_urpf);
+}
+
+/**
+ * @brief Return the the child the RPF list pre-built for this path list
+ */
+index_t
+fib_path_list_get_urpf (fib_node_index_t path_list_index)
+{
+    fib_path_list_t *path_list;
+
+    path_list = fib_path_list_get(path_list_index);
+
+    return (path_list->fpl_urpf);
+}
+
 /*
  * fib_path_list_back_walk
  *
 /*
  * fib_path_list_back_walk
  *
@@ -410,6 +473,8 @@ fib_path_list_back_walk (fib_node_index_t path_list_index,
 
     path_list = fib_path_list_get(path_list_index);
 
 
     path_list = fib_path_list_get(path_list_index);
 
+    fib_path_list_mk_urpf(path_list);
+
     /*
      * propagate the backwalk further
      */
     /*
      * propagate the backwalk further
      */
@@ -461,6 +526,7 @@ fib_path_list_memory_show (void)
                          pool_elts(fib_path_list_pool),
                          pool_len(fib_path_list_pool),
                          sizeof(fib_path_list_t));
                          pool_elts(fib_path_list_pool),
                          pool_len(fib_path_list_pool),
                          sizeof(fib_path_list_t));
+    fib_urpf_list_show_mem();
 }
 
 /*
 }
 
 /*
@@ -483,6 +549,7 @@ fib_path_list_alloc (fib_node_index_t *path_list_index)
 
     fib_node_init(&path_list->fpl_node,
                  FIB_NODE_TYPE_PATH_LIST);
 
     fib_node_init(&path_list->fpl_node,
                  FIB_NODE_TYPE_PATH_LIST);
+    path_list->fpl_urpf = INDEX_INVALID;
 
     if (NULL != path_list_index)
     {
 
     if (NULL != path_list_index)
     {
@@ -519,6 +586,7 @@ fib_path_list_resolve (fib_path_list_t *path_list)
     path_list = fib_path_list_get(path_list_index);
 
     FIB_PATH_LIST_DBG(path_list, "resovled");
     path_list = fib_path_list_get(path_list_index);
 
     FIB_PATH_LIST_DBG(path_list, "resovled");
+    fib_path_list_mk_urpf(path_list);
 
     return (path_list);
 }
 
     return (path_list);
 }
index 42e07ab..852f07d 100644 (file)
@@ -104,9 +104,12 @@ extern fib_node_index_t fib_path_list_copy_and_path_remove(
     fib_node_index_t pl_index,
     fib_path_list_flags_t flags,
     const fib_route_path_t *path);
     fib_node_index_t pl_index,
     fib_path_list_flags_t flags,
     const fib_route_path_t *path);
-extern void fib_path_list_contribute_forwarding (fib_node_index_t path_list_index,
-                                                fib_forward_chain_type_t type,
-                                                dpo_id_t *dpo);
+extern void fib_path_list_contribute_forwarding(fib_node_index_t path_list_index,
+                                               fib_forward_chain_type_t type,
+                                               dpo_id_t *dpo);
+extern void fib_path_list_contribute_urpf(fib_node_index_t path_index,
+                                         index_t urpf);
+extern index_t fib_path_list_get_urpf(fib_node_index_t path_list_index);
 extern index_t fib_path_list_get_adj(fib_node_index_t path_list_index,
                                     fib_forward_chain_type_t type);
 
 extern index_t fib_path_list_get_adj(fib_node_index_t path_list_index,
                                     fib_forward_chain_type_t type);
 
index 898005e..3dc9c1d 100644 (file)
@@ -29,6 +29,7 @@
 #include <vnet/fib/fib_path_list.h>
 #include <vnet/fib/fib_walk.h>
 #include <vnet/fib/fib_node_list.h>
 #include <vnet/fib/fib_path_list.h>
 #include <vnet/fib/fib_walk.h>
 #include <vnet/fib/fib_node_list.h>
+#include <vnet/fib/fib_urpf_list.h>
 
 #define FIB_TEST_I(_cond, _comment, _args...)                  \
 ({                                                             \
 
 #define FIB_TEST_I(_cond, _comment, _args...)                  \
 ({                                                             \
@@ -139,14 +140,15 @@ fib_test_mk_intf (u32 ninterfaces)
     }
 }
 
     }
 }
 
-#define FIB_TEST_REC_FORW(_rec_prefix, _via_prefix)                     \
+#define FIB_TEST_REC_FORW(_rec_prefix, _via_prefix, _bucket)           \
 {                                                                       \
     const dpo_id_t *_rec_dpo = fib_entry_contribute_ip_forwarding(      \
         fib_table_lookup_exact_match(fib_index, (_rec_prefix)));        \
     const dpo_id_t *_via_dpo = fib_entry_contribute_ip_forwarding(      \
         fib_table_lookup(fib_index, (_via_prefix)));                    \
     FIB_TEST(!dpo_cmp(_via_dpo,                                         \
 {                                                                       \
     const dpo_id_t *_rec_dpo = fib_entry_contribute_ip_forwarding(      \
         fib_table_lookup_exact_match(fib_index, (_rec_prefix)));        \
     const dpo_id_t *_via_dpo = fib_entry_contribute_ip_forwarding(      \
         fib_table_lookup(fib_index, (_via_prefix)));                    \
     FIB_TEST(!dpo_cmp(_via_dpo,                                         \
-                      load_balance_get_bucket(_rec_dpo->dpoi_index, 0)), \
+                      load_balance_get_bucket(_rec_dpo->dpoi_index,    \
+                                             _bucket)),                \
              "%U is recursive via %U",                                  \
              format_fib_prefix, (_rec_prefix),                          \
              format_fib_prefix, _via_prefix);                           \
              "%U is recursive via %U",                                  \
              format_fib_prefix, (_rec_prefix),                          \
              format_fib_prefix, _via_prefix);                           \
@@ -167,6 +169,57 @@ fib_test_mk_intf (u32 ninterfaces)
              format_dpo_id, _dpo1, 0);                                  \
 }
 
              format_dpo_id, _dpo1, 0);                                  \
 }
 
+#define FIB_TEST_RPF(_cond, _comment, _args...)                        \
+{                                                              \
+    if (!FIB_TEST_I(_cond, _comment, ##_args)) {               \
+       return (0);                                             \
+    }                                                          \
+}
+
+static int
+fib_test_urpf_is_equal (fib_node_index_t fei,
+                      fib_forward_chain_type_t fct,
+                      u32 num, ...)
+{
+    dpo_id_t dpo = DPO_NULL;
+    fib_urpf_list_t *urpf;
+    index_t ui;
+    va_list ap;
+    int ii;
+
+    va_start(ap, num);
+
+    fib_entry_contribute_forwarding(fei, fct, &dpo);
+    ui = load_balance_get_urpf(dpo.dpoi_index);
+
+    urpf = fib_urpf_list_get(ui);
+
+    FIB_TEST_RPF(num == vec_len(urpf->furpf_itfs),
+                "RPF:%U len %d == %d",
+                format_fib_urpf_list, ui,
+                num, vec_len(urpf->furpf_itfs));
+    FIB_TEST_RPF(num == fib_urpf_check_size(ui),
+                "RPF:%U check-size %d == %d",
+                format_fib_urpf_list, ui,
+                num, vec_len(urpf->furpf_itfs));
+
+    for (ii = 0; ii < num; ii++)
+    {
+       adj_index_t ai = va_arg(ap, adj_index_t);
+
+       FIB_TEST_RPF(ai == urpf->furpf_itfs[ii],
+                    "RPF:%d item:%d - %d == %d",
+                    ui, ii, ai, urpf->furpf_itfs[ii]);
+       FIB_TEST_RPF(fib_urpf_check(ui, ai),
+                    "RPF:%d %d found",
+                    ui, ai);
+    }
+
+    dpo_reset(&dpo);
+
+    return (1);
+}
+
 static void
 fib_test_v4 (void)
 {
 static void
 fib_test_v4 (void)
 {
@@ -329,6 +382,8 @@ fib_test_v4 (void)
     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local interface route present");
 
     dpo = fib_entry_contribute_ip_forwarding(fei);
     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local interface route present");
 
     dpo = fib_entry_contribute_ip_forwarding(fei);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
+            "RPF list for local length 0");
     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
     FIB_TEST((DPO_RECEIVE == dpo->dpoi_type),
             "local interface adj is local");
     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
     FIB_TEST((DPO_RECEIVE == dpo->dpoi_type),
             "local interface adj is local");
@@ -466,6 +521,10 @@ fib_test_v4 (void)
     u8 eth_addr[] = {
        0xde, 0xde, 0xde, 0xba, 0xba, 0xba,
     };
     u8 eth_addr[] = {
        0xde, 0xde, 0xde, 0xba, 0xba, 0xba,
     };
+    ip46_address_t nh_12_12_12_12 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0c0c0c0c),
+    };
+    adj_index_t ai_12_12_12_12;
 
     /*
      * Add a route via an incomplete ADJ. then complete the ADJ
 
     /*
      * Add a route via an incomplete ADJ. then complete the ADJ
@@ -513,6 +572,24 @@ fib_test_v4 (void)
     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type,
              "11.11.11.11/32 via complete adj");
     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type,
              "11.11.11.11/32 via complete adj");
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
+                                   tm->hw[0]->sw_if_index),
+            "RPF list for adj-fib contains adj");
+
+    ai_12_12_12_12 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                        FIB_LINK_IP4,
+                                        &nh_12_12_12_12,
+                                        tm->hw[1]->sw_if_index);
+    FIB_TEST((FIB_NODE_INDEX_INVALID != ai_12_12_12_12), "adj created");
+    adj = adj_get(ai_12_12_12_12);
+    FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
+            "adj is incomplete");
+    FIB_TEST((0 == ip46_address_cmp(&nh_12_12_12_12,
+                                   &adj->sub_type.nbr.next_hop)),
+             "adj nbr next-hop ok");
+    adj_nbr_update_rewrite(ai_12_12_12_12, eth_addr);
+    FIB_TEST((IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index),
+            "adj is complete");
 
     /*
      * add the adj fib
 
     /*
      * add the adj fib
@@ -591,7 +668,7 @@ fib_test_v4 (void)
             fib_entry_pool_size());
 
     /*
             fib_entry_pool_size());
 
     /*
-     * Add 2 routes via the first ADJ. ensure path-list sharing
+     * Add 2 routes via the first ADJ. ensure path-list sharing
      */
     fib_prefix_t pfx_1_1_1_1_s_32 = {
        .fp_len = 32,
      */
     fib_prefix_t pfx_1_1_1_1_s_32 = {
        .fp_len = 32,
@@ -675,6 +752,9 @@ fib_test_v4 (void)
                             FIB_ROUTE_PATH_FLAG_NONE);
     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
     dpo = fib_entry_contribute_ip_forwarding(fei);
                             FIB_ROUTE_PATH_FLAG_NONE);
     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
     dpo = fib_entry_contribute_ip_forwarding(fei);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                   1, tm->hw[0]->sw_if_index),
+            "RPF list for 1.1.2.0/24 contains both adjs");
 
     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type, "type is %d", dpo1->dpoi_type);
 
     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type, "type is %d", dpo1->dpoi_type);
@@ -709,6 +789,11 @@ fib_test_v4 (void)
                                1,
                                FIB_ROUTE_PATH_FLAG_NONE);
     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
                                1,
                                FIB_ROUTE_PATH_FLAG_NONE);
     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
+    dpo = fib_entry_contribute_ip_forwarding(fei);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                  1, tm->hw[0]->sw_if_index),
+            "RPF list for 1.1.2.0/24 contains one adj");
+
     ai = fib_entry_get_adj(fei);
     FIB_TEST((ai_01 == ai), "1.1.2.0/24 resolves via 10.10.10.1");
 
     ai = fib_entry_get_adj(fei);
     FIB_TEST((ai_01 == ai), "1.1.2.0/24 resolves via 10.10.10.1");
 
@@ -740,19 +825,22 @@ fib_test_v4 (void)
        .ip4.as_u32 = clib_host_to_net_u32(0x01010101),
     };
 
        .ip4.as_u32 = clib_host_to_net_u32(0x01010101),
     };
 
-    fib_table_entry_path_add(fib_index,
-                            &bgp_100_pfx,
-                            FIB_SOURCE_API,
-                            FIB_ENTRY_FLAG_NONE,
-                            FIB_PROTOCOL_IP4,
-                            &nh_1_1_1_1,
-                            ~0, // no index provided.
-                            fib_index, // nexthop in same fib as route
-                            1,
-                            MPLS_LABEL_INVALID,
-                            FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_entry_path_add(fib_index,
+                                  &bgp_100_pfx,
+                                  FIB_SOURCE_API,
+                                  FIB_ENTRY_FLAG_NONE,
+                                  FIB_PROTOCOL_IP4,
+                                  &nh_1_1_1_1,
+                                  ~0, // no index provided.
+                                  fib_index, // nexthop in same fib as route
+                                  1,
+                                  MPLS_LABEL_INVALID,
+                                  FIB_ROUTE_PATH_FLAG_NONE);
 
 
-    FIB_TEST_REC_FORW(&bgp_100_pfx, &pfx_1_1_1_1_s_32);
+    FIB_TEST_REC_FORW(&bgp_100_pfx, &pfx_1_1_1_1_s_32, 0);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
+                                   tm->hw[0]->sw_if_index),
+            "RPF list for adj-fib contains adj");
 
     /*
      * +1 entry and +1 shared-path-list
 
     /*
      * +1 entry and +1 shared-path-list
@@ -785,7 +873,10 @@ fib_test_v4 (void)
                             MPLS_LABEL_INVALID,
                             FIB_ROUTE_PATH_FLAG_NONE);
 
                             MPLS_LABEL_INVALID,
                             FIB_ROUTE_PATH_FLAG_NONE);
 
-    FIB_TEST_REC_FORW(&bgp_101_pfx, &pfx_1_1_1_1_s_32);
+    FIB_TEST_REC_FORW(&bgp_101_pfx, &pfx_1_1_1_1_s_32, 0);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
+                                   tm->hw[0]->sw_if_index),
+            "RPF list for adj-fib contains adj");
 
     /*
      * +1 entry, but the recursive path-list is shared.
 
     /*
      * +1 entry, but the recursive path-list is shared.
@@ -889,7 +980,7 @@ fib_test_v4 (void)
                             MPLS_LABEL_INVALID,
                             FIB_ROUTE_PATH_FLAG_NONE);
 
                             MPLS_LABEL_INVALID,
                             FIB_ROUTE_PATH_FLAG_NONE);
 
-    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32);
+    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
 
     /*
      * the adj should be recursive via drop, since the route resolves via
 
     /*
      * the adj should be recursive via drop, since the route resolves via
@@ -898,6 +989,8 @@ fib_test_v4 (void)
     fei = fib_table_lookup(fib_index, &pfx_1_1_1_2_s_32);
     dpo1 = fib_entry_contribute_ip_forwarding(fei);
     FIB_TEST(load_balance_is_drop(dpo1), "1.1.1.2/32 is drop");
     fei = fib_table_lookup(fib_index, &pfx_1_1_1_2_s_32);
     dpo1 = fib_entry_contribute_ip_forwarding(fei);
     FIB_TEST(load_balance_is_drop(dpo1), "1.1.1.2/32 is drop");
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
+            "RPF list for 1.1.1.2/32 contains 0 adjs");
 
     /*
      * +2 entry and +1 shared-path-list
 
     /*
      * +2 entry and +1 shared-path-list
@@ -911,6 +1004,8 @@ fib_test_v4 (void)
 
     /*
      * Unequal Cost load-balance. 3:1 ratio. fits in a 4 bucket LB
 
     /*
      * Unequal Cost load-balance. 3:1 ratio. fits in a 4 bucket LB
+     * The paths are sort by NH first. in this case the the path with greater
+     * weight is first in the set. This ordering is to test the RPF sort|uniq logic
      */
     fib_prefix_t pfx_1_2_3_4_s_32 = {
        .fp_len = 32,
      */
     fib_prefix_t pfx_1_2_3_4_s_32 = {
        .fp_len = 32,
@@ -924,7 +1019,7 @@ fib_test_v4 (void)
                             FIB_SOURCE_API,
                             FIB_ENTRY_FLAG_NONE,
                             FIB_PROTOCOL_IP4,
                             FIB_SOURCE_API,
                             FIB_ENTRY_FLAG_NONE,
                             FIB_PROTOCOL_IP4,
-                             &nh_10_10_10_2,
+                             &nh_10_10_10_1,
                              tm->hw[0]->sw_if_index,
                              ~0,
                              1,
                              tm->hw[0]->sw_if_index,
                              ~0,
                              1,
@@ -935,8 +1030,8 @@ fib_test_v4 (void)
                                    FIB_SOURCE_API,
                                    FIB_ENTRY_FLAG_NONE,
                                   FIB_PROTOCOL_IP4,
                                    FIB_SOURCE_API,
                                    FIB_ENTRY_FLAG_NONE,
                                   FIB_PROTOCOL_IP4,
-                                   &nh_10_10_10_1,
-                                   tm->hw[0]->sw_if_index,
+                                   &nh_12_12_12_12,
+                                   tm->hw[1]->sw_if_index,
                                    ~0,
                                    3,
                                    MPLS_LABEL_INVALID,
                                    ~0,
                                    3,
                                    MPLS_LABEL_INVALID,
@@ -949,14 +1044,16 @@ fib_test_v4 (void)
              "1.2.3.4/32 LB has %d bucket",
              lb->lb_n_buckets);
 
              "1.2.3.4/32 LB has %d bucket",
              lb->lb_n_buckets);
 
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 0, ai_01);
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 1, ai_01);
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 2, ai_01);
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 3, ai_02);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 0, ai_12_12_12_12);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 1, ai_12_12_12_12);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 2, ai_12_12_12_12);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 3, ai_01);
+
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
+                                   tm->hw[0]->sw_if_index,
+                                   tm->hw[1]->sw_if_index),
+            "RPF list for 1.2.3.4/32 contains both adjs");
 
 
-    fib_table_entry_delete(fib_index,
-                           &pfx_1_2_3_4_s_32,
-                           FIB_SOURCE_API);
 
     /*
      * Unequal Cost load-balance. 4:1 ratio.
 
     /*
      * Unequal Cost load-balance. 4:1 ratio.
@@ -974,8 +1071,8 @@ fib_test_v4 (void)
                             FIB_SOURCE_API,
                             FIB_ENTRY_FLAG_NONE,
                             FIB_PROTOCOL_IP4,
                             FIB_SOURCE_API,
                             FIB_ENTRY_FLAG_NONE,
                             FIB_PROTOCOL_IP4,
-                             &nh_10_10_10_2,
-                             tm->hw[0]->sw_if_index,
+                             &nh_12_12_12_12,
+                             tm->hw[1]->sw_if_index,
                              ~0,
                              1,
                              MPLS_LABEL_INVALID,
                              ~0,
                              1,
                              MPLS_LABEL_INVALID,
@@ -1012,13 +1109,85 @@ fib_test_v4 (void)
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 10, ai_01);
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 11, ai_01);
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 12, ai_01);
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 10, ai_01);
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 11, ai_01);
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 12, ai_01);
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 13, ai_02);
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 14, ai_02);
-    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 15, ai_02);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 13, ai_12_12_12_12);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 14, ai_12_12_12_12);
+    FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 15, ai_12_12_12_12);
 
 
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
+                                   tm->hw[0]->sw_if_index,
+                                   tm->hw[1]->sw_if_index),
+            "RPF list for 1.2.3.4/32 contains both adjs");
+
+    /*
+     * A recursive via the two unequal cost entries
+     */
+    fib_prefix_t bgp_44_s_32 = {
+       .fp_len = 32,
+       .fp_proto = FIB_PROTOCOL_IP4,
+       .fp_addr = {
+           /* 200.200.200.201/32 */
+           .ip4.as_u32 = clib_host_to_net_u32(0x44444444),
+       },
+    };
+    fei = fib_table_entry_path_add(fib_index,
+                                   &bgp_44_s_32,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_NONE,
+                                  FIB_PROTOCOL_IP4,
+                                  &pfx_1_2_3_4_s_32.fp_addr,
+                                   ~0,
+                                   fib_index,
+                                   1,
+                                   MPLS_LABEL_INVALID,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_entry_path_add(fib_index,
+                                   &bgp_44_s_32,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_NONE,
+                                  FIB_PROTOCOL_IP4,
+                                  &pfx_1_2_3_5_s_32.fp_addr,
+                                   ~0,
+                                   fib_index,
+                                   1,
+                                   MPLS_LABEL_INVALID,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    FIB_TEST_REC_FORW(&bgp_44_s_32, &pfx_1_2_3_4_s_32, 0);
+    FIB_TEST_REC_FORW(&bgp_44_s_32, &pfx_1_2_3_5_s_32, 1);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
+                                   tm->hw[0]->sw_if_index,
+                                   tm->hw[1]->sw_if_index),
+            "RPF list for 1.2.3.4/32 contains both adjs");
+
+    /*
+     * test the uRPF check functions
+     */
+    dpo_id_t dpo_44 = DPO_NULL;
+    index_t urpfi;
+
+    fib_entry_contribute_forwarding(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, &dpo_44);
+    urpfi = load_balance_get_urpf(dpo_44.dpoi_index);
+
+    FIB_TEST(fib_urpf_check(urpfi, tm->hw[0]->sw_if_index),
+            "uRPF check for 68.68.68.68/32 on %d OK",
+            tm->hw[0]->sw_if_index);
+    FIB_TEST(fib_urpf_check(urpfi, tm->hw[1]->sw_if_index),
+            "uRPF check for 68.68.68.68/32 on %d OK",
+            tm->hw[1]->sw_if_index);
+    FIB_TEST(!fib_urpf_check(urpfi, 99),
+            "uRPF check for 68.68.68.68/32 on 99 not-OK",
+            99);
+    dpo_reset(&dpo_44);
+
+    fib_table_entry_delete(fib_index,
+                           &bgp_44_s_32,
+                           FIB_SOURCE_API);
     fib_table_entry_delete(fib_index,
                            &pfx_1_2_3_5_s_32,
                            FIB_SOURCE_API);
     fib_table_entry_delete(fib_index,
                            &pfx_1_2_3_5_s_32,
                            FIB_SOURCE_API);
+    fib_table_entry_delete(fib_index,
+                           &pfx_1_2_3_4_s_32,
+                           FIB_SOURCE_API);
 
     /*
      * Add a recursive route:
 
     /*
      * Add a recursive route:
@@ -1053,11 +1222,13 @@ fib_test_v4 (void)
                             MPLS_LABEL_INVALID,
                             FIB_ROUTE_PATH_FLAG_NONE);
 
                             MPLS_LABEL_INVALID,
                             FIB_ROUTE_PATH_FLAG_NONE);
 
-    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32);
+    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
 
     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_200_s_32);
     FIB_TEST((FIB_ENTRY_FLAG_NONE == fib_entry_get_flags(fei)),
             "Flags set on RR via non-attached");
 
     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_200_s_32);
     FIB_TEST((FIB_ENTRY_FLAG_NONE == fib_entry_get_flags(fei)),
             "Flags set on RR via non-attached");
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
+            "RPF list for BGP route empty");
 
     /*
      * +2 entry (BGP & RR) and +1 shared-path-list
 
     /*
      * +2 entry (BGP & RR) and +1 shared-path-list
@@ -1119,8 +1290,12 @@ fib_test_v4 (void)
     /*
      * the recursive adj for 200.200.200.200 should be updated.
      */
     /*
      * the recursive adj for 200.200.200.200 should be updated.
      */
-    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32);
-    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32);
+    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
+    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
+    fei = fib_table_lookup(fib_index, &bgp_200_pfx);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
+                                   tm->hw[0]->sw_if_index),
+            "RPF list for BGP route has itf index 0");
 
     /*
      * insert a more specific route than 1.1.1.0/24 that also covers the
 
     /*
      * insert a more specific route than 1.1.1.0/24 that also covers the
@@ -1166,8 +1341,8 @@ fib_test_v4 (void)
      * the recursive adj for 200.200.200.200 should be updated.
      * 200.200.200.201 remains unchanged.
      */
      * the recursive adj for 200.200.200.200 should be updated.
      * 200.200.200.201 remains unchanged.
      */
-    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32);
-    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32);
+    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
+    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
 
     /*
      * remove this /28. 200.200.200.200/32 should revert back to via 1.1.1.0/24
 
     /*
      * remove this /28. 200.200.200.200/32 should revert back to via 1.1.1.0/24
@@ -1187,8 +1362,8 @@ fib_test_v4 (void)
     FIB_TEST((fib_table_lookup(fib_index, &pfx_1_1_1_0_s_28) == 
              fib_table_lookup(fib_index, &pfx_1_1_1_0_s_24)),
             "1.1.1.0/28 lookup via /24");
     FIB_TEST((fib_table_lookup(fib_index, &pfx_1_1_1_0_s_28) == 
              fib_table_lookup(fib_index, &pfx_1_1_1_0_s_24)),
             "1.1.1.0/28 lookup via /24");
-    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32);
-    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32);
+    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
+    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
 
     /*
      * -1 entry. -1 shared path-list
 
     /*
      * -1 entry. -1 shared path-list
@@ -1223,8 +1398,8 @@ fib_test_v4 (void)
     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
             "1.1.1.200/32 route is DROP");
 
     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
             "1.1.1.200/32 route is DROP");
 
-    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32);
-    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32);
+    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
+    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
 
     /*
      * -1 entry
 
     /*
      * -1 entry
@@ -1254,8 +1429,8 @@ fib_test_v4 (void)
     ai = fib_entry_get_adj(fei);
     FIB_TEST((ai = ai_01), "1.1.1.2/32 resolves via 10.10.10.1");
 
     ai = fib_entry_get_adj(fei);
     FIB_TEST((ai = ai_01), "1.1.1.2/32 resolves via 10.10.10.1");
 
-    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32);
-    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32);
+    FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
+    FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
 
     /*
      * no change. 1.1.1.2/32 was already there RR sourced.
 
     /*
      * no change. 1.1.1.2/32 was already there RR sourced.
@@ -2530,6 +2705,28 @@ fib_test_v4 (void)
             "2001::/64 ADJ-adj is NH proto v4");
     fib_table_entry_delete(0, &pfx_2001_s_64, FIB_SOURCE_API);
 
             "2001::/64 ADJ-adj is NH proto v4");
     fib_table_entry_delete(0, &pfx_2001_s_64, FIB_SOURCE_API);
 
+    /*
+     * add a uRPF exempt prefix:
+     *  test:
+     *   - it's forwarding is drop
+     *   - it's uRPF list is not empty
+     *   - the uRPF list for the default route (it's cover) is empty
+     */
+    fei = fib_table_entry_special_add(fib_index,
+                                     &pfx_4_1_1_1_s_32,
+                                     FIB_SOURCE_URPF_EXEMPT,
+                                     FIB_ENTRY_FLAG_DROP,
+                                     ADJ_INDEX_INVALID);
+    dpo = fib_entry_contribute_ip_forwarding(fei);
+    FIB_TEST(load_balance_is_drop(dpo),
+            "uRPF exempt 4.1.1.1/32 DROP");
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1, 0),
+            "uRPF list for exempt prefix has itf index 0");
+    fei = fib_table_lookup_exact_match(fib_index, &pfx_0_0_0_0_s_0);
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
+            "uRPF list for 0.0.0.0/0 empty");
+
+    fib_table_entry_delete(fib_index, &pfx_4_1_1_1_s_32, FIB_SOURCE_URPF_EXEMPT);
 
     /*
      * CLEANUP
 
     /*
      * CLEANUP
@@ -2559,11 +2756,12 @@ fib_test_v4 (void)
             fib_entry_pool_size());
 
     /*
             fib_entry_pool_size());
 
     /*
-     * unlock the adjacencies for which this test provided a rewrite.
+     * unlock the adjacencies for which this test provided a rewrite.
      * These are the last locks on these adjs. they should thus go away.
      */
     adj_unlock(ai_02);
     adj_unlock(ai_01);
      * These are the last locks on these adjs. they should thus go away.
      */
     adj_unlock(ai_02);
     adj_unlock(ai_01);
+    adj_unlock(ai_12_12_12_12);
 
     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
             adj_nbr_db_size());
 
     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
             adj_nbr_db_size());
@@ -2623,6 +2821,8 @@ fib_test_v4 (void)
             fib_path_list_pool_size());
     FIB_TEST((NBR-5 == fib_entry_pool_size()), "entry pool size is %d",
             fib_entry_pool_size());
             fib_path_list_pool_size());
     FIB_TEST((NBR-5 == fib_entry_pool_size()), "entry pool size is %d",
             fib_entry_pool_size());
+    FIB_TEST((NBR-5 == pool_elts(fib_urpf_list_pool)), "uRPF pool size is %d",
+            pool_elts(fib_urpf_list_pool));
 
     return;
 }
 
     return;
 }
@@ -5014,6 +5214,28 @@ fib_test_label (void)
             "2.2.2.2.2/32 LB 1 buckets via: "
             "label 1600 over 1.1.1.1");
 
             "2.2.2.2.2/32 LB 1 buckets via: "
             "label 1600 over 1.1.1.1");
 
+    dpo_id_t dpo_44 = DPO_NULL;
+    index_t urpfi;
+
+    fib_entry_contribute_forwarding(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, &dpo_44);
+    urpfi = load_balance_get_urpf(dpo_44.dpoi_index);
+
+    FIB_TEST(fib_urpf_check(urpfi, tm->hw[0]->sw_if_index),
+            "uRPF check for 2.2.2.2/32 on %d OK",
+            tm->hw[0]->sw_if_index);
+    FIB_TEST(fib_urpf_check(urpfi, tm->hw[1]->sw_if_index),
+            "uRPF check for 2.2.2.2/32 on %d OK",
+            tm->hw[1]->sw_if_index);
+    FIB_TEST(!fib_urpf_check(urpfi, 99),
+            "uRPF check for 2.2.2.2/32 on 99 not-OK",
+            99);
+
+    fib_entry_contribute_forwarding(fei, FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, &dpo_44);
+    FIB_TEST(urpfi == load_balance_get_urpf(dpo_44.dpoi_index),
+            "Shared uRPF on IP and non-EOS chain");
+
+    dpo_reset(&dpo_44);
+
     /*
      * we are holding a lock on the non-eos LB of the via-entry.
      * do a PIC-core failover by shutting the link of the via-entry.
     /*
      * we are holding a lock on the non-eos LB of the via-entry.
      * do a PIC-core failover by shutting the link of the via-entry.
index 232a230..250aad7 100644 (file)
@@ -117,10 +117,6 @@ fib_link_t fib_proto_to_link (fib_protocol_t proto);
  * sceanrios
  */
 typedef enum fib_forward_chain_type_t_ {
  * sceanrios
  */
 typedef enum fib_forward_chain_type_t_ {
-    /**
-     * Contribute an object that is to be used to forward Ethernet packets
-     */
-    FIB_FORW_CHAIN_TYPE_ETHERNET,
     /**
      * Contribute an object that is to be used to forward IP4 packets
      */
     /**
      * Contribute an object that is to be used to forward IP4 packets
      */
@@ -141,17 +137,24 @@ typedef enum fib_forward_chain_type_t_ {
      * option is converted into one of the other three internally.
      */
     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
      * option is converted into one of the other three internally.
      */
     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
+    /**
+     * Contribute an object that is to be used to forward Ethernet packets.
+     * This is last in the list since it is not valid for many FIB objects,
+     * and thus their array of per-chain-type DPOs can be sized smaller.
+     */
+    FIB_FORW_CHAIN_TYPE_ETHERNET,
 }  __attribute__ ((packed)) fib_forward_chain_type_t;
 
 #define FIB_FORW_CHAINS {                                      \
 }  __attribute__ ((packed)) fib_forward_chain_type_t;
 
 #define FIB_FORW_CHAINS {                                      \
-    [FIB_FORW_CHAIN_TYPE_ETHERNET]      = "ehternet",          \
+    [FIB_FORW_CHAIN_TYPE_ETHERNET]      = "ethernet",          \
     [FIB_FORW_CHAIN_TYPE_UNICAST_IP4]   = "unicast-ip4",       \
     [FIB_FORW_CHAIN_TYPE_UNICAST_IP6]   = "unicast-ip6",       \
     [FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS]  = "mpls-neos",         \
     [FIB_FORW_CHAIN_TYPE_MPLS_EOS]      = "mpls-eos",          \
 }
 
     [FIB_FORW_CHAIN_TYPE_UNICAST_IP4]   = "unicast-ip4",       \
     [FIB_FORW_CHAIN_TYPE_UNICAST_IP6]   = "unicast-ip6",       \
     [FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS]  = "mpls-neos",         \
     [FIB_FORW_CHAIN_TYPE_MPLS_EOS]      = "mpls-eos",          \
 }
 
-#define FIB_FORW_CHAIN_NUM (FIB_FORW_CHAIN_TYPE_MPLS_EOS+1)
+#define FIB_FORW_CHAIN_NUM (FIB_FORW_CHAIN_TYPE_MPLS_ETHERNET+1)
+#define FIB_FORW_CHAIN_MPLS_NUM (FIB_FORW_CHAIN_TYPE_MPLS_EOS+1)
 
 #define FOR_EACH_FIB_FORW_CHAIN(_item)                   \
     for (_item = FIB_FORW_CHAIN_TYPE_ETHERNET;           \
 
 #define FOR_EACH_FIB_FORW_CHAIN(_item)                   \
     for (_item = FIB_FORW_CHAIN_TYPE_ETHERNET;           \
diff --git a/vnet/vnet/fib/fib_urpf_list.c b/vnet/vnet/fib/fib_urpf_list.c
new file mode 100644 (file)
index 0000000..263812a
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/fib/fib_urpf_list.h>
+#include <vnet/adj/adj.h>
+
+/**
+ * @brief pool of all fib_urpf_list
+ */
+fib_urpf_list_t *fib_urpf_list_pool;
+
+u8 *
+format_fib_urpf_list (u8 *s, va_list args)
+{
+    fib_urpf_list_t *urpf;
+    index_t ui;
+    u32 *swi;
+
+    ui = va_arg(args, index_t);
+    urpf = fib_urpf_list_get(ui);
+
+    s = format(s, "uPRF-list:%d len:%d itfs:[",
+              ui, vec_len(urpf->furpf_itfs));
+
+    vec_foreach(swi, urpf->furpf_itfs)
+    {
+       s = format(s, "%d, ", *swi);
+    }
+    s = format(s, "]");
+
+    return (s);
+}
+
+index_t
+fib_urpf_list_alloc_and_lock (void)
+{
+    fib_urpf_list_t *urpf;
+
+    pool_get(fib_urpf_list_pool, urpf);
+    memset(urpf, 0, sizeof(*urpf));
+
+    urpf->furpf_locks++;
+
+    return (urpf - fib_urpf_list_pool);
+}
+
+void
+fib_urpf_list_unlock (index_t ui)
+{
+    fib_urpf_list_t *urpf;
+
+    if (INDEX_INVALID == ui)
+       return;
+
+    urpf = fib_urpf_list_get(ui);
+
+    urpf->furpf_locks--;
+
+    if (0 == urpf->furpf_locks)
+    {
+       vec_free(urpf->furpf_itfs);
+       pool_put(fib_urpf_list_pool, urpf);
+    }
+}
+
+void
+fib_urpf_list_lock (index_t ui)
+{
+    fib_urpf_list_t *urpf;
+
+    urpf = fib_urpf_list_get(ui);
+
+    urpf->furpf_locks++;
+}
+
+/**
+ * @brief Append another interface to the list.
+ */
+void
+fib_urpf_list_append (index_t ui,
+                     u32 sw_if_index)
+{
+    fib_urpf_list_t *urpf;
+
+    urpf = fib_urpf_list_get(ui);
+
+    vec_add1(urpf->furpf_itfs, sw_if_index);
+}
+
+/**
+ * @brief Combine to interface lists
+ */
+void
+fib_urpf_list_combine (index_t ui1,
+                      index_t ui2)
+{
+    fib_urpf_list_t *urpf1, *urpf2;
+
+    urpf1 = fib_urpf_list_get(ui1);
+    urpf2 = fib_urpf_list_get(ui2);
+
+    vec_append(urpf1->furpf_itfs, urpf2->furpf_itfs);
+}
+
+/**
+ * @brief Sort the interface indicies.
+ * The sort is the first step in obtaining a unique list, so the order,
+ * w.r.t. next-hop, interface,etc is not important. So a sort based on the
+ * index is all we need.
+ */
+static int
+fib_urpf_itf_cmp_for_sort (void * v1,
+                          void * v2)
+{
+    fib_node_index_t *i1 = v1, *i2 = v2;
+
+    return (*i2 < *i1);
+}
+
+/**
+ * @brief Convert the uRPF list from the itf set obtained during the walk
+ * to a unique list.
+ */
+void
+fib_urpf_list_bake (index_t ui)
+{
+    fib_urpf_list_t *urpf;
+
+    urpf = fib_urpf_list_get(ui);
+
+    ASSERT(!(urpf->furpf_flags & FIB_URPF_LIST_BAKED));
+
+    if (vec_len(urpf->furpf_itfs) > 1)
+    {
+       u32 i,j;
+
+       /*
+        * cat list | sort | uniq > rpf_list
+        */
+       vec_sort_with_function(urpf->furpf_itfs, fib_urpf_itf_cmp_for_sort);
+
+       i = 0, j = 1;
+       while (j < vec_len(urpf->furpf_itfs))
+       {
+           if (urpf->furpf_itfs[i] == urpf->furpf_itfs[j])
+           {
+               /*
+                * the itfacenct entries are the same.
+                * search forward for a unique one
+                */
+               while (urpf->furpf_itfs[i] == urpf->furpf_itfs[j] &&
+                      j < vec_len(urpf->furpf_itfs))
+               {
+                   j++;
+               }
+               if (j == vec_len(urpf->furpf_itfs))
+               {
+                   /*
+                    * ran off the end without finding a unique index.
+                    * we are done.
+                    */
+                   break;
+               }
+               else
+               {
+                   urpf->furpf_itfs[i+1] = urpf->furpf_itfs[j];
+               }
+           }
+           i++, j++;
+       }
+
+       /*
+        * set the length of the vector to the number of unique itfs
+        */
+       _vec_len(urpf->furpf_itfs) = i+1;
+    }
+
+    urpf->furpf_flags |= FIB_URPF_LIST_BAKED;
+}
+
+void
+fib_urpf_list_show_mem (void)
+{
+    fib_show_memory_usage("uRPF-list",
+                         pool_elts(fib_urpf_list_pool),
+                         pool_len(fib_urpf_list_pool),
+                         sizeof(fib_urpf_list_t));
+}
+
+static clib_error_t *
+show_fib_urpf_list_command (vlib_main_t * vm,
+                           unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+    index_t ui;
+
+    if (unformat (input, "%d", &ui))
+    {
+       /*
+        * show one in detail
+        */
+       if (!pool_is_free_index(fib_urpf_list_pool, ui))
+       {
+           vlib_cli_output (vm, "%d@%U",
+                            ui,
+                            format_fib_urpf_list, ui);
+       }
+       else
+       {
+           vlib_cli_output (vm, "uRPF %d invalid", ui);
+       }
+    }
+    else
+    {
+       /*
+        * show all
+        */
+       vlib_cli_output (vm, "FIB uRPF Entries:");
+       pool_foreach_index(ui, fib_urpf_list_pool,
+        ({
+           vlib_cli_output (vm, "%d@%U",
+                            ui,
+                            format_fib_urpf_list, ui);
+       }));
+    }
+
+    return (NULL);
+}
+
+/* *INDENT-OFF* */
+/*?
+ * The '<em>sh fib uRPF [index] </em>' command displays the uRPF lists
+ *
+ * @cliexpar
+ * @cliexstart{show fib uRPF}
+ * FIB uRPF Entries:
+ *  0@uPRF-list:0 len:0 itfs:[]
+ *  1@uPRF-list:1 len:2 itfs:[1, 2, ]
+ *  2@uPRF-list:2 len:1 itfs:[3, ]
+ *  3@uPRF-list:3 len:1 itfs:[9, ]
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (show_fib_urpf_list, static) = {
+  .path = "show fib uRPF",
+  .function = show_fib_urpf_list_command,
+  .short_help = "show fib uRPF",
+};
+/* *INDENT-OFF* */
diff --git a/vnet/vnet/fib/fib_urpf_list.h b/vnet/vnet/fib/fib_urpf_list.h
new file mode 100644 (file)
index 0000000..09f4757
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @brief A unicast RPF list.
+ * The uRPF list is the set of interfaces that a prefix can be reached through.
+ * There are 3 levels of RPF check:
+ *   - do we have any route to the source (i.e. it's not drop)
+ *   - did the packet arrive on an interface that the source is reachable through
+ *   - did the packet arrive from a peer that the source is reachable through
+ * we don't support the last. But it could be done by storing adjs in the uPRF
+ * list rather than interface indices.
+ *
+ * these conditions are checked against the list by:
+ *   - the list is not empty
+ *   - there is an interface in the list that is on the input interface.
+ *   - there is an adj in the list whose MAC address matches the packet's
+ *     source MAC and input interface.
+ *
+ * To speed the last two checks the interface list only needs to have the unique
+ * interfaces present. If the uRPF check was instead implemented by forward
+ * walking the DPO chain, then that walk would encounter a great deal of
+ * non-adjacency objects (i.e. load-balances, mpls-labels, etc) and potentially
+ * the same adjacency many times (esp. when UCMP is used).
+ * To that end the uRPF list is a collapsed, unique interface only list.
+ */
+
+#ifndef __FIB_URPF_LIST_H__
+#define __FIB_URPF_LIST_H__
+
+#include <vnet/fib/fib_types.h>
+#include <vnet/adj/adj.h>
+
+/**
+ * @brief flags
+ */
+typedef enum fib_urpf_list_flag_t_
+{
+    /**
+     * @brief Set to indicated that the uRPF list has already been baked.
+     * This is protection against it being baked more than once. These
+     * are not chunky fries - once is enough.
+     */
+    FIB_URPF_LIST_BAKED = (1 << 0),
+} fib_urpf_list_flag_t;
+
+typedef struct fib_urpf_list_t_
+{
+    /**
+     * The list of interfaces that comprise the allowed accepting interfaces
+     */
+    adj_index_t *furpf_itfs;
+
+    /**
+     * flags
+     */
+    fib_urpf_list_flag_t furpf_flags;
+
+    /**
+     * uRPF lists are shared amongst many entries so we require a locking
+     * mechanism.
+     */
+    u32 furpf_locks;
+} fib_urpf_list_t;
+
+extern index_t fib_urpf_list_alloc_and_lock(void);
+extern void fib_urpf_list_unlock(index_t urpf);
+extern void fib_urpf_list_lock(index_t urpf);
+
+extern void fib_urpf_list_append(index_t urpf, adj_index_t adj);
+extern void fib_urpf_list_combine(index_t urpf1, index_t urpf2);
+
+extern void fib_urpf_list_bake(index_t urpf);
+
+extern u8 *format_fib_urpf_list(u8 *s, va_list ap);
+
+extern void fib_urpf_list_show_mem(void);
+
+/**
+ * @brief pool of all fib_urpf_list
+ */
+extern fib_urpf_list_t *fib_urpf_list_pool;
+
+static inline fib_urpf_list_t *
+fib_urpf_list_get (index_t index)
+{
+    return (pool_elt_at_index(fib_urpf_list_pool, index));
+}
+
+/**
+ * @brief Data-Plane function to check an input interface against an uRPF list
+ *
+ * @param ui The uRPF list index to check against. Get this from the load-balance
+ *             object that is the result of the FIB lookup
+ * @param sw_if_index The SW interface index to validate
+ *
+ * @return 1 if the interface is found, 0 otherwise
+ */
+always_inline int
+fib_urpf_check (index_t ui, u32 sw_if_index)
+{
+    fib_urpf_list_t *urpf;
+    u32 *swi;
+
+    urpf = fib_urpf_list_get(ui);
+
+    vec_foreach(swi, urpf->furpf_itfs)
+    {
+       if (*swi == sw_if_index)
+           return (1);
+    }
+
+    return (0);
+}
+
+/**
+ * @brief Data-Plane function to check the size of an uRPF list, (i.e. the number
+ *        of interfaces in the list).
+ *
+ * @param ui The uRPF list index to check against. Get this from the load-balance
+ *             object that is the result of the FIB lookup
+ *
+ * @return the number of interfaces in the list
+ */
+always_inline int
+fib_urpf_check_size (index_t ui)
+{
+    fib_urpf_list_t *urpf;
+
+    urpf = fib_urpf_list_get(ui);
+
+    return (vec_len(urpf->furpf_itfs));
+}
+
+#endif
index 0028118..aa6fca0 100644 (file)
@@ -365,6 +365,11 @@ gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
   u32 ti;
 
   hi = vnet_get_hw_interface (vnm, hw_if_index);
   u32 ti;
 
   hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+  if (NULL == gm->tunnel_index_by_sw_if_index ||
+      hi->sw_if_index >= vec_len(gm->tunnel_index_by_sw_if_index))
+      return (NULL);
+
   ti = gm->tunnel_index_by_sw_if_index[hi->sw_if_index];
 
   if (~0 == ti)
   ti = gm->tunnel_index_by_sw_if_index[hi->sw_if_index];
 
   if (~0 == ti)
index b8d326f..72c9fb0 100644 (file)
@@ -46,6 +46,7 @@
 #include <vnet/api_errno.h>     /* for API error numbers */
 #include <vnet/fib/fib_table.h> /* for FIB table and entry creation */
 #include <vnet/fib/fib_entry.h> /* for FIB table and entry creation */
 #include <vnet/api_errno.h>     /* for API error numbers */
 #include <vnet/fib/fib_table.h> /* for FIB table and entry creation */
 #include <vnet/fib/fib_entry.h> /* for FIB table and entry creation */
+#include <vnet/fib/fib_urpf_list.h> /* for FIB uRPF check */
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/classify_dpo.h>
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/classify_dpo.h>
@@ -1501,31 +1502,31 @@ ip4_local (vlib_main_t * vm,
          /* 
            * Must have a route to source otherwise we drop the packet.
            * ip4 broadcasts are accepted, e.g. to make dhcp client work
          /* 
            * Must have a route to source otherwise we drop the packet.
            * ip4 broadcasts are accepted, e.g. to make dhcp client work
+          *
+          * The checks are:
+          *  - the source is a recieve => it's from us => bogus, do this
+          *    first since it sets a different error code.
+          *  - uRPF check for any route to source - accept if passes.
+          *  - allow packets destined to the broadcast address from unknown sources
            */
            */
-         error0 = (error0 == IP4_ERROR_UNKNOWN_PROTOCOL
-                   && dpo0->dpoi_type != DPO_ADJACENCY
-                   && dpo0->dpoi_type != DPO_ADJACENCY_INCOMPLETE
-                   && dpo0->dpoi_type != DPO_RECEIVE
-                   && dpo0->dpoi_type != DPO_DROP
-                   && dpo0->dpoi_type != DPO_ADJACENCY_GLEAN
-                   && ip0->dst_address.as_u32 != 0xFFFFFFFF
-                   ? IP4_ERROR_SRC_LOOKUP_MISS
-                   : error0);
-          error0 = (dpo0->dpoi_type == DPO_RECEIVE ?
+          error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    dpo0->dpoi_type == DPO_RECEIVE) ?
                     IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
                     error0);
                     IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
                     error0);
-         error1 = (error1 == IP4_ERROR_UNKNOWN_PROTOCOL
-                   && dpo1->dpoi_type != DPO_ADJACENCY
-                   && dpo1->dpoi_type != DPO_ADJACENCY_INCOMPLETE
-                   && dpo1->dpoi_type != DPO_RECEIVE
-                   && dpo1->dpoi_type != DPO_DROP
-                   && dpo1->dpoi_type != DPO_ADJACENCY_GLEAN
-                   && ip1->dst_address.as_u32 != 0xFFFFFFFF
+         error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    !fib_urpf_check_size(lb0->lb_urpf) &&
+                    ip0->dst_address.as_u32 != 0xFFFFFFFF)
                    ? IP4_ERROR_SRC_LOOKUP_MISS
                    ? IP4_ERROR_SRC_LOOKUP_MISS
-                   : error1);
-          error1 = (dpo0->dpoi_type == DPO_RECEIVE ?
+                   : error0);
+          error1 = ((error1 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    dpo1->dpoi_type == DPO_RECEIVE) ?
                     IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
                     error1);
                     IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
                     error1);
+         error1 = ((error1 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    !fib_urpf_check_size(lb1->lb_urpf) &&
+                    ip1->dst_address.as_u32 != 0xFFFFFFFF)
+                   ? IP4_ERROR_SRC_LOOKUP_MISS
+                   : error1);
 
          next0 = lm->local_next_by_ip_protocol[proto0];
          next1 = lm->local_next_by_ip_protocol[proto1];
 
          next0 = lm->local_next_by_ip_protocol[proto0];
          next1 = lm->local_next_by_ip_protocol[proto1];
@@ -1670,20 +1671,15 @@ ip4_local (vlib_main_t * vm,
              vnet_buffer (p0)->ip.adj_index[VLIB_RX] =
                  dpo0->dpoi_index;
 
              vnet_buffer (p0)->ip.adj_index[VLIB_RX] =
                  dpo0->dpoi_index;
 
-         /* Must have a route to source otherwise we drop the packet. */
-         error0 = (error0 == IP4_ERROR_UNKNOWN_PROTOCOL
-                   && dpo0->dpoi_type != DPO_ADJACENCY
-                   && dpo0->dpoi_type != DPO_ADJACENCY_INCOMPLETE
-                   && dpo0->dpoi_type != DPO_RECEIVE
-                   && dpo0->dpoi_type != DPO_DROP
-                   && dpo0->dpoi_type != DPO_ADJACENCY_GLEAN
-                   && ip0->dst_address.as_u32 != 0xFFFFFFFF
-                   ? IP4_ERROR_SRC_LOOKUP_MISS
-                   : error0);
-          /* Packet originated from a local address => spoofing */
-          error0 = (dpo0->dpoi_type == DPO_RECEIVE ?
+          error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    dpo0->dpoi_type == DPO_RECEIVE) ?
                     IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
                     error0);
                     IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
                     error0);
+         error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    !fib_urpf_check_size(lb0->lb_urpf) &&
+                    ip0->dst_address.as_u32 != 0xFFFFFFFF)
+                   ? IP4_ERROR_SRC_LOOKUP_MISS
+                   : error0);
 
          next0 = lm->local_next_by_ip_protocol[proto0];
 
 
          next0 = lm->local_next_by_ip_protocol[proto0];
 
index 2323ac2..97d4703 100644 (file)
 
 #include <vnet/ip/ip.h>
 #include <vnet/fib/ip4_fib.h>
 
 #include <vnet/ip/ip.h>
 #include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_urpf_list.h>
 #include <vnet/dpo/load_balance.h>
 
 typedef struct {
   u8 packet_data[64];
 #include <vnet/dpo/load_balance.h>
 
 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)
 } ip4_source_check_trace_t;
 
 static u8 * format_ip4_source_check_trace (u8 * s, va_list * va)
@@ -69,11 +71,7 @@ typedef enum {
 } ip4_source_check_type_t;
 
 typedef union {
 } 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
 } ip4_source_check_config_t;
 
 always_inline uword
@@ -115,9 +113,6 @@ ip4_source_check_inline (vlib_main_t * vm,
          const load_balance_t * lb0, * lb1;
          u32 pi0, next0, pass0, lb_index0;
          u32 pi1, next1, pass1, lb_index1;
          const load_balance_t * lb0, * lb1;
          u32 pi0, next0, pass0, lb_index0;
          u32 pi1, next1, pass1, lb_index1;
-          const ip_adjacency_t *adj0, *adj1;
-          const dpo_id_t *dpo0, *dpo1;
-          u32 ii0, ii1;
 
          /* Prefetch next iteration. */
          {
 
          /* Prefetch next iteration. */
          {
@@ -182,61 +177,18 @@ ip4_source_check_inline (vlib_main_t * vm,
          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 = 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);
 
-          if (PREDICT_TRUE(1 == lb0->lb_n_buckets))
-          {
-              dpo0 = load_balance_get_bucket_i(lb0, 0);
-              if (PREDICT_TRUE(dpo0->dpoi_type == DPO_ADJACENCY))
-              {
-                  pass0 |= (source_check_type ==
-                            IP4_SOURCE_CHECK_REACHABLE_VIA_ANY);
-                  adj0 = adj_get(dpo0->dpoi_index);
-                  pass0 |= (vnet_buffer (p0)->sw_if_index[VLIB_RX] ==
-                            adj0->rewrite_header.sw_if_index);
-              }
-          }
-          else
-          {
-              for (ii0 = 0; ii0 < lb0->lb_n_buckets && !pass0; ii0++)
-              {
-                  dpo0 = load_balance_get_bucket_i(lb0, ii0);
-                  if (PREDICT_TRUE(dpo0->dpoi_type == DPO_ADJACENCY))
-                  {
-                      pass0 |= (source_check_type ==
-                                IP4_SOURCE_CHECK_REACHABLE_VIA_ANY);
-                      adj0 = adj_get(dpo0->dpoi_index);
-                      pass0 |= (vnet_buffer (p0)->sw_if_index[VLIB_RX] ==
-                                adj0->rewrite_header.sw_if_index);
-                  }
-              }
-          }
-          if (PREDICT_TRUE(1 == lb1->lb_n_buckets))
-          {
-              dpo1 = load_balance_get_bucket_i(lb1, 0);
-              if (PREDICT_TRUE(dpo1->dpoi_type == DPO_ADJACENCY))
-              {
-                  pass1 |= (source_check_type ==
-                            IP4_SOURCE_CHECK_REACHABLE_VIA_ANY);
-                  adj1 = adj_get(dpo1->dpoi_index);
-                  pass1 |= (vnet_buffer (p1)->sw_if_index[VLIB_RX] ==
-                            adj1->rewrite_header.sw_if_index);
-              }
-          }
-          else
-          {
-              for (ii1 = 0; ii1 < lb1->lb_n_buckets && !pass1; ii1++)
-              {
-                  dpo1 = load_balance_get_bucket_i(lb1, ii1);
-                 if (PREDICT_TRUE(dpo1->dpoi_type == DPO_ADJACENCY))
-                  {
-                      pass1 |= (source_check_type ==
-                                IP4_SOURCE_CHECK_REACHABLE_VIA_ANY);
-                      adj1 = adj_get(dpo1->dpoi_index);
-                      pass1 |= (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);
 
          next0 = (pass0 ? next0 : IP4_SOURCE_CHECK_NEXT_DROP);
          next1 = (pass1 ? next1 : IP4_SOURCE_CHECK_NEXT_DROP);
 
@@ -255,11 +207,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;
          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, lb_index0;
          const load_balance_t * lb0;
          u32 pi0, next0, pass0, lb_index0;
          const load_balance_t * lb0;
-          const dpo_id_t *dpo0;
-          u32 ii0;
 
          pi0 = from[0];
          to_next[0] = pi0;
 
          pi0 = from[0];
          to_next[0] = pi0;
@@ -295,33 +244,15 @@ ip4_source_check_inline (vlib_main_t * vm,
          /* Pass multicast. */
          pass0 = ip4_address_is_multicast (&ip0->src_address) || ip0->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF);
 
          /* Pass multicast. */
          pass0 = ip4_address_is_multicast (&ip0->src_address) || ip0->src_address.as_u32 == clib_host_to_net_u32(0xFFFFFFFF);
 
-          if (PREDICT_TRUE(1 == lb0->lb_n_buckets))
-          {
-              dpo0 = load_balance_get_bucket_i(lb0, 0);
-              if (PREDICT_TRUE(dpo0->dpoi_type == DPO_ADJACENCY))
-              {
-                  pass0 |= (source_check_type ==
-                            IP4_SOURCE_CHECK_REACHABLE_VIA_ANY);
-                  adj0 = adj_get(dpo0->dpoi_index);
-                  pass0 |= (vnet_buffer (p0)->sw_if_index[VLIB_RX] ==
-                            adj0->rewrite_header.sw_if_index);
-              }
-          }
-          else
-          {
-              for (ii0 = 0; ii0 < lb0->lb_n_buckets && !pass0; ii0++)
-              {
-                  dpo0 = load_balance_get_bucket_i(lb0, ii0);
-                  if (PREDICT_TRUE(dpo0->dpoi_type == DPO_ADJACENCY))
-                  {
-                      pass0 |= (source_check_type ==
-                                IP4_SOURCE_CHECK_REACHABLE_VIA_ANY);
-                      adj0 = adj_get(dpo0->dpoi_index);
-                      pass0 |= (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];
 
          next0 = (pass0 ? next0 : IP4_SOURCE_CHECK_NEXT_DROP);
          p0->error = error_node->errors[IP4_ERROR_UNICAST_SOURCE_CHECK_FAILS];
@@ -392,6 +323,7 @@ set_ip_source_check (vlib_main_t * vm,
                     unformat_input_t * input,
                     vlib_cli_command_t * cmd)
 {
                     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;
   vnet_main_t * vnm = vnet_get_main();
   ip4_main_t * im = &ip4_main;
   ip_lookup_main_t * lm = &im->lookup_main;
@@ -402,21 +334,37 @@ set_ip_source_check (vlib_main_t * vm,
   u32 feature_index;
 
   sw_if_index = ~0;
   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_vnet_sw_interface, vnm, &sw_if_index))
+  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 (~0 == sw_if_index)
     {
       error = clib_error_return (0, "unknown interface `%U'",
     {
       error = clib_error_return (0, "unknown interface `%U'",
-                                format_unformat_error, input);
+                                format_unformat_error, line_input);
       goto done;
     }
 
       goto done;
     }
 
-  is_del = 0;
-  config.no_default_route = 0;
   config.fib_index = im->fib_index_by_sw_if_index[sw_if_index];
   config.fib_index = im->fib_index_by_sw_if_index[sw_if_index];
-  feature_index = im->ip4_unicast_rx_feature_source_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
   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
   ci = (is_del
        ? vnet_config_del_feature
@@ -432,11 +380,121 @@ set_ip_source_check (vlib_main_t * vm,
   return error;
 }
 
   return error;
 }
 
+/* *INDENT-OFF* */
+/*?
+ * Add the unicast RPF check feature to an input interface
+ *
+ * @cliexpar
+ * @cliexstart{set interface ip source-check}
+ * Two flavours are supported;
+ *  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.
+ * the deafult is strict.
+ *
+ * @cliexend
+?*/
 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",
 };
 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",
 };
+/* *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);
+}
+
+/* *INDENT-OFF* */
+/*?
+ * Add an exemption for a prefix to pass the uRPF loose check. Testing purposes only.
+ *
+ * @cliexpar
+ * @cliexstart{ip rpf-accept}
+ *
+ * Add an exception for a prefix to pass the loose RPF tests. This is usefull
+ * for testing purposes.
+ * VPP always performs a loose uRPF check for for-us traffic.
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (ip_source_check_accept_command, static) = {
+  .path = "ip urpf-accept",
+  .function = ip_source_check_accept,
+  .short_help = "Add a loose uRPF check exemption",
+};
+/* *INDENT-ON* */
+
 
 /* Dummy init function to get us linked in. */
 clib_error_t * ip4_source_check_init (vlib_main_t * vm)
 
 /* Dummy init function to get us linked in. */
 clib_error_t * ip4_source_check_init (vlib_main_t * vm)
index 5cfdc23..384e389 100644 (file)
@@ -598,12 +598,20 @@ vnet_ip_route_cmd (vlib_main_t * vm,
          {
              for (j = 0; j < vec_len (rpaths); j++)
              {
          {
              for (j = 0; j < vec_len (rpaths); j++)
              {
+                 u32 fi;
                  /*
                   * the CLI parsing stored table Ids, swap to FIB indicies
                   */
                  /*
                   * the CLI parsing stored table Ids, swap to FIB indicies
                   */
-                 rpaths[i].frp_fib_index =
-                     fib_table_id_find_fib_index(prefixs[i].fp_proto,
-                                                 rpaths[i].frp_fib_index);
+                 fi = fib_table_id_find_fib_index(prefixs[i].fp_proto,
+                                                  rpaths[i].frp_fib_index);
+
+                 if (~0 == fi)
+                 {
+                     error = clib_error_return(0 , "Via table %d does not exist",
+                                               rpaths[i].frp_fib_index);
+                     goto done;
+                 }
+                 rpaths[i].frp_fib_index = fi;
 
                  fib_prefix_t rpfx = {
                      .fp_len = prefixs[i].fp_len,
 
                  fib_prefix_t rpfx = {
                      .fp_len = prefixs[i].fp_len,