BFD-FIB interactions 63/6063/5
authorNeale Ranns <nranns@cisco.com>
Wed, 5 Apr 2017 15:11:14 +0000 (08:11 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Thu, 6 Apr 2017 15:18:44 +0000 (15:18 +0000)
- 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>
25 files changed:
src/vnet.am
src/vnet/adj/adj.c
src/vnet/adj/adj.h
src/vnet/adj/adj_bfd.c [new file with mode: 0644]
src/vnet/adj/adj_delegate.c [new file with mode: 0644]
src/vnet/adj/adj_delegate.h [new file with mode: 0644]
src/vnet/bfd/bfd_main.c
src/vnet/bfd/bfd_main.h
src/vnet/bfd/bfd_udp.h
src/vnet/fib/fib_attached_export.c
src/vnet/fib/fib_attached_export.h
src/vnet/fib/fib_bfd.c [new file with mode: 0644]
src/vnet/fib/fib_entry.c
src/vnet/fib/fib_entry.h
src/vnet/fib/fib_entry_cover.c
src/vnet/fib/fib_entry_cover.h
src/vnet/fib/fib_entry_delegate.c
src/vnet/fib/fib_entry_delegate.h
src/vnet/fib/fib_path.c
src/vnet/fib/fib_test.c
src/vnet/fib/ip4_fib.c
src/vnet/fib/ip6_fib.c
test/framework.py
test/test_bfd.py
test/vpp_papi_provider.py

index 1d52ae1..643ae92 100644 (file)
@@ -936,7 +936,8 @@ libvnet_la_SOURCES +=                               \
   vnet/fib/fib_path.c                          \
   vnet/fib/fib_path_ext.c                      \
   vnet/fib/fib_urpf_list.c                     \
-  vnet/fib/fib_attached_export.c
+  vnet/fib/fib_attached_export.c               \
+  vnet/fib/fib_bfd.c
 
 nobase_include_HEADERS +=                      \
   vnet/fib/fib.h                               \
@@ -962,7 +963,9 @@ libvnet_la_SOURCES +=                               \
   vnet/adj/adj_l2.c                            \
   vnet/adj/adj_nsh.c                           \
   vnet/adj/adj.c                               \
-  vnet/adj/rewrite.c
+  vnet/adj/rewrite.c                           \
+  vnet/adj/adj_bfd.c                           \
+  vnet/adj/adj_delegate.c
 
 nobase_include_HEADERS +=                      \
   vnet/adj/adj.h                               \
index 7cf9e9d..9018200 100644 (file)
@@ -18,6 +18,7 @@
 #include <vnet/adj/adj_glean.h>
 #include <vnet/adj/adj_midchain.h>
 #include <vnet/adj/adj_mcast.h>
+#include <vnet/adj/adj_delegate.h>
 #include <vnet/fib/fib_node_list.h>
 
 /* Adjacency packet/byte counters indexed by adjacency index. */
@@ -57,13 +58,14 @@ adj_alloc (fib_protocol_t proto)
     vlib_validate_combined_counter(&adjacency_counters,
                                    adj_get_index(adj));
 
-    adj->rewrite_header.sw_if_index = ~0;
-    adj->lookup_next_index = 0;
-
     fib_node_init(&adj->ia_node,
                   FIB_NODE_TYPE_ADJ);
+
     adj->ia_nh_proto = proto;
     adj->ia_flags = 0;
+    adj->rewrite_header.sw_if_index = ~0;
+    adj->lookup_next_index = 0;
+    adj->ia_delegates = NULL;
 
     ip4_main.lookup_main.adjacency_heap = adj_pool;
     ip6_main.lookup_main.adjacency_heap = adj_pool;
@@ -122,11 +124,19 @@ format_ip_adjacency (u8 * s, va_list * args)
 
     if (fiaf & FORMAT_IP_ADJACENCY_DETAIL)
     {
+        adj_delegate_type_t adt;
+        adj_delegate_t *aed;
         vlib_counter_t counts;
 
         vlib_get_combined_counter(&adjacency_counters, adj_index, &counts);
         s = format (s, "\n counts:[%Ld:%Ld]", counts.packets, counts.bytes);
        s = format (s, "\n locks:%d", adj->ia_node.fn_locks);
+       s = format(s, "\n delegates:\n  ");
+        FOR_EACH_ADJ_DELEGATE(adj, adt, aed,
+        {
+            s = format(s, "  %U\n", format_adj_deletegate, aed);
+        });
+
        s = format(s, "\n children:\n  ");
        s = fib_node_children_format(adj->ia_node.fn_children, s);
     }
@@ -173,7 +183,11 @@ adj_last_lock_gone (ip_adjacency_t *adj)
        adj_mcast_remove(adj->ia_nh_proto,
                         adj->rewrite_header.sw_if_index);
        break;
-    default:
+    case IP_LOOKUP_NEXT_DROP:
+    case IP_LOOKUP_NEXT_PUNT:
+    case IP_LOOKUP_NEXT_LOCAL:
+    case IP_LOOKUP_NEXT_ICMP_ERROR:
+    case IP_LOOKUP_N_NEXT:
        /*
         * type not stored in any DB from which we need to remove it
         */
@@ -183,6 +197,8 @@ adj_last_lock_gone (ip_adjacency_t *adj)
     vlib_worker_thread_barrier_release(vm);
 
     fib_node_deinit(&adj->ia_node);
+    ASSERT(0 == vec_len(adj->ia_delegates));
+    vec_free(adj->ia_delegates);
     pool_put(adj_pool, adj);
 }
 
@@ -351,6 +367,33 @@ adj_get_sw_if_index (adj_index_t ai)
     return (adj->rewrite_header.sw_if_index);
 }
 
+/**
+ * @brief Return true if the adjacency is 'UP', i.e. can be used for forwarding
+ * 0 is down, !0 is up.
+ */
+int
+adj_is_up (adj_index_t ai)
+{
+    const adj_delegate_t *aed;
+
+    aed = adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD);
+
+    if (NULL == aed)
+    {
+        /*
+         * no BFD tracking - resolved
+         */
+        return (!0);
+    }
+    else
+    {
+        /*
+         * defer to the state of the BFD tracking
+         */
+        return (ADJ_BFD_STATE_UP == aed->ad_bfd_state);
+    }
+}
+
 /**
  * @brief Return the rewrite string of the adjacency
  */
index af7730f..32997c9 100644 (file)
@@ -255,6 +255,15 @@ typedef struct ip_adjacency_t_
 
   /* Rewrite in second/third cache lines */
   vnet_declare_rewrite (VLIB_BUFFER_PRE_DATA_SIZE);
