BFD-FIB interactions

- single-hop BFD: attach a delegate to the appropriate adjacency
- multi-hop BFD [not supported yet]: attach a delegate to the FIB entry.

adjacency/fib_entry state tracks the BFD session state. when the state is down the object does not contribute forwarding hence and hence dependent objects will not use it.
For example, if a route is ECMP via two adjacencies and one of them is BFD down, then only the other is used to forward (i.e. we don't drop half the traffic).

Change-Id: I0ef53e20e73b067001a132cd0a3045408811a822
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c
index 928a9d4..6b202a9 100644
--- a/src/vnet/fib/fib_path.c
+++ b/src/vnet/fib/fib_path.c
@@ -558,12 +558,6 @@
      * resolve directly via the adjacnecy discribed by the
      * interface and next-hop
      */
-    if (!vnet_sw_interface_is_admin_up(vnet_get_main(),
-				      path->attached_next_hop.fp_interface))
-    {
-	path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
-    }
-
     dpo_set(&path->fp_dpo,
 	    DPO_ADJACENCY,
 	    fib_proto_to_dpo(path->fp_nh_proto),
@@ -578,6 +572,13 @@
     path->fp_sibling = adj_child_add(path->fp_dpo.dpoi_index,
 				     FIB_NODE_TYPE_PATH,
 				     fib_path_get_index(path));
+
+    if (!vnet_sw_interface_is_admin_up(vnet_get_main(),
+				      path->attached_next_hop.fp_interface) ||
+        !adj_is_up(path->fp_dpo.dpoi_index))
+    {
+	path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
+    }
 }
 
 /*
@@ -653,6 +654,19 @@
             load_balance_map_path_state_change(fib_path_get_index(path));
 	}
     }
+    /*
+     * check for over-riding factors on the FIB entry itself
+     */
+    if (!fib_entry_is_resolved(path->fp_via_fib))
+    {
+        path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
+        dpo_copy(&via_dpo, drop_dpo_get(fib_proto_to_dpo(path->fp_nh_proto)));
+
+        /*
+         * PIC edge trigger. let the load-balance maps know
+         */
+        load_balance_map_path_state_change(fib_path_get_index(path));
+    }
 
     /*
      * update the path's contributed DPO
@@ -855,15 +869,16 @@
                            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));
 
+            path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
+            if (if_is_up && adj_is_up(ai))
+            {
+                path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RESOLVED;
+            }
+
             dpo_set(&path->fp_dpo, DPO_ADJACENCY,
                     fib_proto_to_dpo(path->fp_nh_proto),
                     ai);
@@ -1684,11 +1699,11 @@
 {
     fib_path_t *path;
 
-    if (!fib_path_is_resolved(path_index))
-	return;
-
     path = fib_path_get(path_index);
 
+    /*
+     * resolved and unresolved paths contribute to the RPF list.
+     */
     switch (path->fp_type)
     {
     case FIB_PATH_TYPE_ATTACHED_NEXT_HOP:
@@ -1700,7 +1715,14 @@
 	break;
 
     case FIB_PATH_TYPE_RECURSIVE:
-	fib_entry_contribute_urpf(path->fp_via_fib, urpf);
+        if (FIB_NODE_INDEX_INVALID != path->fp_via_fib)
+        {
+            /*
+             * there's unresolved due to constraints, and there's unresolved
+             * due to ain't go no via. can't do nowt w'out via.
+             */
+            fib_entry_contribute_urpf(path->fp_via_fib, urpf);
+        }
 	break;
 
     case FIB_PATH_TYPE_EXCLUSIVE: