ip: add support for buffer offload metadata in ip midchain
[vpp.git] / src / vnet / adj / adj_bfd.c
index 3d294c4..e54ba6d 100644 (file)
 #include <vnet/adj/adj_nbr.h>
 #include <vnet/fib/fib_walk.h>
 
+/**
+ * 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;
+
+#define ADJ_BFD_STATES {                        \
+    [ADJ_BFD_STATE_DOWN] = "down",              \
+    [ADJ_BFD_STATE_UP]   = "up",                \
+}
+
+static const char *adj_bfd_state_names[] = ADJ_BFD_STATES;
+
+/**
+ * BFD delegate daa
+ */
+typedef struct adj_bfd_delegate_t_
+{
+    /**
+     * BFD session state
+     */
+    adj_bfd_state_t abd_state;
+
+    /**
+     * BFD session index
+     */
+    u32 abd_index;
+} adj_bfd_delegate_t;
+
+/**
+ * Pool of delegates
+*/
+static adj_bfd_delegate_t *abd_pool;
+
+static inline adj_bfd_delegate_t*
+adj_bfd_from_base (adj_delegate_t *ad)
+{
+    if (NULL != ad)
+    {
+        return (pool_elt_at_index(abd_pool, ad->ad_index));
+    }
+    return (NULL);
+}
+
+static inline const adj_bfd_delegate_t*
+adj_bfd_from_const_base (const adj_delegate_t *ad)
+{
+    if (NULL != ad)
+    {
+        return (pool_elt_at_index(abd_pool, ad->ad_index));
+    }
+    return (NULL);
+}
+
 static adj_bfd_state_t
 adj_bfd_bfd_state_to_fib (bfd_state_e bstate)
 {
@@ -56,8 +114,7 @@ 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_bfd_delegate_t *abd;
     adj_delegate_t *aed;
     adj_index_t ai;
 
@@ -70,19 +127,28 @@ adj_bfd_notify (bfd_listen_event_e event,
         return;
     }
 
-    key = &session->udp.key;
-
-    fproto = (ip46_address_is_ip4 (&key->peer_addr) ?
-              FIB_PROTOCOL_IP4:
-              FIB_PROTOCOL_IP6);
+    switch (session->transport)
+    {
+    case BFD_TRANSPORT_UDP4:
+    case BFD_TRANSPORT_UDP6:
+        /*
+         * pick up the same adjacency that the BFD session is using
+         * to send. The BFD session is holding a lock on this adj.
+         */
+        ai = session->udp.adj_index;
+        break;
+    default:
+        /*
+         * Don't know what adj this session uses
+         */
+        return;
+    }
 
-    /*
-     * 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);
+    if (INDEX_INVALID == ai)
+    {
+        /* No associated Adjacency with the session */
+        return;
+    }
 
     switch (event)
     {
@@ -101,35 +167,34 @@ adj_bfd_notify (bfd_listen_event_e event,
         else
         {
             /*
-             * lock the adj. add the delegate.
-             * Lockinging the adj prevents it being removed and thus maintains
-             * the BFD derived states
+             * allocate and init a new delegate struct
              */
-            adj_lock(ai);
-
-            aed = adj_delegate_find_or_add(adj_get(ai), ADJ_DELEGATE_BFD);
+            pool_get(abd_pool, abd);
 
             /*
-             * 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.
+             * it would be best here if we could ignore this create and just
+             * wait for the first update, but this is not possible because
+             * BFD sessions are created in the down state, and can remain this
+             * way without transitioning to another state if the peer is
+             * unresponsive. So we have to assume down and wait for up.
              */
-            aed->ad_bfd_state = ADJ_BFD_STATE_UP;
-            aed->ad_bfd_index = session->bs_idx;
+            abd->abd_state = ADJ_BFD_STATE_DOWN;
+            abd->abd_index = session->bs_idx;
+
+            adj_delegate_add(adj_get(ai), ADJ_DELEGATE_BFD, abd - abd_pool);
+            adj_bfd_update_walk(ai);
         }
         break;
 
     case BFD_LISTEN_EVENT_UPDATE:
         /*
-         * state change up/dowm and
+         * state change up/down and
          */
-        aed = adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD);
+        abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
 
-        if (NULL != aed)
+        if (NULL != abd)
         {
-            aed->ad_bfd_state = adj_bfd_bfd_state_to_fib(session->local_state);
+            abd->abd_state = adj_bfd_bfd_state_to_fib(session->local_state);
             adj_bfd_update_walk(ai);
         }
         /*
@@ -142,18 +207,18 @@ adj_bfd_notify (bfd_listen_event_e event,
         /*
          * session has been removed.
          */
+        abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
 
-        if (adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD))
+        if (NULL != abd)
         {
             /*
              * has an associated BFD tracking delegate
-             * remove the BFD tracking deletgate, update children, then
-             * unlock the adj
+             * remove the BFD tracking delegate, update children
              */
-            adj_delegate_remove(adj_get(ai), ADJ_DELEGATE_BFD);
+            adj_delegate_remove(ai, ADJ_DELEGATE_BFD);
+            pool_put(abd_pool, abd);
 
             adj_bfd_update_walk(ai);
-            adj_unlock(ai);
         }
         /*
          * else
@@ -161,24 +226,61 @@ adj_bfd_notify (bfd_listen_event_e event,
          */
         break;
     }
+}
 
-    /*
-     * unlock match of the add-or-lock at the start
-     */
-    adj_unlock(ai);
+int
+adj_bfd_is_up (adj_index_t ai)
+{
+    const adj_bfd_delegate_t *abd;
+
+    abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
+
+    if (NULL == abd)
+    {
+        /*
+         * no BFD tracking - resolved
+         */
+        return (!0);
+    }
+    else
+    {
+        /*
+         * defer to the state of the BFD tracking
+         */
+        return (ADJ_BFD_STATE_UP == abd->abd_state);
+    }
 }
 
-static clib_error_t *
-adj_bfd_main_init (vlib_main_t * vm)
+/**
+ * Print a delegate that represents BFD tracking
+ */
+static u8 *
+adj_delegate_fmt_bfd (const adj_delegate_t *aed, u8 *s)
 {
-    clib_error_t * error = NULL;
+    const adj_bfd_delegate_t *abd = adj_bfd_from_const_base(aed);
 
-    if ((error = vlib_call_init_function (vm, bfd_main_init)))
-        return (error);
+    s = format(s, "BFD:[state:%s index:%d]",
+               adj_bfd_state_names[abd->abd_state],
+               abd->abd_index);
 
+    return (s);
+}
+
+const static adj_delegate_vft_t adj_delegate_vft = {
+  .adv_format = adj_delegate_fmt_bfd,
+};
+
+static clib_error_t *
+adj_bfd_main_init (vlib_main_t * vm)
+{
     bfd_register_listener(adj_bfd_notify);
 
-    return (error);
+    adj_delegate_register_type (ADJ_DELEGATE_BFD, &adj_delegate_vft);
+
+    return (0);
 }
 
-VLIB_INIT_FUNCTION (adj_bfd_main_init);
+VLIB_INIT_FUNCTION (adj_bfd_main_init)=
+{
+    .runs_after = VLIB_INITS("bfd_main_init"),
+};