+
+  /**
+   * more control plane members that do not fit on the first cacheline
+   */
+  /**
+   * A sorted vector of delegates
+   */
+  struct adj_delegate_t_ *ia_delegates;
+
 } ip_adjacency_t;
 
 STATIC_ASSERT ((STRUCT_OFFSET_OF (ip_adjacency_t, cacheline0) == 0),
@@ -307,6 +316,12 @@ extern vnet_link_t adj_get_link_type (adj_index_t ai);
  */
 extern u32 adj_get_sw_if_index (adj_index_t ai);
 
+/**
+ * @brief Return true if the adjacency is 'UP', i.e. can be used for forwarding.
+ * 0 is down, !0 is up.
+ */
+extern int adj_is_up (adj_index_t ai);
+
 /**
  * @brief Return the link type of the adjacency
  */
diff --git a/src/vnet/adj/adj_bfd.c b/src/vnet/adj/adj_bfd.c
new file mode 100644 (file)
index 0000000..3d294c4
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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/bfd/bfd_main.h>
+
+#include <vnet/adj/adj_delegate.h>
+#include <vnet/adj/adj_nbr.h>
+#include <vnet/fib/fib_walk.h>
+
+static adj_bfd_state_t
+adj_bfd_bfd_state_to_fib (bfd_state_e bstate)
+{
+    switch (bstate)
+    {
+    case BFD_STATE_up:
+        return (ADJ_BFD_STATE_UP);
+    case BFD_STATE_down:
+    case BFD_STATE_admin_down:
+    case BFD_STATE_init:
+        return (ADJ_BFD_STATE_DOWN);
+    }
+    return (ADJ_BFD_STATE_DOWN);
+}
+
+static void
+adj_bfd_update_walk (adj_index_t ai)
+{
+    /*
+     * initiate a backwalk of dependent children
+     * to notify of the state change of this adj.
+     */
+    fib_node_back_walk_ctx_t ctx = {
+        .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
+    };
+    fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &ctx);
+}
+
+/**
+ * @brief Callback function registered with BFD module to receive notifications
+ * of the CRUD of BFD sessions
+ * would be static but for the fact it's called from the unit-tests
+ */
+void
+adj_bfd_notify (bfd_listen_event_e event,
+                const bfd_session_t *session)
+{
+    const bfd_udp_key_t *key;
+    fib_protocol_t fproto;
+    adj_delegate_t *aed;
+    adj_index_t ai;
+
+    if (BFD_HOP_TYPE_SINGLE != session->hop_type)
+    {
+        /*
+         * multi-hop BFD sessions attach directly to the FIB entry
+         * single-hop adj to the associate adjacency.
+         */
+        return;
+    }
+
+    key = &session->udp.key;
+
+    fproto = (ip46_address_is_ip4 (&key->peer_addr) ?
+              FIB_PROTOCOL_IP4:
+              FIB_PROTOCOL_IP6);
+
+    /*
+     * find the adj that corresponds to the BFD session.
+     */
+    ai = adj_nbr_add_or_lock(fproto,
+                             fib_proto_to_link(fproto),
+                             &key->peer_addr,
+                             key->sw_if_index);
+
+    switch (event)
+    {
+    case BFD_LISTEN_EVENT_CREATE:
+        /*
+         * The creation of a new session
+         */
+        if ((ADJ_INDEX_INVALID != ai) &&
+            (aed = adj_delegate_get(adj_get(ai),
+                                    ADJ_DELEGATE_BFD)))
+        {
+            /*
+             * already got state for this adj
+             */
+        }
+        else
+        {
+            /*
+             * lock the adj. add the delegate.
+             * Lockinging the adj prevents it being removed and thus maintains
+             * the BFD derived states
+             */
+            adj_lock(ai);
+
+            aed = adj_delegate_find_or_add(adj_get(ai), ADJ_DELEGATE_BFD);
+
+            /*
+             * pretend the session is up and skip the walk.
+             * If we set it down then we get traffic loss on new children.
+             * if we walk then we lose traffic for existing children. Wait
+             * for the first BFD UP/DOWN before we let the session's state
+             * influence forwarding.
+             */
+            aed->ad_bfd_state = ADJ_BFD_STATE_UP;
+            aed->ad_bfd_index = session->bs_idx;
+        }
+        break;
+
+    case BFD_LISTEN_EVENT_UPDATE:
+        /*
+         * state change up/dowm and
+         */
+        aed = adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD);
+
+        if (NULL != aed)
+        {
+            aed->ad_bfd_state = adj_bfd_bfd_state_to_fib(session->local_state);
+            adj_bfd_update_walk(ai);
+        }
+        /*
+         * else
+         *   not an adj with BFD state
+         */
+        break;
+
+    case BFD_LISTEN_EVENT_DELETE:
+        /*
+         * session has been removed.
+         */
+
+        if (adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD))
+        {
+            /*
+             * has an associated BFD tracking delegate
+             * remove the BFD tracking deletgate, update children, then
+             * unlock the adj
+             */
+            adj_delegate_remove(adj_get(ai), ADJ_DELEGATE_BFD);
+
+            adj_bfd_update_walk(ai);
+            adj_unlock(ai);
+        }
+        /*
+         * else
+         *  no BFD associated state
+         */
+        break;
+    }
+
+    /*
+     * unlock match of the add-or-lock at the start
+     */
+    adj_unlock(ai);
+}
+
+static clib_error_t *
+adj_bfd_main_init (vlib_main_t * vm)
+{
+    clib_error_t * error = NULL;
+
+    if ((error = vlib_call_init_function (vm, bfd_main_init)))
+        return (error);
+
+    bfd_register_listener(adj_bfd_notify);
+
+    return (error);
+}
+
+VLIB_INIT_FUNCTION (adj_bfd_main_init);
diff --git a/src/vnet/adj/adj_delegate.c b/src/vnet/adj/adj_delegate.c
new file mode 100644 (file)
index 0000000..701b36e
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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/adj/adj_delegate.h>
+#include <vnet/adj/adj.h>
+#include <vnet/adj/adj_internal.h>
+
+static adj_delegate_t *
+adj_delegate_find_i (const ip_adjacency_t *adj,
+                     adj_delegate_type_t type,
+                     u32 *index)
+{
+    adj_delegate_t *delegate;
+    int ii;
+
+    ii = 0;
+    vec_foreach(delegate, adj->ia_delegates)
+    {
+       if (delegate->ad_type == type)
+       {
+            if (NULL != index)
+                *index = ii;
+
+           return (delegate);
+       }
+       else
+       {
+           ii++;
+       }
+    }
+
+    return (NULL);
+}
+
+adj_delegate_t *
+adj_delegate_get (const ip_adjacency_t *adj,
+                  adj_delegate_type_t type)
+{
+    return (adj_delegate_find_i(adj, type, NULL));
+}
+
+void
+adj_delegate_remove (ip_adjacency_t *adj,
+                     adj_delegate_type_t type)
+{
+    adj_delegate_t *aed;
+    u32 index = ~0;
+
+    aed = adj_delegate_find_i(adj, type, &index);
+
+    ASSERT(NULL != aed);
+
+    vec_del1(adj->ia_delegates, index);
+}
+
+static int
+adj_delegate_cmp_for_sort (void * v1,
+                           void * v2)
+{
+    adj_delegate_t *delegate1 = v1, *delegate2 = v2;
+
+    return (delegate1->ad_type - delegate2->ad_type);
+}
+
+static void
+adj_delegate_init (ip_adjacency_t *adj,
+                   adj_delegate_type_t type)
+
+{
+    adj_delegate_t delegate = {
+       .ad_adj_index = adj_get_index(adj),
+       .ad_type = type,
+    };
+
+    vec_add1(adj->ia_delegates, delegate);
+    vec_sort_with_function(adj->ia_delegates,
+                          adj_delegate_cmp_for_sort);
+}
+
+adj_delegate_t *
+adj_delegate_find_or_add (ip_adjacency_t *adj,
+                          adj_delegate_type_t adt)
+{
+    adj_delegate_t *delegate;
+
+    delegate = adj_delegate_get(adj, adt);
+
+    if (NULL == delegate)
+    {
+       adj_delegate_init(adj, adt);
+    }
+
+    return (adj_delegate_get(adj, adt));
+}
+
+/**
+ * typedef for printing a delegate
+ */
+typedef u8 * (*adj_delegate_format_t)(const adj_delegate_t *aed,
+                                      u8 *s);
+
+/**
+ * Print a delegate that represents BFD tracking
+ */
+static u8 *
+adj_delegate_fmt_bfd (const adj_delegate_t *aed,
+                      u8 *s)
+{
+    s = format(s, "BFD:[state:%d index:%d]",
+               aed->ad_bfd_state,
+               aed->ad_bfd_index);
+
+    return (s);
+}
+
+/**
+ * A delegate type to formatter map
+ */
+static adj_delegate_format_t aed_formatters[] =
+{
+    [ADJ_DELEGATE_BFD] = adj_delegate_fmt_bfd,
+};
+
+u8 *
+format_adj_deletegate (u8 * s, va_list * args)
+{
+    adj_delegate_t *aed;
+
+    aed = va_arg (*args, adj_delegate_t *);
+
+    return (aed_formatters[aed->ad_type](aed, s));
+}
diff --git a/src/vnet/adj/adj_delegate.h b/src/vnet/adj/adj_delegate.h
new file mode 100644 (file)
index 0000000..1765120
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef __ADJ_DELEGATE_T__
+#define __ADJ_DELEGATE_T__
+
+#include <vnet/adj/adj.h>
+
+/**
+ * Delegate types
+ */
+typedef enum adj_delegate_type_t_ {
+    /**
+     * BFD session state
+     */
+    ADJ_DELEGATE_BFD,
+} adj_delegate_type_t;
+
+#define FOR_EACH_ADJ_DELEGATE(_adj, _adt, _aed, _body)        \
+{                                                             \
+    for (_adt = ADJ_DELEGATE_BFD;                             \
+         _adt <= ADJ_DELEGATE_BFD;                            \
+         _adt++)                                              \
+    {                                                         \
+        _aed = adj_delegate_get(_adj, _adt);                  \
+        if (NULL != _aed) {                                   \
+            _body;                                            \
+        }                                                     \
+    }                                                         \
+}
+
+/**
+ * Distillation of the BFD session states into a go/no-go for using
+ * the associated tracked adjacency
+ */
+typedef enum adj_bfd_state_t_
+{
+    ADJ_BFD_STATE_DOWN,
+    ADJ_BFD_STATE_UP,
+} adj_bfd_state_t;
+
+/**
+ * A Delagate is a means to implement the Delagation design pattern;
+ * the extension of an object's functionality through the composition of,
+ * and delgation to, other objects.
+ * These 'other' objects are delegates. Delagates are thus attached to
+ * ADJ objects to extend their functionality.
+ */
+typedef struct adj_delegate_t_
+{
+    /**
+     * The ADJ entry object to which the delagate is attached
+     */
+    adj_index_t ad_adj_index;
+
+    /**
+     * The delagate type
+     */
+    adj_delegate_type_t ad_type;
+
+    /**
+     * A union of data for the different delegate types
+     */
+    union
+    {
+        /**
+         * BFD delegate daa
+         */
+        struct {
+            /**
+             * BFD session state
+             */
+            adj_bfd_state_t ad_bfd_state;
+            /**
+             * BFD session index
+             */
+            u32 ad_bfd_index;
+        };
+    };
+} adj_delegate_t;
+
+extern void adj_delegate_remove(ip_adjacency_t *adj,
+                                adj_delegate_type_t type);
+
+extern adj_delegate_t *adj_delegate_find_or_add(ip_adjacency_t *adj,
+                                                adj_delegate_type_t fdt);
+extern adj_delegate_t *adj_delegate_get(const ip_adjacency_t *adj,
+                                        adj_delegate_type_t type);
+
+extern u8 *format_adj_deletegate(u8 * s, va_list * args);
+
+#endif
index 2b70a20..66b31ce 100644 (file)
@@ -101,6 +101,7 @@ bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
   bs->local_diag = BFD_DIAG_CODE_no_diag;
   bs->remote_state = BFD_STATE_down;
   bs->remote_discr = 0;
