span: add tx functionality and support for multiple mirror ports
[vpp.git] / vnet / vnet / fib / fib_path.c
index 03cc7be..809e3e1 100644 (file)
 
 #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
@@ -206,10 +207,17 @@ typedef struct fib_path_t_ {
            u32 fp_interface;
        } attached;
        struct {
-           /**
-            * The next-hop
-            */
-           ip46_address_t fp_nh;
+           union
+           {
+               /**
+                * The next-hop
+                */
+               ip46_address_t fp_ip;
+               /**
+                * The local label to resolve through.
+                */
+               mpls_label_t fp_local_label;
+           } fp_nh;
            /**
             * The FIB table index in which to find the next-hop.
             * This needs to be fixed. We should lookup the adjacencies in
@@ -236,7 +244,7 @@ typedef struct fib_path_t_ {
        } recursive;
        struct {
            /**
-            * The FIN index in which to perfom the next lookup
+            * The FIB index in which to perfom the next lookup
             */
            fib_node_index_t fp_tbl_id;
        } deag;
@@ -427,11 +435,22 @@ format_fib_path (u8 * s, va_list * args)
        }
        break;
     case FIB_PATH_TYPE_RECURSIVE:
-       s = format (s, "via %U",
-                   format_ip46_address,
-                   &path->recursive.fp_nh,
-                   IP46_TYPE_ANY);
-       s = format (s, " in fib:%d", path->recursive.fp_tbl_id, path->fp_via_fib); 
+       if (FIB_PROTOCOL_MPLS == path->fp_nh_proto)
+       {
+           s = format (s, "via %U",
+                       format_mpls_unicast_label,
+                       path->recursive.fp_nh.fp_local_label);
+       }
+       else
+       {
+           s = format (s, "via %U",
+                       format_ip46_address,
+                       &path->recursive.fp_nh.fp_ip,
+                       IP46_TYPE_ANY);
+       }
+       s = format (s, " in fib:%d",
+                   path->recursive.fp_tbl_id,
+                   path->fp_via_fib); 
        s = format (s, " via-fib:%d", path->fp_via_fib); 
        s = format (s, " via-dpo:[%U:%d]",
                    format_dpo_type, path->fp_dpo.dpoi_type, 
@@ -500,7 +519,7 @@ fib_path_last_lock_gone (fib_node_t *node)
 
 static const adj_index_t
 fib_path_attached_next_hop_get_adj (fib_path_t *path,
-                                   fib_link_t link)
+                                   vnet_link_t link)
 {
     if (vnet_sw_interface_is_p2p(vnet_get_main(),
                                 path->attached_next_hop.fp_interface))
@@ -562,7 +581,7 @@ fib_path_recursive_adj_update (fib_path_t *path,
                               fib_forward_chain_type_t fct,
                               dpo_id_t *dpo)
 {
-    dpo_id_t via_dpo = DPO_NULL;
+    dpo_id_t via_dpo = DPO_INVALID;
 
     /*
      * get the DPO to resolve through from the via-entry
@@ -676,7 +695,7 @@ fib_path_unresolve (fib_path_t *path)
        {
            fib_prefix_t pfx;
 
-           fib_prefix_from_ip46_addr(&path->recursive.fp_nh, &pfx);
+           fib_entry_get_prefix(path->fp_via_fib, &pfx);
            fib_entry_child_remove(path->fp_via_fib,
                                   path->fp_sibling);
            fib_table_entry_special_remove(path->recursive.fp_tbl_id,
@@ -756,6 +775,21 @@ fib_path_back_walk_notify (fib_node_t *node,
                fib_path_proto_to_chain_type(path->fp_nh_proto),
                &path->fp_dpo);
        }
+       if ((FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE & ctx->fnbw_reason) ||
+            (FIB_NODE_BW_REASON_FLAG_ADJ_DOWN   & ctx->fnbw_reason))
+       {
+           /*
+            * ADJ updates (complete<->incomplete) do not need to propagate to
+            * recursive entries.
+            * The only reason its needed as far back as here, is that the adj
+            * and the incomplete adj are a different DPO type, so the LBs need
+            * to re-stack.
+            * If this walk was quashed in the fib_entry, then any non-fib_path
+            * children (like tunnels that collapse out the LB when they stack)
+            * would not see the update.
+            */
+           return (FIB_NODE_BACK_WALK_CONTINUE);
+       }
        break;
     case FIB_PATH_TYPE_ATTACHED_NEXT_HOP:
        /*
@@ -771,10 +805,24 @@ FIXME comment
         */
        if (FIB_NODE_BW_REASON_FLAG_INTERFACE_UP & ctx->fnbw_reason)
        {
+            if (path->fp_oper_flags & FIB_PATH_OPER_FLAG_RESOLVED)
+            {
+                /*
+                 * alreday resolved. no need to walk back again
+                 */
+                return (FIB_NODE_BACK_WALK_CONTINUE);
+            }
            path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RESOLVED;
        }
        if (FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN & ctx->fnbw_reason)
        {
+            if (!(path->fp_oper_flags & FIB_PATH_OPER_FLAG_RESOLVED))
+            {
+                /*
+                 * alreday unresolved. no need to walk back again
+                 */
+                return (FIB_NODE_BACK_WALK_CONTINUE);
+            }
            path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
        }
        if (FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE & ctx->fnbw_reason)
@@ -793,8 +841,18 @@ FIXME comment
             /*
              * restack the DPO to pick up the correct DPO sub-type
              */
+            uword if_is_up;
             adj_index_t ai;
 
+            if_is_up = vnet_sw_interface_is_admin_up(
+                           vnet_get_main(),
+                           path->attached_next_hop.fp_interface);
+
+            if (if_is_up)
+            {
+                path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RESOLVED;
+            }
+
             ai = fib_path_attached_next_hop_get_adj(
                      path,
                      fib_proto_to_link(path->fp_nh_proto));
@@ -803,6 +861,32 @@ FIXME comment
                     fib_proto_to_dpo(path->fp_nh_proto),
                     ai);
             adj_unlock(ai);
