BFD: add ARP-awareness, fix bugs
[vpp.git] / src / vnet / bfd / bfd_udp.c
index 146faad..ebee590 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/**
+ * @file
+ * @brief BFD UDP transport layer implementation
+ */
 #include <vppinfra/types.h>
 #include <vlibmemory/api.h>
 #include <vlib/vlib.h>
 #include <vlib/buffer.h>
 #include <vnet/ip/format.h>
 #include <vnet/ethernet/packet.h>
-#include <vnet/ip/udp_packet.h>
+#include <vnet/udp/udp_packet.h>
+#include <vnet/udp/udp.h>
 #include <vnet/ip/lookup.h>
 #include <vnet/ip/icmp46_packet.h>
 #include <vnet/ip/ip4.h>
 #include <vnet/ip/ip6.h>
-#include <vnet/ip/udp.h>
 #include <vnet/ip/ip6_packet.h>
 #include <vnet/adj/adj.h>
 #include <vnet/adj/adj_nbr.h>
@@ -47,6 +51,14 @@ typedef struct
   int echo_source_is_set;
   /* loopback interface used to get echo source ip */
   u32 echo_source_sw_if_index;
+  /* node index of "ip4-arp" node */
+  u32 ip4_arp_idx;
+  /* node index of "ip6-discover-neighbor" node */
+  u32 ip6_ndp_idx;
+  /* node index of "ip4-rewrite" node */
+  u32 ip4_rewrite_idx;
+  /* node index of "ip6-rewrite" node */
+  u32 ip6_rewrite_idx;
 } bfd_udp_main_t;
 
 static vlib_node_registration_t bfd_udp4_input_node;
@@ -60,8 +72,7 @@ vnet_api_error_t
 bfd_udp_set_echo_source (u32 sw_if_index)
 {
   vnet_sw_interface_t *sw_if =
-    vnet_get_sw_interface_safe (bfd_udp_main.vnet_main,
-                               bfd_udp_main.echo_source_sw_if_index);
+    vnet_get_sw_interface_safe (bfd_udp_main.vnet_main, sw_if_index);
   if (sw_if)
     {
       bfd_udp_main.echo_source_sw_if_index = sw_if_index;
@@ -84,6 +95,7 @@ bfd_udp_is_echo_available (bfd_transport_e transport)
 {
   if (!bfd_udp_main.echo_source_is_set)
     {
+      BFD_DBG ("UDP echo source not set - echo not available");
       return 0;
     }
   /*
@@ -127,6 +139,7 @@ bfd_udp_is_echo_available (bfd_transport_e transport)
           /* *INDENT-ON* */
        }
     }
+  BFD_DBG ("No usable IP address for UDP echo - echo not available");
   return 0;
 }
 
@@ -144,11 +157,6 @@ bfd_udp_bs_idx_to_sport (u32 bs_idx)
   return 49152 + bs_idx % (65535 - 49152 + 1);
 }
 
-static void
-lol ()
-{
-}
-
 int
 bfd_udp_get_echo_src_ip4 (ip4_address_t * addr)
 {
@@ -204,7 +212,6 @@ bfd_udp_get_echo_src_ip6 (ip6_address_t * addr)
           {
             *addr = *x;
             addr->as_u8[15] ^= 1; /* flip the last bit of the address */
-            lol ();
             return 1;
           }
       }));
@@ -213,16 +220,37 @@ bfd_udp_get_echo_src_ip6 (ip6_address_t * addr)
   return 0;
 }
 
+void
+bfd_udp_get_echo_source (int *is_set, u32 * sw_if_index, int *have_usable_ip4,
+                        ip4_address_t * ip4, int *have_usable_ip6,
+                        ip6_address_t * ip6)
+{
+  if (bfd_udp_main.echo_source_is_set)
+    {
+      *is_set = 1;
+      *sw_if_index = bfd_udp_main.echo_source_sw_if_index;
+      *have_usable_ip4 = bfd_udp_get_echo_src_ip4 (ip4);
+      *have_usable_ip6 = bfd_udp_get_echo_src_ip6 (ip6);
+    }
+  else
+    {
+      *is_set = 0;
+    }
+}
+
 int