+  bs->hop_type = BFD_HOP_TYPE_SINGLE;
   bs->config_desired_min_tx_usec = BFD_DEFAULT_DESIRED_MIN_TX_USEC;
   bs->config_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
   bs->effective_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
@@ -387,6 +388,17 @@ bfd_set_remote_required_min_echo_rx (bfd_main_t * bm, bfd_session_t * bs,
     }
 }
 
+static void
+bfd_notify_listeners (bfd_main_t * bm,
+                     bfd_listen_event_e event, const bfd_session_t * bs)
+{
+  bfd_notify_fn_t *fn;
+  vec_foreach (fn, bm->listeners)
+  {
+    (*fn) (event, bs);
+  }
+}
+
 void
 bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
 {
@@ -396,6 +408,7 @@ bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
   bfd_recalc_tx_interval (bm, bs);
   vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
                             BFD_EVENT_NEW_SESSION, bs->bs_idx);
+  bfd_notify_listeners (bm, BFD_LISTEN_EVENT_CREATE, bs);
 }
 
 void
@@ -533,6 +546,7 @@ bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
       bfd_set_timer (bm, bs, now, handling_wakeup);
       break;
     }
+  bfd_notify_listeners (bm, BFD_LISTEN_EVENT_UPDATE, bs);
 }
 
 static void
@@ -1121,6 +1135,14 @@ bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
 
 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
 
+void
+bfd_register_listener (bfd_notify_fn_t fn)
+{
+  bfd_main_t *bm = &bfd_main;
+
+  vec_add1 (bm->listeners, fn);
+}
+
 /*
  * setup function
  */
@@ -1180,6 +1202,7 @@ bfd_get_session (bfd_main_t * bm, bfd_transport_e t)
 void
 bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
 {
+  bfd_notify_listeners (bm, BFD_LISTEN_EVENT_DELETE, bs);
   if (bs->auth.curr_key)
     {
       --bs->auth.curr_key->use_count;
index d722a55..93adac3 100644 (file)
@@ -66,6 +66,20 @@ typedef enum
 #undef F
 } bfd_poll_state_e;
 
+/**
+ * hop types
+ */
+#define foreach_bfd_hop(F)                     \
+  F (SINGLE, "single")                         \
+  F (MULTI,  "multi")                          \
+
+typedef enum
+{
+#define F(sym, str) BFD_HOP_TYPE_##sym,
+  foreach_bfd_hop (F)
+#undef F
+} bfd_hop_type_e;
+
 typedef struct bfd_session_s
 {
   /** index in bfd_main.sessions pool */
@@ -77,6 +91,9 @@ typedef struct bfd_session_s
   /** remote session state */
   bfd_state_e remote_state;
 
+  /** BFD hop type */
+  bfd_hop_type_e hop_type;
+
   /** local diagnostics */
   bfd_diag_code_e local_diag;
 
@@ -220,6 +237,26 @@ typedef struct bfd_session_s
   };
 } bfd_session_t;
 
+/**
+ * listener events
+ */
+#define foreach_bfd_listen_event(F)            \
+  F (CREATE, "sesion-created")                 \
+  F (UPDATE, "session-updated")                \
+  F (DELETE, "session-deleted")
+
+typedef enum
+{
+#define F(sym, str) BFD_LISTEN_EVENT_##sym,
+  foreach_bfd_listen_event (F)
+#undef F
+} bfd_listen_event_e;
+
+/**
+ * session nitification call back function type
+ */
+typedef void (*bfd_notify_fn_t) (bfd_listen_event_e, const bfd_session_t *);
+
 typedef struct
 {
   /** pool of bfd sessions context data */
@@ -259,6 +296,8 @@ typedef struct
   /** hashmap - index in pool auth_keys by conf_key_id */
   u32 *auth_key_by_conf_key_id;
 
+  /** A vector of callback notification functions */
+  bfd_notify_fn_t *listeners;
 } bfd_main_t;
 
 extern bfd_main_t bfd_main;
@@ -345,6 +384,11 @@ const char *bfd_poll_state_string (bfd_poll_state_e state);
  */
 #define BFD_REQUIRED_MIN_RX_USEC_WHILE_ECHO USEC_PER_SECOND
 