+
+            if (!if_is_up)
+            {
+                /*
+                 * If the interface is not up there is no reason to walk
+                 * back to children. if we did they would only evalute
+                 * that this path is unresolved and hence it would
+                 * not contribute the adjacency - so it would be wasted
+                 * CPU time.
+                 */
+                return (FIB_NODE_BACK_WALK_CONTINUE);
+            }
+        }
+        if (FIB_NODE_BW_REASON_FLAG_ADJ_DOWN & ctx->fnbw_reason)
+       {
+            if (!(path->fp_oper_flags & FIB_PATH_OPER_FLAG_RESOLVED))
+            {
+                /*
+                 * alreday unresolved. no need to walk back again
+                 */
+                return (FIB_NODE_BACK_WALK_CONTINUE);
+            }
+            /*
+             * the adj has gone down. the path is no longer resolved.
+             */
+           path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
         }
        break;
     case FIB_PATH_TYPE_ATTACHED:
@@ -870,7 +954,7 @@ static const fib_node_vft_t fib_path_vft = {
 static fib_path_cfg_flags_t
 fib_path_route_flags_to_cfg_flags (const fib_route_path_t *rpath)
 {
-    fib_path_cfg_flags_t cfg_flags = FIB_PATH_CFG_ATTRIBUTE_FIRST;
+    fib_path_cfg_flags_t cfg_flags = FIB_PATH_CFG_FLAG_NONE;
 
     if (rpath->frp_flags & FIB_ROUTE_PATH_RESOLVE_VIA_HOST)
        cfg_flags |= FIB_PATH_CFG_FLAG_RESOLVE_HOST;
@@ -905,6 +989,14 @@ fib_path_create (fib_node_index_t pl_index,
     path->fp_nh_proto = nh_proto;
     path->fp_via_fib = FIB_NODE_INDEX_INVALID;
     path->fp_weight = rpath->frp_weight;
+    if (0 == path->fp_weight)
+    {
+        /*
+         * a weight of 0 is a meaningless value. We could either reject it, and thus force
+         * clients to always use 1, or we can accept it and fixup approrpiately.
+         */
+        path->fp_weight = 1;
+    }
     path->fp_cfg_flags = flags;
     path->fp_cfg_flags |= fib_path_route_flags_to_cfg_flags(rpath);
 
@@ -951,7 +1043,14 @@ fib_path_create (fib_node_index_t pl_index,
        else
        {
            path->fp_type = FIB_PATH_TYPE_RECURSIVE;
-           path->recursive.fp_nh = rpath->frp_addr;
+           if (FIB_PROTOCOL_MPLS == path->fp_nh_proto)
+           {
+               path->recursive.fp_nh.fp_local_label = rpath->frp_local_label;
+           }
+           else
+           {
+               path->recursive.fp_nh.fp_ip = rpath->frp_addr;
+           }
            path->recursive.fp_tbl_id = rpath->frp_fib_index;
        }
     }
@@ -1227,13 +1326,20 @@ fib_path_cmp_w_route_path (fib_node_index_t path_index,
                      rpath->frp_sw_if_index);
            break;
        case FIB_PATH_TYPE_RECURSIVE:
-           res = ip46_address_cmp(&path->recursive.fp_nh,
-                                  &rpath->frp_addr);
-           if (0 == res)
-           {
-               res = (path->recursive.fp_tbl_id - rpath->frp_fib_index);
-           }
+            if (FIB_PROTOCOL_MPLS == path->fp_nh_proto)
+            {
+                res = path->recursive.fp_nh.fp_local_label - rpath->frp_local_label;
+            }
+            else
+            {
+                res = ip46_address_cmp(&path->recursive.fp_nh.fp_ip,
+                                       &rpath->frp_addr);
+            }
+
+            if (0 == res)
+            {
+                res = (path->recursive.fp_tbl_id - rpath->frp_fib_index);
+            }
            break;
        case FIB_PATH_TYPE_DEAG:
            res = (path->deag.fp_tbl_id - rpath->frp_fib_index);
@@ -1432,7 +1538,14 @@ fib_path_resolve (fib_node_index_t path_index)
 
        ASSERT(FIB_NODE_INDEX_INVALID == path->fp_via_fib);
 
-       fib_prefix_from_ip46_addr(&path->recursive.fp_nh, &pfx);
+       if (FIB_PROTOCOL_MPLS == path->fp_nh_proto)
+       {
+           fib_prefix_from_mpls_label(path->recursive.fp_nh.fp_local_label, &pfx);
+       }
+       else
+       {
+           fib_prefix_from_ip46_addr(&path->recursive.fp_nh.fp_ip, &pfx);
+       }
 
        fei = fib_table_entry_special_add(path->recursive.fp_tbl_id,
                                          &pfx,
@@ -1551,6 +1664,62 @@ fib_path_get_weight (fib_node_index_t path_index)
     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,
@@ -1590,7 +1759,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
                adj_index_t ai;
 
                /*
-                * get a MPLS link type adj.
+                * get a appropriate link type adj.
                 */
                ai = fib_path_attached_next_hop_get_adj(
                         path,
@@ -1609,12 +1778,6 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
            case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
            case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
            case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
-               /*
-                * Assume that EOS and IP forwarding is the same.
-                * revisit for ieBGP
-                */
-               dpo_copy(dpo, &path->fp_dpo);
-               break;
            case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
                fib_path_recursive_adj_update(path, fct, dpo);
                break;
@@ -1733,6 +1896,59 @@ fib_path_is_looped (fib_node_index_t path_index)
     return (path->fp_oper_flags & FIB_PATH_OPER_FLAG_RECURSIVE_LOOP);
 }
 
+int
+fib_path_encode (fib_node_index_t path_list_index,
+                fib_node_index_t path_index,
+                 void *ctx)
+{
+    fib_route_path_encode_t **api_rpaths = ctx;
+    fib_route_path_encode_t *api_rpath;
+    fib_path_t *path;
+
+    path = fib_path_get(path_index);
+    if (!path)
+      return (0);
+    vec_add2(*api_rpaths, api_rpath, 1);
+    api_rpath->rpath.frp_weight = path->fp_weight;
+    api_rpath->rpath.frp_proto = path->fp_nh_proto;
+    api_rpath->rpath.frp_sw_if_index = ~0;
+    api_rpath->dpo = path->exclusive.fp_ex_dpo;
+    switch (path->fp_type)
+      {
+      case FIB_PATH_TYPE_RECEIVE:
+        api_rpath->rpath.frp_addr = path->receive.fp_addr;
+        api_rpath->rpath.frp_sw_if_index = path->receive.fp_interface;
+        break;
+      case FIB_PATH_TYPE_ATTACHED:
+        api_rpath->rpath.frp_sw_if_index = path->attached.fp_interface;
+        break;
+      case FIB_PATH_TYPE_ATTACHED_NEXT_HOP:
+        api_rpath->rpath.frp_sw_if_index = path->attached_next_hop.fp_interface;
+        api_rpath->rpath.frp_addr = path->attached_next_hop.fp_nh;
+        break;
+      case FIB_PATH_TYPE_SPECIAL:
+        break;
+      case FIB_PATH_TYPE_DEAG:
+        break;
+      case FIB_PATH_TYPE_RECURSIVE:
+        api_rpath->rpath.frp_addr = path->recursive.fp_nh.fp_ip;
+        break;
+      default:
+        break;
+      }
+    return (1);
+}
+
+fib_protocol_t
+fib_path_get_proto (fib_node_index_t path_index)
+{
+    fib_path_t *path;
+
+    path = fib_path_get(path_index);
+
+    return (path->fp_nh_proto);
+}
+
 void
 fib_path_module_init (void)
 {
@@ -1744,13 +1960,36 @@ show_fib_path_command (vlib_main_t * vm,
                        unformat_input_t * input,
                        vlib_cli_command_t * cmd)
 {
+    fib_node_index_t pi;
     fib_path_t *path;
 
-    vlib_cli_output (vm, "FIB Path Lists");
-    pool_foreach(path, fib_path_pool,
-    ({
-       vlib_cli_output (vm, "%U", format_fib_path, path);
-    }));
+    if (unformat (input, "%d", &pi))
+    {
+       /*
+        * show one in detail
+        */
+       if (!pool_is_free_index(fib_path_pool, pi))
+       {
+           path = fib_path_get(pi);
+           u8 *s = fib_path_format(pi, NULL);
+           s = format(s, "children:");
+           s = fib_node_children_format(path->fp_node.fn_children, s);
+           vlib_cli_output (vm, "%s", s);
+           vec_free(s);
+       }
+       else
+       {
+           vlib_cli_output (vm, "path %d invalid", pi);
+       }
+    }
+    else
+    {
+       vlib_cli_output (vm, "FIB Paths");
+       pool_foreach(path, fib_path_pool,
+       ({
+           vlib_cli_output (vm, "%U", format_fib_path, path);
+       }));
+    }
 
     return (NULL);
 }