-bfd_add_udp4_transport (vlib_main_t * vm, vlib_buffer_t * b,
+bfd_add_udp4_transport (vlib_main_t * vm, u32 bi,
                        const bfd_session_t * bs, int is_echo)
 {
   const bfd_udp_session_t *bus = &bs->udp;
   const bfd_udp_key_t *key = &bus->key;
+  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
 
   b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED;
   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
+  vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
+  vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
   typedef struct
   {
     ip4_header_t ip4;
@@ -266,15 +294,18 @@ bfd_add_udp4_transport (vlib_main_t * vm, vlib_buffer_t * b,
 }
 
 int
-bfd_add_udp6_transport (vlib_main_t * vm, vlib_buffer_t * b,
+bfd_add_udp6_transport (vlib_main_t * vm, u32 bi,
                        const bfd_session_t * bs, int is_echo)
 {
   const bfd_udp_session_t *bus = &bs->udp;
   const bfd_udp_key_t *key = &bus->key;
+  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
 
   b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED;
   vnet_buffer (b)->ip.adj_index[VLIB_RX] = bus->adj_index;
   vnet_buffer (b)->ip.adj_index[VLIB_TX] = bus->adj_index;
+  vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
+  vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
   typedef struct
   {
     ip6_header_t ip6;
@@ -329,6 +360,76 @@ bfd_add_udp6_transport (vlib_main_t * vm, vlib_buffer_t * b,
   return 1;
 }
 
+static void
+bfd_create_frame_to_next_node (vlib_main_t * vm, u32 bi, u32 next_node)
+{
+  vlib_frame_t *f = vlib_get_frame_to_node (vm, next_node);
+  u32 *to_next = vlib_frame_vector_args (f);
+  to_next[0] = bi;
+  f->n_vectors = 1;
+  vlib_put_frame_to_node (vm, next_node, f);
+}
+
+int
+bfd_udp_calc_next_node (const struct bfd_session_s *bs, u32 * next_node)
+{
+  const bfd_udp_session_t *bus = &bs->udp;
+  ip_adjacency_t *adj = adj_get (bus->adj_index);
+  switch (adj->lookup_next_index)
+    {
+    case IP_LOOKUP_NEXT_ARP:
+      switch (bs->transport)
+       {
+       case BFD_TRANSPORT_UDP4:
+         *next_node = bfd_udp_main.ip4_arp_idx;
+         return 1;
+       case BFD_TRANSPORT_UDP6:
+         *next_node = bfd_udp_main.ip6_ndp_idx;
+         return 1;
+       }
+      break;
+    case IP_LOOKUP_NEXT_REWRITE:
+      switch (bs->transport)
+       {
+       case BFD_TRANSPORT_UDP4:
+         *next_node = bfd_udp_main.ip4_rewrite_idx;
+         return 1;
+       case BFD_TRANSPORT_UDP6:
+         *next_node = bfd_udp_main.ip6_rewrite_idx;
+         return 1;
+       }
+      break;
+    default:
+      /* drop */
+      break;
+    }
+  return 0;
+}
+
+int
+bfd_transport_udp4 (vlib_main_t * vm, u32 bi, const struct bfd_session_s *bs)
+{
+  u32 next_node;
+  int rv = bfd_udp_calc_next_node (bs, &next_node);
+  if (rv)
+    {
+      bfd_create_frame_to_next_node (vm, bi, next_node);
+    }
+  return rv;
+}
+
+int
+bfd_transport_udp6 (vlib_main_t * vm, u32 bi, const struct bfd_session_s *bs)
+{
+  u32 next_node;
+  int rv = bfd_udp_calc_next_node (bs, &next_node);
+  if (rv)
+    {
+      bfd_create_frame_to_next_node (vm, bi, next_node);
+    }
+  return 1;
+}
+
 static bfd_session_t *
 bfd_lookup_session (bfd_udp_main_t * bum, const bfd_udp_key_t * key)
 {
@@ -398,21 +499,6 @@ bfd_udp_add_session_internal (bfd_udp_main_t * bum, u32 sw_if_index,
       BFD_DBG ("adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, VNET_LINK_IP4, %U, %d) "
               "returns %d", format_ip46_address, &key->peer_addr,
               IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
-
-      fib_prefix_t fib_prefix;
-      memset (&fib_prefix, 0, sizeof (fib_prefix));
-      fib_prefix.fp_len = 0;
-      fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
-      fib_prefix.fp_addr = key->local_addr;
-      u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, 0);    /* FIXME table id 0? */
-      dpo_id_t dpo = DPO_INVALID;
-      dpo_proto_t dproto;
-      dproto = fib_proto_to_dpo (fib_prefix.fp_proto);
-      receive_dpo_add_or_lock (dproto, ~0, NULL, &dpo);
-      fib_table_entry_special_dpo_update (fib_index, &fib_prefix,
-                                         FIB_SOURCE_API,
-                                         FIB_ENTRY_FLAG_LOCAL, &dpo);
-      dpo_reset (&dpo);
     }
   else
     {
@@ -660,97 +746,6 @@ bfd_udp_session_set_flags (u32 sw_if_index,
   return 0;
 }
 
-vnet_api_error_t
-bfd_auth_set_key (u32 conf_key_id, u8 auth_type, u8 key_len,
-                 const u8 * key_data)
-{
-#if WITH_LIBSSL > 0
-  bfd_auth_key_t *auth_key = NULL;
-  if (!key_len || key_len > bfd_max_len_for_auth_type (auth_type))
-    {
-      clib_warning ("Invalid authentication key length for auth_type=%d:%s "
-                   "(key_len=%u, must be "
-                   "non-zero, expected max=%u)",
-                   auth_type, bfd_auth_type_str (auth_type), key_len,
-                   (u32) bfd_max_len_for_auth_type (auth_type));
-      return VNET_API_ERROR_INVALID_VALUE;
-    }
-  if (!bfd_auth_type_supported (auth_type))
-    {
-      clib_warning ("Unsupported auth type=%d:%s", auth_type,
-                   bfd_auth_type_str (auth_type));
-      return VNET_API_ERROR_BFD_NOTSUPP;
-    }
-  bfd_main_t *bm = bfd_udp_main.bfd_main;
-  uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
-  if (key_idx_p)
-    {
-      /* modifying existing key - must not be used */
-      const uword key_idx = *key_idx_p;
-      auth_key = pool_elt_at_index (bm->auth_keys, key_idx);
-      if (auth_key->use_count > 0)
-       {
-         clib_warning ("Authentication key with conf ID %u in use by %u BFD "
-                       "sessions - cannot modify",
-                       conf_key_id, auth_key->use_count);
-         return VNET_API_ERROR_BFD_EINUSE;
-       }
-    }
-  else
-    {
-      /* adding new key */
-      pool_get (bm->auth_keys, auth_key);
-      auth_key->conf_key_id = conf_key_id;
-      hash_set (bm->auth_key_by_conf_key_id, conf_key_id,
-               auth_key - bm->auth_keys);
-    }
-  auth_key->auth_type = auth_type;
-  memset (auth_key->key, 0, sizeof (auth_key->key));
-  clib_memcpy (auth_key->key, key_data, key_len);
-  return 0;
-#else
-  clib_warning ("SSL missing, cannot manipulate authentication keys");
-  return VNET_API_ERROR_BFD_NOTSUPP;
-#endif
-}
-
-vnet_api_error_t
-bfd_auth_del_key (u32 conf_key_id)
-{
-#if WITH_LIBSSL > 0
-  bfd_auth_key_t *auth_key = NULL;
-  bfd_main_t *bm = bfd_udp_main.bfd_main;
-  uword *key_idx_p = hash_get (bm->auth_key_by_conf_key_id, conf_key_id);
-  if (key_idx_p)
-    {
-      /* deleting existing key - must not be used */
-      const uword key_idx = *key_idx_p;
-      auth_key = pool_elt_at_index (bm->auth_keys, key_idx);
-      if (auth_key->use_count > 0)
-       {
-         clib_warning ("Authentication key with conf ID %u in use by %u BFD "
-                       "sessions - cannot delete",
-                       conf_key_id, auth_key->use_count);
-         return VNET_API_ERROR_BFD_EINUSE;
-       }
-      hash_unset (bm->auth_key_by_conf_key_id, conf_key_id);
-      memset (auth_key, 0, sizeof (*auth_key));
-      pool_put (bm->auth_keys, auth_key);
-    }
-  else
-    {
-      /* no such key */
-      clib_warning ("Authentication key with conf ID %u does not exist",
-                   conf_key_id);
-      return VNET_API_ERROR_BFD_ENOENT;
-    }
-  return 0;
-#else
-  clib_warning ("SSL missing, cannot manipulate authentication keys");
-  return VNET_API_ERROR_BFD_NOTSUPP;
-#endif
-}
-
 vnet_api_error_t
 bfd_udp_auth_activate (u32 sw_if_index,
                       const ip46_address_t * local_addr,
@@ -792,7 +787,8 @@ bfd_udp_auth_deactivate (u32 sw_if_index,
 typedef enum
 {
   BFD_UDP_INPUT_NEXT_NORMAL,
-  BFD_UDP_INPUT_NEXT_REPLY,
+  BFD_UDP_INPUT_NEXT_REPLY_ARP,
+  BFD_UDP_INPUT_NEXT_REPLY_REWRITE,
   BFD_UDP_INPUT_N_NEXT,
 } bfd_udp_input_next_t;
 
@@ -1201,8 +1197,11 @@ bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
          const bfd_pkt_t *pkt = vlib_buffer_get_current (b0);
          if (bfd_pkt_get_poll (pkt))
            {
+             b0->current_data = 0;
+             b0->current_length = 0;
+             memset (vnet_buffer (b0), 0, sizeof (*vnet_buffer (b0)));
              bfd_init_final_control_frame (vm, b0, bfd_udp_main.bfd_main,
-                                           bs);
+                                           bs, 0);
              if (is_ipv6)
                {
                  vlib_node_increment_counter (vm, bfd_udp6_input_node.index,
@@ -1213,7 +1212,20 @@ bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
                  vlib_node_increment_counter (vm, bfd_udp4_input_node.index,
                                               b0->error, 1);
                }
-             next0 = BFD_UDP_INPUT_NEXT_REPLY;
+             const bfd_udp_session_t *bus = &bs->udp;
+             ip_adjacency_t *adj = adj_get (bus->adj_index);
+             switch (adj->lookup_next_index)
+               {
+               case IP_LOOKUP_NEXT_ARP:
+                 next0 = BFD_UDP_INPUT_NEXT_REPLY_ARP;
+                 break;
+               case IP_LOOKUP_NEXT_REWRITE:
+                 next0 = BFD_UDP_INPUT_NEXT_REPLY_REWRITE;
+                 break;
+               default:
+                 /* drop */
+                 break;
+               }
            }
        }
       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
@@ -1250,7 +1262,8 @@ VLIB_REGISTER_NODE (bfd_udp4_input_node, static) = {
   .next_nodes =
       {
               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
-              [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
+              [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
+              [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
       },
 };
 /* *INDENT-ON* */
@@ -1277,7 +1290,8 @@ VLIB_REGISTER_NODE (bfd_udp6_input_node, static) = {
   .next_nodes =
       {
               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
-              [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
+              [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
+              [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
       },
 };
 /* *INDENT-ON* */
@@ -1335,7 +1349,7 @@ bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
              vlib_node_increment_counter (vm, bfd_udp_echo4_input_node.index,
                                           b0->error, 1);
            }
-         next0 = BFD_UDP_INPUT_NEXT_REPLY;
+         next0 = BFD_UDP_INPUT_NEXT_REPLY_REWRITE;
        }
 
       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
@@ -1389,7 +1403,8 @@ VLIB_REGISTER_NODE (bfd_udp_echo4_input_node, static) = {
   .next_nodes =
       {
               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
-              [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
+              [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip4-arp",
+              [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip4-lookup",
       },
 };
 /* *INDENT-ON* */
@@ -1417,7 +1432,8 @@ VLIB_REGISTER_NODE (bfd_udp_echo6_input_node, static) = {
   .next_nodes =
       {
               [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
-              [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
+              [BFD_UDP_INPUT_NEXT_REPLY_ARP] = "ip6-discover-neighbor",
+              [BFD_UDP_INPUT_NEXT_REPLY_REWRITE] = "ip6-lookup",
       },
 };
 
@@ -1464,6 +1480,19 @@ bfd_udp_init (vlib_main_t * vm)
                         bfd_udp_echo4_input_node.index, 1);
   udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo6,
                         bfd_udp_echo6_input_node.index, 0);
+  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "ip4-arp");
+  ASSERT (node);
+  bfd_udp_main.ip4_arp_idx = node->index;
+  node = vlib_get_node_by_name (vm, (u8 *) "ip6-discover-neighbor");
+  ASSERT (node);
+  bfd_udp_main.ip6_ndp_idx = node->index;
+  node = vlib_get_node_by_name (vm, (u8 *) "ip4-rewrite");
+  ASSERT (node);
+  bfd_udp_main.ip4_rewrite_idx = node->index;
+  node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
+  ASSERT (node);
+  bfd_udp_main.ip6_rewrite_idx = node->index;
+
   return 0;
 }