+/**
+ * Register a callback function to receive session notifications.
+ */
+void bfd_register_listener (bfd_notify_fn_t fn);
+
 #endif /* __included_bfd_main_h__ */
 
 /*
index a4adbad..8786810 100644 (file)
 /* *INDENT-OFF* */
 /** identifier of BFD session based on UDP transport only */
 typedef CLIB_PACKED (struct {
-  /** interface to which the session is tied */
-  u32 sw_if_index;
+  union {
+    /** interface to which the session is tied - single-hop */
+    u32 sw_if_index;
+    /** the FIB index the peer is in - multi-hop*/
+    u32 fib_index;
+  };
   /** local address */
   ip46_address_t local_addr;
   /** peer address */
index 715e63e..cc8ebc8 100644 (file)
@@ -514,67 +514,52 @@ fib_attached_export_cover_update (fib_entry_t *fib_entry)
 }
 
 u8*
-fib_ae_import_format (fib_entry_t *fib_entry,
+fib_ae_import_format (fib_node_index_t impi,
                      u8* s)
 {
-    fib_entry_delegate_t *fed;
-
-    fed = fib_entry_delegate_get(fib_entry,
-                                 FIB_ENTRY_DELEGATE_ATTACHED_IMPORT);
-
-    if (NULL != fed)
-    {
-       fib_node_index_t *index;
-       fib_ae_import_t *import;
-
-       import = pool_elt_at_index(fib_ae_import_pool, fed->fd_index);
+    fib_node_index_t *index;
+    fib_ae_import_t *import;
 
-       s = format(s, "\n  Attached-Import:%d:[", (import - fib_ae_import_pool));
-       s = format(s, "export-prefix:%U ", format_fib_prefix, &import->faei_prefix);
-       s = format(s, "export-entry:%d ", import->faei_export_entry);
-       s = format(s, "export-sibling:%d ", import->faei_export_sibling);
-       s = format(s, "exporter:%d ", import->faei_exporter);
-       s = format(s, "export-fib:%d ", import->faei_export_fib);
+    import = pool_elt_at_index(fib_ae_import_pool, impi);
 
-       s = format(s, "import-entry:%d ", import->faei_import_entry);
-       s = format(s, "import-fib:%d ", import->faei_import_fib);
+    s = format(s, "\n  Attached-Import:%d:[", (import - fib_ae_import_pool));
+    s = format(s, "export-prefix:%U ", format_fib_prefix, &import->faei_prefix);
+    s = format(s, "export-entry:%d ", import->faei_export_entry);
+    s = format(s, "export-sibling:%d ", import->faei_export_sibling);
+    s = format(s, "exporter:%d ", import->faei_exporter);
+    s = format(s, "export-fib:%d ", import->faei_export_fib);
+    s = format(s, "import-entry:%d ", import->faei_import_entry);
+    s = format(s, "import-fib:%d ", import->faei_import_fib);
 
-       s = format(s, "importeds:[");
-       vec_foreach(index, import->faei_importeds)
-       {
-           s = format(s, "%d, ", *index);
-       }
-        s = format(s, "]]");
+    s = format(s, "importeds:[");
+    vec_foreach(index, import->faei_importeds)
+    {
+        s = format(s, "%d, ", *index);
     }
+    s = format(s, "]]");
 
     return (s);
 }
 
 u8*
-fib_ae_export_format (fib_entry_t *fib_entry,
+fib_ae_export_format (fib_node_index_t expi,
                      u8* s)
 {
-    fib_entry_delegate_t *fed;
-
-    fed = fib_entry_delegate_get(fib_entry,
-                                 FIB_ENTRY_DELEGATE_ATTACHED_EXPORT);
-
-    if (NULL != fed)
-    {
-        fib_node_index_t *index;
-       fib_ae_export_t *export;
+    fib_node_index_t *index;
+    fib_ae_export_t *export;
 
-       export = pool_elt_at_index(fib_ae_export_pool, fed->fd_list);
+    export = pool_elt_at_index(fib_ae_export_pool, expi);
     
-       s = format(s, "\n  Attached-Export:%d:[", (export - fib_ae_export_pool));
-       s = format(s, "export-entry:%d ", export->faee_ei);
+    s = format(s, "\n  Attached-Export:%d:[", (export - fib_ae_export_pool));
+    s = format(s, "export-entry:%d ", export->faee_ei);
 
-       s = format(s, "importers:[");
-       vec_foreach(index, export->faee_importers)
-       {
-           s = format(s, "%d, ", *index);
-       }
-       s = format(s, "]]");
+    s = format(s, "importers:[");
+    vec_foreach(index, export->faee_importers)
+    {
+        s = format(s, "%d, ", *index);
     }
+    s = format(s, "]]");
+
     return (s);
 }
index fa28a6e..d4c2b57 100644 (file)
@@ -51,7 +51,7 @@ extern void fib_attached_export_covered_removed(fib_entry_t *cover,
 extern void fib_attached_export_cover_change(fib_entry_t *fib_entry);
 extern void fib_attached_export_cover_update(fib_entry_t *fib_entry);
 
-extern u8* fib_ae_import_format(fib_entry_t *fib_entry, u8*s);
-extern u8* fib_ae_export_format(fib_entry_t *fib_entry, u8*s);
+extern u8* fib_ae_import_format(fib_node_index_t impi, u8*s);
+extern u8* fib_ae_export_format(fib_node_index_t expi, u8*s);
 
 #endif
diff --git a/src/vnet/fib/fib_bfd.c b/src/vnet/fib/fib_bfd.c
new file mode 100644 (file)
index 0000000..e5affb8
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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/bfd/bfd_main.h>
+
+#include <vnet/fib/fib_entry_delegate.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_walk.h>
+
+static fib_bfd_state_t
+fib_bfd_bfd_state_to_fib (bfd_state_e bstate)
+{
+    switch (bstate)
+    {
+    case BFD_STATE_up:
+        return (FIB_BFD_STATE_UP);
+    case BFD_STATE_down:
+    case BFD_STATE_admin_down:
+    case BFD_STATE_init:
+        return (FIB_BFD_STATE_DOWN);
+    }
+    return (FIB_BFD_STATE_DOWN);
+}
+
+static void
+fib_bfd_update_walk (fib_node_index_t fei)
+{
+    /*
+     * initiate a backwalk of dependent children
+     * to notify of the state change of this entry.
+     */
+    fib_node_back_walk_ctx_t ctx = {
+        .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
+    };
+    fib_walk_sync(FIB_NODE_TYPE_ENTRY, fei, &ctx);
+}
+
+/**
+ * @brief Callback function registered with BFD module to receive notifications
+ * of the CRUD of BFD sessions
+ * would be static but for the fact it's called from the unit-tests
+ */
+void
+fib_bfd_notify (bfd_listen_event_e event,
+                const bfd_session_t *session)
+{
+    fib_entry_delegate_t *fed;
+    const bfd_udp_key_t *key;
+    fib_node_index_t fei;
+
+    if (BFD_HOP_TYPE_MULTI != session->hop_type)
+    {
+        /*
+         * multi-hop BFD sessions attach directly to the FIB entry
+         * single-hop adj to the associate adjacency.
+         */
+        return;
+    }
+
+    key = &session->udp.key;
+
+    fib_prefix_t pfx = {
+        .fp_addr = key->peer_addr,
+        .fp_proto = (ip46_address_is_ip4 (&key->peer_addr) ?
+                     FIB_PROTOCOL_IP4:
+                     FIB_PROTOCOL_IP6),
+        .fp_len = (ip46_address_is_ip4 (&key->peer_addr) ?
+                   32:
+                   128),
+    };
+
+    /*
+     * get the FIB entry
+     */
+    fei = fib_table_lookup_exact_match(key->fib_index, &pfx);
+
+    switch (event)
+    {
+    case BFD_LISTEN_EVENT_CREATE:
+        /*
+         * The creation of a new session
+         */
+        if ((FIB_NODE_INDEX_INVALID != fei) &&
+            (fed = fib_entry_delegate_get(fib_entry_get(fei),
+                                          FIB_ENTRY_DELEGATE_BFD)))
+        {
+            /*
+             * already got state for this entry
+             */
+        }
+        else
+        {
+            /*
+             * source and lock the entry. add the delegate
+             */
+            fei = fib_table_entry_special_add(key->fib_index,
+                                              &pfx,
+                                              FIB_SOURCE_RR,
+                                              FIB_ENTRY_FLAG_NONE,
+                                              ADJ_INDEX_INVALID);
+            fib_entry_lock(fei);
+
+            fed = fib_entry_delegate_find_or_add(fib_entry_get(fei),
+                                                 FIB_ENTRY_DELEGATE_BFD);
+
+            /*
+             * pretend the session is up and skip the walk.
+             * If we set it down then we get traffic loss on new children.
+             * if we walk then we lose traffic for existing children. Wait
+             * for the first BFD UP/DOWN before we let the session's state
+             * influence forwarding.
+             */
+            fed->fd_bfd_state = FIB_BFD_STATE_UP;
+        }
+        break;
+
+    case BFD_LISTEN_EVENT_UPDATE:
+        /*
+         * state change up/dowm and
+         */
+        ASSERT(FIB_NODE_INDEX_INVALID != fei);
+
+        fed = fib_entry_delegate_get(fib_entry_get(fei),
+                                     FIB_ENTRY_DELEGATE_BFD);
+
+        if (NULL != fed)
+        {
+            fed->fd_bfd_state = fib_bfd_bfd_state_to_fib(session->local_state);
+            fib_bfd_update_walk(fei);
+        }
+        /*
+         * else
+         *   no BFD state
+         */
+        break;
+
+    case BFD_LISTEN_EVENT_DELETE:
+        /*
+         * session has been removed.
+         */
+        if (FIB_NODE_INDEX_INVALID == fei)
+        {
+            /*
+             * no FIB entry
+             */
+        }
+        else if (fib_entry_delegate_get(fib_entry_get(fei),
+                                        FIB_ENTRY_DELEGATE_BFD))
+        {
+            /*
+             * has an associated BFD tracking delegate
+             * usource the entry and remove the BFD tracking deletgate
+             */
+            fib_entry_delegate_remove(fib_entry_get(fei),
+                                      FIB_ENTRY_DELEGATE_BFD);
+            fib_bfd_update_walk(fei);
+
+            fib_table_entry_special_remove(key->fib_index,
+                                           &pfx,
+                                           FIB_SOURCE_RR);
+            fib_entry_unlock(fei);
+        }
+        /*
+         * else
+         * no BFD associated state
+         */
+        break;
+    }
+}
+
+static clib_error_t *
+fib_bfd_main_init (vlib_main_t * vm)
+{
+    clib_error_t * error = NULL;
+
+    if ((error = vlib_call_init_function (vm, bfd_main_init)))
+        return (error);
+
+    bfd_register_listener(fib_bfd_notify);
+
+    return (error);
+}
+
+VLIB_INIT_FUNCTION (fib_bfd_main_init);
index 6ac5461..dac1fce 100644 (file)
@@ -99,7 +99,6 @@ format_fib_entry (u8 * s, va_list * args)
     fib_entry_src_t *src;
     fib_node_index_t fei;
     fib_source_t source;
-    u32 n_covered;
     int level;
 
     fei = va_arg (*args, fib_node_index_t);
@@ -143,14 +142,6 @@ format_fib_entry (u8 * s, va_list * args)
            }
        }));
     
-       n_covered = fib_entry_cover_get_size(fib_entry);
-       if (n_covered > 0) {
-           s = format(s, "\n tracking %d covered: ", n_covered);
-           s = fib_entry_cover_list_format(fib_entry, s);
-       }
-       s = fib_ae_import_format(fib_entry, s);
-       s = fib_ae_export_format(fib_entry, s);
-
        s = format (s, "\n forwarding: ");
     }
     else
@@ -179,20 +170,17 @@ format_fib_entry (u8 * s, va_list * args)
             fib_entry_delegate_type_t fdt;
             fib_entry_delegate_t *fed;
 
-            FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
+            s = format (s, " Delegates:\n");
+            FOR_EACH_DELEGATE(fib_entry, fdt, fed,
             {
-                s = format(s, "  %U-chain\n  %U",
-                           format_fib_forw_chain_type,
-                           fib_entry_delegate_type_to_chain_type(fdt),
-                           format_dpo_id, &fed->fd_dpo, 2);
-                s = format(s, "\n");
+                s = format(s, "  %U\n", format_fib_entry_deletegate, fed);
             });
         }
     }
 
     if (level >= FIB_ENTRY_FORMAT_DETAIL2)
     {
-        s = format(s, "\nchildren:");
+        s = format(s, " Children:");
         s = fib_node_children_format(fib_entry->fe_node.fn_children, s);
     }
 
@@ -1339,6 +1327,36 @@ fib_entry_get_best_source (fib_node_index_t entry_index)
     return (fib_entry_src_get_source(bsrc));
 }
 
+/**
+ * Return !0 is the entry is reoslved, i.e. will return a valid forwarding
+ * chain
+ */
+int
+fib_entry_is_resolved (fib_node_index_t fib_entry_index)
+{
+    fib_entry_delegate_t *fed;
+    fib_entry_t *fib_entry;
+
+    fib_entry = fib_entry_get(fib_entry_index);
+
+    fed = fib_entry_delegate_get(fib_entry, FIB_ENTRY_DELEGATE_BFD);
+
+    if (NULL == fed)
+    {
+        /*
+         * no BFD tracking - resolved
+         */
+        return (!0);
+    }
+    else
+    {
+        /*
+         * defer to the state of the BFD tracking
+         */
+        return (FIB_BFD_STATE_UP == fed->fd_bfd_state);
+    }
+}
+
 static int
 fib_ip4_address_compare (const ip4_address_t * a1,
                          const ip4_address_t * a2)
index 12fa9eb..a3f75e6 100644 (file)
@@ -525,6 +525,7 @@ extern int fib_entry_is_sourced(fib_node_index_t fib_entry_index,
                                 fib_source_t source);
 
 extern fib_node_index_t fib_entry_get_path_list(fib_node_index_t fib_entry_index);
+extern int fib_entry_is_resolved(fib_node_index_t fib_entry_index);
 
 extern void fib_entry_module_init(void);
 
index 147c5da..814df57 100644 (file)
@@ -106,51 +106,6 @@ fib_entry_cover_walk (fib_entry_t *cover,
                        &ctx);
 }
 
-u32
-fib_entry_cover_get_size (fib_entry_t *cover)
-{
-    fib_entry_delegate_t *fed;
-
-    fed = fib_entry_delegate_get(cover, FIB_ENTRY_DELEGATE_COVERED);
-
-    if (NULL == fed)
-        return (0);
-
-    return (fib_node_list_get_size(fed->fd_list));
-}
-
-typedef struct fib_entry_cover_list_format_ctx_t_ {
-    u8 *s;
-} fib_entry_cover_list_format_ctx_t;
-
-static int
-fib_entry_covered_list_format_one (fib_entry_t *cover,
-                                  fib_node_index_t covered,
-                                  void *args)
-{
-    fib_entry_cover_list_format_ctx_t * ctx = args;
-
-    ctx->s = format(ctx->s, "%d, ", covered);
-
-    /* continue */
-    return (1);
-}
-
-u8*
-fib_entry_cover_list_format (fib_entry_t *fib_entry,
-                            u8 *s)
-{
-    fib_entry_cover_list_format_ctx_t ctx = {
-       .s = s,
-    };
-
-    fib_entry_cover_walk(fib_entry, 
-                        fib_entry_covered_list_format_one,
-                        &ctx);
-
-    return (ctx.s);
-}
-
 static int
 fib_entry_cover_change_one (fib_entry_t *cover,
                            fib_node_index_t covered,
index fbbbc21..500d5b3 100644 (file)
@@ -39,9 +39,4 @@ extern void fib_entry_cover_change_notify(fib_node_index_t cover_index,
                                          fib_node_index_t covered_index);
 extern void fib_entry_cover_update_notify(fib_entry_t *cover);
 
-extern u32 fib_entry_cover_get_size(fib_entry_t *cover);
-
-extern u8* fib_entry_cover_list_format(fib_entry_t *fib_entry,
-                                      u8 *s);
-
 #endif
index 70840b1..41af14f 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <vnet/fib/fib_entry_delegate.h>
 #include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_attached_export.h>
 
 static fib_entry_delegate_t *
 fib_entry_delegate_find_i (const fib_entry_t *fib_entry,
@@ -149,8 +150,107 @@ fib_entry_delegate_type_to_chain_type (fib_entry_delegate_type_t fdt)
     case FIB_ENTRY_DELEGATE_COVERED:
     case FIB_ENTRY_DELEGATE_ATTACHED_IMPORT:
     case FIB_ENTRY_DELEGATE_ATTACHED_EXPORT:
+    case FIB_ENTRY_DELEGATE_BFD:
         break;
     }
     ASSERT(0);
     return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
 }
+
+/**
+ * typedef for printing a delegate
+ */
+typedef u8 * (*fib_entry_delegate_format_t)(const fib_entry_delegate_t *fed,
+                                            u8 *s);
+
+/**
+ * Print a delegate that represents a forwarding chain
+ */
+static u8 *
+fib_entry_delegate_fmt_fwd_chain (const fib_entry_delegate_t *fed,
+                                  u8 *s)
+{
+    s = format(s, "%U-chain\n  %U",
+               format_fib_forw_chain_type,
+               fib_entry_delegate_type_to_chain_type(fed->fd_type),
+               format_dpo_id, &fed->fd_dpo, 2);
+
+    return (s);
+}
+
+/**
+ * Print a delegate that represents cover tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_covered (const fib_entry_delegate_t *fed,
+                                  u8 *s)
+{
+    s = format(s, "covered:[");
+    s = fib_node_children_format(fed->fd_list, s);
+    s = format(s, "]");
+
+    return (s);
+}
+
+/**
+ * Print a delegate that represents attached-import tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_import (const fib_entry_delegate_t *fed,
+                               u8 *s)
+{
+    s = format(s, "import:%U", fib_ae_import_format, fed->fd_index);
+
+    return (s);
+}
+
+/**
+ * Print a delegate that represents attached-export tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_export (const fib_entry_delegate_t *fed,
+                               u8 *s)
+{
+    s = format(s, "export:%U", fib_ae_export_format, fed->fd_index);
+
+    return (s);
+}
+
+/**
+ * Print a delegate that represents BFD tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_bfd (const fib_entry_delegate_t *fed,
+                               u8 *s)
+{
+    s = format(s, "BFD:%d", fed->fd_bfd_state);
+
+    return (s);
+}
+
+/**
+ * A delegate type to formatter map
+ */
+static fib_entry_delegate_format_t fed_formatters[] =
+{
+    [FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4] = fib_entry_delegate_fmt_fwd_chain,
+    [FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP6] = fib_entry_delegate_fmt_fwd_chain,
+    [FIB_ENTRY_DELEGATE_CHAIN_MPLS_EOS] = fib_entry_delegate_fmt_fwd_chain,
+    [FIB_ENTRY_DELEGATE_CHAIN_MPLS_NON_EOS] = fib_entry_delegate_fmt_fwd_chain,
+    [FIB_ENTRY_DELEGATE_CHAIN_ETHERNET] = fib_entry_delegate_fmt_fwd_chain,
+    [FIB_ENTRY_DELEGATE_CHAIN_NSH] = fib_entry_delegate_fmt_fwd_chain,
+    [FIB_ENTRY_DELEGATE_COVERED] = fib_entry_delegate_fmt_covered,
+    [FIB_ENTRY_DELEGATE_ATTACHED_IMPORT] = fib_entry_delegate_fmt_import,
+    [FIB_ENTRY_DELEGATE_ATTACHED_EXPORT] = fib_entry_delegate_fmt_export,
+    [FIB_ENTRY_DELEGATE_BFD] = fib_entry_delegate_fmt_bfd,
+};
+
+u8 *
+format_fib_entry_deletegate (u8 * s, va_list * args)
+{
+    fib_entry_delegate_t *fed;
+
+    fed = va_arg (*args, fib_entry_delegate_t *);
+
+    return (fed_formatters[fed->fd_type](fed, s));
+}
index d9183c5..333d357 100644 (file)
@@ -42,6 +42,10 @@ typedef enum fib_entry_delegate_type_t_ {
      * to their respective cover
      */
     FIB_ENTRY_DELEGATE_COVERED,
+    /**
+     * BFD session state
+     */
+    FIB_ENTRY_DELEGATE_BFD,
     /**
      * Attached import/export functionality
      */
@@ -61,6 +65,28 @@ typedef enum fib_entry_delegate_type_t_ {
         }                                                     \
     }                                                         \
 }
+#define FOR_EACH_DELEGATE(_entry, _fdt, _fed, _body)          \
+{                                                             \
+    for (_fdt = FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4;         \
+         _fdt <= FIB_ENTRY_DELEGATE_ATTACHED_EXPORT;          \
+         _fdt++)                                              \
+    {                                                         \
+        _fed = fib_entry_delegate_get(_entry, _fdt);          \
+        if (NULL != _fed) {                                   \
+            _body;                                            \
+        }                                                     \
+    }                                                         \
+}
+
+/**
+ * Distillation of the BFD session states into a go/no-go for using
+ * the associated tracked FIB entry
+ */
+typedef enum fib_bfd_state_t_
+{
+    FIB_BFD_STATE_UP,
+    FIB_BFD_STATE_DOWN,
+} fib_bfd_state_t;
 
 /**
  * A Delagate is a means to implmenet the Delagation design pattern; the extension of an
@@ -103,6 +129,11 @@ typedef struct fib_entry_delegate_t_
          * For the cover tracking. The node list;
          */
         fib_node_list_t fd_list;
+
+        /**
+         * BFD state
+         */
+        fib_bfd_state_t fd_bfd_state;
     };
 } fib_entry_delegate_t;
 
@@ -122,4 +153,6 @@ extern fib_forward_chain_type_t fib_entry_delegate_type_to_chain_type(
 extern fib_entry_delegate_type_t fib_entry_chain_type_to_delegate_type(
      fib_forward_chain_type_t type);
 
+extern u8 *format_fib_entry_deletegate(u8 * s, va_list * args);
+
 #endif
index 928a9d4..6b202a9 100644 (file)
@@ -558,12 +558,6 @@ fib_path_attached_next_hop_set (fib_path_t *path)
      * 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 @@ fib_path_attached_next_hop_set (fib_path_t *path)
     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 @@ fib_path_recursive_adj_update (fib_path_t *path,
             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 @@ FIXME comment
                            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_contribute_urpf (fib_node_index_t path_index,
 {
     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 @@ fib_path_contribute_urpf (fib_node_index_t path_index,
        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:
index 92141dd..3c9b8a3 100644 (file)
@@ -24,6 +24,7 @@
 #include <vnet/dpo/drop_dpo.h>
 #include <vnet/dpo/receive_dpo.h>
 #include <vnet/dpo/ip_null_dpo.h>
+#include <vnet/bfd/bfd_main.h>
 
 #include <vnet/mpls/mpls.h>
 
 #include <vnet/fib/fib_node_list.h>
 #include <vnet/fib/fib_urpf_list.h>
 
+/*
+ * Add debugs for passing tests
+ */
+static int fib_test_do_debug;
+
 #define FIB_TEST_I(_cond, _comment, _args...)                  \
 ({                                                             \
     int _evald = (_cond);                                      \
@@ -40,6 +46,9 @@
        fformat(stderr, "FAIL:%d: " _comment "\n",              \
                __LINE__, ##_args);                             \
     } else {                                                   \
+       if (fib_test_do_debug)                                  \
+            fformat(stderr, "PASS:%d: " _comment "\n",          \
+                    __LINE__, ##_args);                                \
     }                                                          \
     _evald;                                                    \
 })
@@ -6735,6 +6744,509 @@ fib_test_walk (void)
     return (0);
 }
 
+/*
+ * declaration of the otherwise static callback functions
+ */
+void fib_bfd_notify (bfd_listen_event_e event,
+                     const bfd_session_t *session);
+void adj_bfd_notify (bfd_listen_event_e event,
+                     const bfd_session_t *session);
+
+/**
+ * Test BFD session interaction with FIB
+ */
+static int
+fib_test_bfd (void)
+{
+    fib_node_index_t fei;
+    test_main_t *tm;
+    int n_feis;
+
+    /* via 10.10.10.1 */
+    ip46_address_t nh_10_10_10_1 = {
+        .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
+    };
+    /* via 10.10.10.2 */
+    ip46_address_t nh_10_10_10_2 = {
+        .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
+    };
+    /* via 10.10.10.10 */
+    ip46_address_t nh_10_10_10_10 = {
+        .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
+    };
+    n_feis = fib_entry_pool_size();
+
+    tm = &test_main;
+
+    /*
+     * add interface routes. we'll assume this works. it's tested elsewhere
+     */
+    fib_prefix_t pfx_10_10_10_10_s_24 = {
+        .fp_len = 24,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_10,
+    };
+
+    fib_table_entry_update_one_path(0, &pfx_10_10_10_10_s_24,
+                                    FIB_SOURCE_INTERFACE,
+                                    (FIB_ENTRY_FLAG_CONNECTED |
+                                     FIB_ENTRY_FLAG_ATTACHED),
+                                    FIB_PROTOCOL_IP4,
+                                    NULL,
+                                    tm->hw[0]->sw_if_index,
+                                    ~0, // invalid fib index
+                                    1, // weight
+                                    NULL,
+                                    FIB_ROUTE_PATH_FLAG_NONE);
+
+    fib_prefix_t pfx_10_10_10_10_s_32 = {
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_10,
+    };
+    fib_table_entry_update_one_path(0, &pfx_10_10_10_10_s_32,
+                                    FIB_SOURCE_INTERFACE,
+                                    (FIB_ENTRY_FLAG_CONNECTED |
+                                     FIB_ENTRY_FLAG_LOCAL),
+                                    FIB_PROTOCOL_IP4,
+                                    NULL,
+                                    tm->hw[0]->sw_if_index,
+                                    ~0, // invalid fib index
+                                    1, // weight
+                                    NULL,
+                                    FIB_ROUTE_PATH_FLAG_NONE);
+
+    /*
+     * A BFD session via a neighbour we do not yet know
+     */
+    bfd_session_t bfd_10_10_10_1 = {
+        .udp = {
+            .key = {
+                .fib_index = 0,
+                .peer_addr = nh_10_10_10_1,
+            },
+        },
+        .hop_type = BFD_HOP_TYPE_MULTI,
+        .local_state = BFD_STATE_init,
+    };
+
+    fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+    /*
+     * A new entry will be created that forwards via the adj
+     */
+    adj_index_t ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                                    VNET_LINK_IP4,
+                                                    &nh_10_10_10_1,
+                                                    tm->hw[0]->sw_if_index);
+    fib_prefix_t pfx_10_10_10_1_s_32 = {
+        .fp_addr = nh_10_10_10_1,
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+    };
+    fib_test_lb_bucket_t adj_o_10_10_10_1 = {
+        .type = FT_LB_ADJ,
+        .adj = {
+            .adj = ai_10_10_10_1,
+        },
+    };
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     1,
+                                     &adj_o_10_10_10_1),
+             "BFD sourced %U via %U",
+             format_fib_prefix, &pfx_10_10_10_1_s_32,
+             format_ip_adjacency, ai_10_10_10_1, FORMAT_IP_ADJACENCY_NONE);
+
+    /*
+     * Delete the BFD session. Expect the fib_entry to be removed
+     */
+    fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
+    FIB_TEST(FIB_NODE_INDEX_INVALID == fei,
+             "BFD sourced %U removed",
+             format_fib_prefix, &pfx_10_10_10_1_s_32);
+
+    /*
+     * Add the BFD source back
+     */
+    fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+    /*
+     * source the entry via the ADJ fib
+     */
+    fei = fib_table_entry_update_one_path(0,
+                                          &pfx_10_10_10_1_s_32,
+                                          FIB_SOURCE_ADJ,
+                                          FIB_ENTRY_FLAG_ATTACHED,
+                                          FIB_PROTOCOL_IP4,
+                                          &nh_10_10_10_1,
+                                          tm->hw[0]->sw_if_index,
+                                          ~0, // invalid fib index
+                                          1,
+                                          NULL,
+                                          FIB_ROUTE_PATH_FLAG_NONE);
+
+    /*
+     * Delete the BFD session. Expect the fib_entry to remain
+     */
+    fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     1,
+                                     &adj_o_10_10_10_1),
+             "BFD sourced %U remains via %U",
+             format_fib_prefix, &pfx_10_10_10_1_s_32,
+             format_ip_adjacency, ai_10_10_10_1, FORMAT_IP_ADJACENCY_NONE);
+
+    /*
+     * Add the BFD source back
+     */
+    fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+    /*
+     * Create another ADJ FIB
+     */
+    fib_prefix_t pfx_10_10_10_2_s_32 = {
+        .fp_addr = nh_10_10_10_2,
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+    };
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_2_s_32,
+                                    FIB_SOURCE_ADJ,
+                                    FIB_ENTRY_FLAG_ATTACHED,
+                                    FIB_PROTOCOL_IP4,
+                                    &nh_10_10_10_2,
+                                    tm->hw[0]->sw_if_index,
+                                    ~0, // invalid fib index
+                                    1,
+                                    NULL,
+                                    FIB_ROUTE_PATH_FLAG_NONE);
+    /*
+     * A BFD session for the new ADJ FIB
+     */
+    bfd_session_t bfd_10_10_10_2 = {
+        .udp = {
+            .key = {
+                .fib_index = 0,
+                .peer_addr = nh_10_10_10_2,
+            },
+        },
+        .hop_type = BFD_HOP_TYPE_MULTI,
+        .local_state = BFD_STATE_init,
+    };
+
+    fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_2);
+
+    /*
+     * remove the adj-fib source whilst the session is present
+     * then add it back
+     */
+    fib_table_entry_delete(0, &pfx_10_10_10_2_s_32, FIB_SOURCE_ADJ);
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_2_s_32,
+                                    FIB_SOURCE_ADJ,
+                                    FIB_ENTRY_FLAG_ATTACHED,
+                                    FIB_PROTOCOL_IP4,
+                                    &nh_10_10_10_2,
+                                    tm->hw[0]->sw_if_index,
+                                    ~0, // invalid fib index
+                                    1,
+                                    NULL,
+                                    FIB_ROUTE_PATH_FLAG_NONE);
+
+    /*
+     * Before adding a recursive via the BFD tracked ADJ-FIBs,
+     * bring one of the sessions UP, leave the other down
+     */
+    bfd_10_10_10_1.local_state = BFD_STATE_up;
+    fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+    bfd_10_10_10_2.local_state = BFD_STATE_down;
+    fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
+
+    /*
+     * A recursive prefix via both of the ADJ FIBs
+     */
+    fib_prefix_t pfx_200_0_0_0_s_24 = {
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_len = 32,
+        .fp_addr = {
+            .ip4.as_u32 = clib_host_to_net_u32(0xc8000000),
+        },
+    };
+    const dpo_id_t *dpo_10_10_10_1, *dpo_10_10_10_2;
+
+    dpo_10_10_10_1 =
+        fib_entry_contribute_ip_forwarding(
+            fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32));
+    dpo_10_10_10_2 =
+        fib_entry_contribute_ip_forwarding(
+            fib_table_lookup_exact_match(0, &pfx_10_10_10_2_s_32));
+
+    fib_test_lb_bucket_t lb_o_10_10_10_1 = {
+        .type = FT_LB_O_LB,
+        .lb = {
+            .lb = dpo_10_10_10_1->dpoi_index,
+        },
+    };
+    fib_test_lb_bucket_t lb_o_10_10_10_2 = {
+        .type = FT_LB_O_LB,
+        .lb = {
+            .lb = dpo_10_10_10_2->dpoi_index,
+        },
+    };
+
+    /*
+     * A prefix via the adj-fib that is BFD down => DROP
+     */
+    fei = fib_table_entry_path_add(0,
+                                   &pfx_200_0_0_0_s_24,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_NONE,
+                                   FIB_PROTOCOL_IP4,
+                                   &nh_10_10_10_2,
+                                   ~0, // recursive
+                                   0, // default fib index
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+    /*
+     * add a path via the UP BFD adj-fib.
+     *  we expect that the DOWN BFD ADJ FIB is not used.
+     */
+    fei = fib_table_entry_path_add(0,
+                                   &pfx_200_0_0_0_s_24,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_NONE,
+                                   FIB_PROTOCOL_IP4,
+                                   &nh_10_10_10_1,
+                                   ~0, // recursive
+                                   0, // default fib index
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     1,
+                                     &lb_o_10_10_10_1),
+             "Recursive %U only UP BFD adj-fibs",
+             format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+    /*
+     * Send a BFD state change to UP - both sessions are now up
+     *  the recursive prefix should LB over both
+     */
+    bfd_10_10_10_2.local_state = BFD_STATE_up;
+    fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
+
+
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     2,
+                                     &lb_o_10_10_10_1,
+                                     &lb_o_10_10_10_2),
+             "Recursive %U via both UP BFD adj-fibs",
+             format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+    /*
+     * Send a BFD state change to DOWN
+     *  the recursive prefix should exclude the down
+     */
+    bfd_10_10_10_2.local_state = BFD_STATE_down;
+    fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
+
+
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     1,
+                                     &lb_o_10_10_10_1),
+             "Recursive %U via only UP",
+             format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+    /*
+     * Delete the BFD session while it is in the DOWN state.
+     *  FIB should consider the entry's state as back up
+     */
+    fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_2);
+
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     2,
+                                     &lb_o_10_10_10_1,
+                                     &lb_o_10_10_10_2),
+             "Recursive %U via both UP BFD adj-fibs post down session delete",
+             format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+    /*
+     * Delete the BFD other session while it is in the UP state.
+     */
+    fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     2,
+                                     &lb_o_10_10_10_1,
+                                     &lb_o_10_10_10_2),
+             "Recursive %U via both UP BFD adj-fibs post up session delete",
+             format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+    /*
+     * cleaup
+     */
+    fib_table_entry_delete(0, &pfx_200_0_0_0_s_24, FIB_SOURCE_API);
+    fib_table_entry_delete(0, &pfx_10_10_10_1_s_32, FIB_SOURCE_ADJ);
+    fib_table_entry_delete(0, &pfx_10_10_10_2_s_32, FIB_SOURCE_ADJ);
+
+    fib_table_entry_delete(0, &pfx_10_10_10_10_s_32, FIB_SOURCE_INTERFACE);
+    fib_table_entry_delete(0, &pfx_10_10_10_10_s_24, FIB_SOURCE_INTERFACE);
+
+    adj_unlock(ai_10_10_10_1);
+     /*
+     * test no-one left behind
+     */
+    FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
+    FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
+
+    /*
+     * Single-hop BFD tests
+     */
+    bfd_10_10_10_1.hop_type = BFD_HOP_TYPE_SINGLE;
+    bfd_10_10_10_1.udp.key.sw_if_index = tm->hw[0]->sw_if_index;
+
+    adj_bfd_notify(BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+    ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                        VNET_LINK_IP4,
+                                        &nh_10_10_10_1,
+                                        tm->hw[0]->sw_if_index);
+    /*
+     * whilst the BFD session is not signalled, the adj is up
+     */
+    FIB_TEST(adj_is_up(ai_10_10_10_1), "Adj state up on uninit session");
+
+    /*
+     * bring the BFD session up
+     */
+    bfd_10_10_10_1.local_state = BFD_STATE_up;
+    adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+    FIB_TEST(adj_is_up(ai_10_10_10_1), "Adj state up on UP session");
+
+    /*
+     * bring the BFD session down
+     */
+    bfd_10_10_10_1.local_state = BFD_STATE_down;
+    adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+    FIB_TEST(!adj_is_up(ai_10_10_10_1), "Adj state down on DOWN session");
+
+
+    /*
+     * add an attached next hop FIB entry via the down adj
+     */
+    fib_prefix_t pfx_5_5_5_5_s_32 = {
+        .fp_addr = {
+            .ip4 = {
+                .as_u32 = clib_host_to_net_u32(0x05050505),
+            },
+        },
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+    };
+
+    fei = fib_table_entry_path_add(0,
+                                   &pfx_5_5_5_5_s_32,
+                                   FIB_SOURCE_CLI,
+                                   FIB_ENTRY_FLAG_NONE,
+                                   FIB_PROTOCOL_IP4,
+                                   &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0, // invalid fib index
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_5_5_5_5_s_32);
+
+    /*
+     * Add a path via an ADJ that is up
+     */
+    adj_index_t ai_10_10_10_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                                    VNET_LINK_IP4,
+                                                    &nh_10_10_10_2,
+                                                    tm->hw[0]->sw_if_index);
+
+    fib_test_lb_bucket_t adj_o_10_10_10_2 = {
+        .type = FT_LB_ADJ,
+        .adj = {
+            .adj = ai_10_10_10_2,
+        },
+    };
+    adj_o_10_10_10_1.adj.adj = ai_10_10_10_1;
+
+    fei = fib_table_entry_path_add(0,
+                                   &pfx_5_5_5_5_s_32,
+                                   FIB_SOURCE_CLI,
+                                   FIB_ENTRY_FLAG_NONE,
+                                   FIB_PROTOCOL_IP4,
+                                   &nh_10_10_10_2,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0, // invalid fib index
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     1,
+                                     &adj_o_10_10_10_2),
+             "BFD sourced %U via %U",
+             format_fib_prefix, &pfx_5_5_5_5_s_32,
+             format_ip_adjacency, ai_10_10_10_2, FORMAT_IP_ADJACENCY_NONE);
+
+    /*
+     * Bring up the down session - should now LB
+     */
+    bfd_10_10_10_1.local_state = BFD_STATE_up;
+    adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                     2,
+                                     &adj_o_10_10_10_1,
+                                     &adj_o_10_10_10_2),
+             "BFD sourced %U via noth adjs",
+             format_fib_prefix, &pfx_5_5_5_5_s_32);
+
+    /*
+     * remove the BFD session state from the adj
+     */
+    adj_bfd_notify(BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+    /*
+     * clean-up
+     */
+    fib_table_entry_delete(0, &pfx_5_5_5_5_s_32, FIB_SOURCE_CLI);
+    adj_unlock(ai_10_10_10_1);
+    adj_unlock(ai_10_10_10_2);
+
+    /*
+     * test no-one left behind
+     */
+    FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
+    FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
+    return (0);
+}
+
 static int
 lfib_test (void)
 {
@@ -7119,6 +7631,11 @@ fib_test (vlib_main_t * vm,
     res = 0;
     fib_test_mk_intf(4);
 
+    if (unformat (input, "debug"))
+    {
+        fib_test_do_debug = 1;
+    }
+
     if (unformat (input, "ip"))
     {
        res += fib_test_v4();
@@ -7140,6 +7657,10 @@ fib_test (vlib_main_t * vm,
     {
        res += fib_test_walk();
     }
+    else if (unformat (input, "bfd"))
+    {
+       res += fib_test_bfd();
+    }
     else
     {
         /*
@@ -7151,6 +7672,7 @@ fib_test (vlib_main_t * vm,
        res += fib_test_v4();
        res += fib_test_v6();
        res += fib_test_ae();
+       res += fib_test_bfd();
        res += fib_test_label();
        res += lfib_test();
     }
index 98d4e52..b03186e 100644 (file)
@@ -477,12 +477,15 @@ static void
 ip4_fib_table_show_one (ip4_fib_t *fib,
                        vlib_main_t * vm,
                        ip4_address_t *address,
-                       u32 mask_len)
+                       u32 mask_len,
+                        int detail)
 {    
     vlib_cli_output(vm, "%U",
                     format_fib_entry,
                     ip4_fib_table_lookup(fib, address, mask_len),
-                    FIB_ENTRY_FORMAT_DETAIL);
+                    (detail ?
+                     FIB_ENTRY_FORMAT_DETAIL2 :
+                     FIB_ENTRY_FORMAT_DETAIL));
 }
 
 static clib_error_t *
@@ -496,6 +499,7 @@ ip4_show_fib (vlib_main_t * vm,
     ip4_address_t matching_address;
     u32 matching_mask = 32;
     int i, table_id = -1, fib_index = ~0;
+    int detail = 0;
 
     verbose = 1;
     matching = 0;
@@ -506,6 +510,9 @@ ip4_show_fib (vlib_main_t * vm,
            || unformat (input, "sum"))
            verbose = 0;
 
+       else if (unformat (input, "detail") || unformat (input, "det"))
+           detail = 1;
+
        else if (unformat (input, "mtrie"))
            mtrie = 1;
 
@@ -563,7 +570,8 @@ ip4_show_fib (vlib_main_t * vm,
        }
        else
        {
-           ip4_fib_table_show_one(fib, vm, &matching_address, matching_mask);
+           ip4_fib_table_show_one(fib, vm, &matching_address,
+                                   matching_mask, detail);
        }
     }));
 
@@ -717,7 +725,7 @@ ip4_show_fib (vlib_main_t * vm,
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (ip4_show_fib_command, static) = {
     .path = "show ip fib",
-    .short_help = "show ip fib [summary] [table <table-id>] [index <fib-id>] [<ip4-addr>[/<mask>]] [mtrie]",
+    .short_help = "show ip fib [summary] [table <table-id>] [index <fib-id>] [<ip4-addr>[/<mask>]] [mtrie] [detail]",
     .function = ip4_show_fib,
 };
 /* *INDENT-ON* */
index 0ee029d..0029714 100644 (file)
@@ -560,12 +560,15 @@ static void
 ip6_fib_table_show_one (ip6_fib_t *fib,
                        vlib_main_t * vm,
                        ip6_address_t *address,
-                       u32 mask_len)
+                       u32 mask_len,
+                        int detail)
 {
     vlib_cli_output(vm, "%U",
                     format_fib_entry,
                     ip6_fib_table_lookup(fib->index, address, mask_len),
-                    FIB_ENTRY_FORMAT_DETAIL);
+                    (detail ?
+                     FIB_ENTRY_FORMAT_DETAIL2:
+                     FIB_ENTRY_FORMAT_DETAIL));
 }
 
 typedef struct {
@@ -573,8 +576,9 @@ typedef struct {
   u64 count_by_prefix_length[129];
 } count_routes_in_fib_at_prefix_length_arg_t;
 
-static void count_routes_in_fib_at_prefix_length 
-(BVT(clib_bihash_kv) * kvp, void *arg)
+static void
+count_routes_in_fib_at_prefix_length (BVT(clib_bihash_kv) * kvp,
+                                      void *arg)
 {
   count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
   int mask_width;
@@ -600,6 +604,7 @@ ip6_show_fib (vlib_main_t * vm,
     ip6_address_t matching_address;
     u32 mask_len  = 128;
     int table_id = -1, fib_index = ~0;
+    int detail = 0;
 
     verbose = 1;
     matching = 0;
@@ -610,6 +615,10 @@ ip6_show_fib (vlib_main_t * vm,
            unformat (input, "summary") ||
            unformat (input, "sum"))
            verbose = 0;
+       else if (unformat (input, "detail")   ||
+                 unformat (input, "det"))
+           detail = 1;
 
        else if (unformat (input, "%U/%d",
                           unformat_ip6_address, &matching_address, &mask_len))
@@ -667,7 +676,7 @@ ip6_show_fib (vlib_main_t * vm,
        }
        else
        {
-           ip6_fib_table_show_one(fib, vm, &matching_address, mask_len);
+           ip6_fib_table_show_one(fib, vm, &matching_address, mask_len, detail);
        }
     }));
 
@@ -771,7 +780,7 @@ ip6_show_fib (vlib_main_t * vm,
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (ip6_show_fib_command, static) = {
     .path = "show ip6 fib",
-    .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]]",
+    .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]] [detail]",
     .function = ip6_show_fib,
 };
 /* *INDENT-ON* */
index fbd21d2..ce70af2 100644 (file)
@@ -359,7 +359,7 @@ class VppTestCase(unittest.TestCase):
                            self._testMethodDoc))
         if not self.vpp_dead:
             self.logger.debug(self.vapi.cli("show trace"))
-            self.logger.info(self.vapi.ppcli("show interfaces"))
+            self.logger.info(self.vapi.ppcli("show interface"))
             self.logger.info(self.vapi.ppcli("show hardware"))
             self.logger.info(self.vapi.ppcli("show error"))
             self.logger.info(self.vapi.ppcli("show run"))
index c9d0abd..e8f8f33 100644 (file)
@@ -16,9 +16,10 @@ from scapy.layers.inet6 import IPv6
 from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \
     BFDDiagCode, BFDState, BFD_vpp_echo
 from framework import VppTestCase, VppTestRunner, running_extended_tests
-from vpp_pg_interface import CaptureTimeoutError
+from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc
 from util import ppp
 from vpp_papi_provider import UnexpectedApiReturnValueError
+from vpp_ip_route import VppIpRoute, VppRoutePath
 
 USEC_IN_SEC = 1000000
 
@@ -1582,6 +1583,107 @@ class BFD6TestCase(VppTestCase):
             self.test_session.send_packet()
 
 
+class BFDFIBTestCase(VppTestCase):
+    """ BFD-FIB interactions (IPv6) """
+
+    vpp_session = None
+    test_session = None
+
+    def setUp(self):
+        super(BFDFIBTestCase, self).setUp()
+        self.create_pg_interfaces(range(1))
+
+        self.vapi.want_bfd_events()
+        self.pg0.enable_capture()
+
+        for i in self.pg_interfaces:
+            i.admin_up()
+            i.config_ip6()
+            i.configure_ipv6_neighbors()
+
+    def tearDown(self):
+        if not self.vpp_dead:
+            self.vapi.want_bfd_events(enable_disable=0)
+
+        super(BFDFIBTestCase, self).tearDown()
+
+    @staticmethod
+    def pkt_is_not_data_traffic(p):
+        """ not data traffic implies BFD or the usual IPv6 ND/RA"""
+        if p.haslayer(BFD) or is_ipv6_misc(p):
+            return True
+        return False
+
+    def test_session_with_fib(self):
+        """ BFD-FIB interactions """
+
+        # packets to match against both of the routes
+        p = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IPv6(src="3001::1", dst="2001::1") /
+             UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100)),
+             (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+              IPv6(src="3001::1", dst="2002::1") /
+              UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100))]
+
+        # A recursive and a non-recursive route via a next-hop that
+        # will have a BFD session
+        ip_2001_s_64 = VppIpRoute(self, "2001::", 64,
+                                  [VppRoutePath(self.pg0.remote_ip6,
+                                                self.pg0.sw_if_index,
+                                                is_ip6=1)],
+                                  is_ip6=1)
+        ip_2002_s_64 = VppIpRoute(self, "2002::", 64,
+                                  [VppRoutePath(self.pg0.remote_ip6,
+                                                0xffffffff,
+                                                is_ip6=1)],
+                                  is_ip6=1)
+        ip_2001_s_64.add_vpp_config()
+        ip_2002_s_64.add_vpp_config()
+
+        # bring the session up now the routes are present
+        self.vpp_session = VppBFDUDPSession(self,
+                                            self.pg0,
+                                            self.pg0.remote_ip6,
+                                            af=AF_INET6)
+        self.vpp_session.add_vpp_config()
+        self.vpp_session.admin_up()
+        self.test_session = BFDTestSession(self, self.pg0, AF_INET6)
+
+        # session is up - traffic passes
+        bfd_session_up(self)
+
+        self.pg0.add_stream(p)
+        self.pg_start()
+        for packet in p:
+            captured = self.pg0.wait_for_packet(
+                1,
+                filter_out_fn=self.pkt_is_not_data_traffic)
+            self.assertEqual(captured[IPv6].dst,
+                             packet[IPv6].dst)
+
+        # session is up - traffic is dropped
+        bfd_session_down(self)
+
+        self.pg0.add_stream(p)
+        self.pg_start()
+        with self.assertRaises(CaptureTimeoutError):
+            self.pg0.wait_for_packet(1, self.pkt_is_not_data_traffic)
+
+        # session is up - traffic passes
+        bfd_session_up(self)
+
+        self.pg0.add_stream(p)
+        self.pg_start()
+        for packet in p:
+            captured = self.pg0.wait_for_packet(
+                1,
+                filter_out_fn=self.pkt_is_not_data_traffic)
+            self.assertEqual(captured[IPv6].dst,
+                             packet[IPv6].dst)
+
+
 class BFDSHA1TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """
 
index 7f9e2ae..e8025df 100644 (file)
@@ -152,8 +152,9 @@ class VppPapiProvider(object):
                 raise UnexpectedApiReturnValueError(msg)
         elif self._expect_api_retval == self._zero:
             if hasattr(reply, 'retval') and reply.retval != expected_retval:
-                msg = "API call failed, expected zero return value instead "\
-                    "of %d in %s" % (expected_retval, repr(reply))
+                msg = "API call failed, expected %d return value instead "\
+                    "of %d in %s" % (expected_retval, reply.retval,
+                                     repr(reply))
                 self.test_class.logger.info(msg)
                 raise UnexpectedApiReturnValueError(msg)
         else: