L2 BD: introduce a BD interface on which to send UU packets
[vpp.git] / src / vnet / lisp-gpe / interface.c
index 3288b24..1ccb53d 100644 (file)
@@ -23,7 +23,7 @@
 #include <vppinfra/hash.h>
 #include <vnet/vnet.h>
 #include <vnet/ip/ip.h>
-#include <vnet/ip/udp.h>
+#include <vnet/udp/udp.h>
 #include <vnet/ethernet/ethernet.h>
 #include <vnet/lisp-gpe/lisp_gpe.h>
 #include <vnet/lisp-gpe/lisp_gpe_fwd_entry.h>
@@ -185,7 +185,8 @@ format_lisp_gpe_header_with_length (u8 * s, va_list * args)
 
   s = format (s, "\n  ver_res %d res %d next_protocol %d iid %d(%x)",
              h->ver_res, h->res, h->next_protocol,
-             clib_net_to_host_u32 (h->iid), clib_net_to_host_u32 (h->iid));
+             clib_net_to_host_u32 (h->iid << 8),
+             clib_net_to_host_u32 (h->iid << 8));
   return s;
 }
 
@@ -201,7 +202,7 @@ VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = {
 
 typedef struct
 {
-  u32 lb_index;
+  u32 dpo_index;
 } l2_lisp_gpe_tx_trace_t;
 
 static u8 *
@@ -211,7 +212,7 @@ format_l2_lisp_gpe_tx_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   l2_lisp_gpe_tx_trace_t *t = va_arg (*args, l2_lisp_gpe_tx_trace_t *);
 
-  s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->lb_index);
+  s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->dpo_index);
   return s;
 }
 
@@ -238,6 +239,8 @@ l2_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
 {
   u32 n_left_from, next_index, *from, *to_next;
   lisp_gpe_main_t *lgm = &lisp_gpe_main;
+  u32 thread_index = vm->thread_index;
+  vlib_combined_counter_main_t *cm = &load_balance_main.lbm_to_counters;
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -273,12 +276,14 @@ l2_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
                                     e0->src_address, e0->dst_address);
          vnet_buffer (b0)->ip.adj_index[VLIB_TX] = lbi0;
 
-
+         vlib_increment_combined_counter (cm, thread_index, lbi0, 1,
+                                          vlib_buffer_length_in_chain (vm,
+                                                                       b0));
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
                                                           sizeof (*tr));
-             tr->lb_index = lbi0;
+             tr->dpo_index = lbi0;
            }
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
                                           n_left_to_next, bi0, l2_arc_to_lb);
@@ -306,6 +311,110 @@ VNET_DEVICE_CLASS (l2_lisp_gpe_device_class,static) = {
 };
 /* *INDENT-ON* */
 
+typedef struct
+{
+  u32 dpo_index;
+} nsh_lisp_gpe_tx_trace_t;
+
+u8 *
+format_nsh_lisp_gpe_tx_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  nsh_lisp_gpe_tx_trace_t *t = va_arg (*args, nsh_lisp_gpe_tx_trace_t *);
+
+  s = format (s, "NSH-GPE-TX: tunnel %d", t->dpo_index);
+  return s;
+}
+
+/**
+ * @brief LISP-GPE interface TX for NSH overlays.
+ * @node nsh_lisp_gpe_interface_tx
+ *
+ * The NSH LISP-GPE interface TX function.
+ *
+ * @param[in]   vm        vlib_main_t corresponding to the current thread.
+ * @param[in]   node      vlib_node_runtime_t data for this node.
+ * @param[in]   frame     vlib_frame_t whose contents should be dispatched.
+ *
+ * @return number of vectors in frame.
+ */
+static uword
+nsh_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
+                          vlib_frame_t * from_frame)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+  lisp_gpe_main_t *lgm = &lisp_gpe_main;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         vlib_buffer_t *b0;
+         u32 bi0;
+         u32 *nsh0, next0;
+         const dpo_id_t *dpo0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         nsh0 = vlib_buffer_get_current (b0);
+
+         vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_LCAF;
+
+         /* lookup SPI + SI (second word of the NSH header).
+          * NB: Load balancing was done by the control plane */
+         dpo0 = lisp_nsh_fib_lookup (lgm, nsh0[1]);
+
+         next0 = dpo0->dpoi_next_node;
+         vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
+                                                           sizeof (*tr));
+             tr->dpo_index = dpo0->dpoi_index;
+           }
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return from_frame->n_vectors;
+}
+
+static u8 *
+format_nsh_lisp_gpe_name (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  return format (s, "nsh_lisp_gpe%d", dev_instance);
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (nsh_lisp_gpe_device_class,static) = {
+  .name = "NSH_LISP_GPE",
+  .format_device_name = format_nsh_lisp_gpe_name,
+  .format_tx_trace = format_nsh_lisp_gpe_tx_trace,
+  .tx_function = nsh_lisp_gpe_interface_tx,
+};
+/* *INDENT-ON* */
+
 static vnet_hw_interface_t *
 lisp_gpe_create_iface (lisp_gpe_main_t * lgm, u32 vni, u32 dp_table,
                       vnet_device_class_t * dev_class,
@@ -396,12 +505,14 @@ lisp_gpe_iface_set_table (u32 sw_if_index, u32 table_id)
 {
   fib_node_index_t fib_index;
 
-  fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id);
+  fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id,
+                                                FIB_SOURCE_LISP);
   vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index);
   ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index;
   ip4_sw_interface_enable_disable (sw_if_index, 1);
 
-  fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id);
+  fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id,
+                                                FIB_SOURCE_LISP);
   vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index);
   ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index;
   ip6_sw_interface_enable_disable (sw_if_index, 1);
@@ -421,7 +532,7 @@ lisp_gpe_tenant_del_default_routes (u32 table_id)
 
     fib_index = fib_table_find (prefix.fp_proto, table_id);
     fib_table_entry_special_remove (fib_index, &prefix, FIB_SOURCE_LISP);
-    fib_table_unlock (fib_index, prefix.fp_proto);
+    fib_table_unlock (fib_index, prefix.fp_proto, FIB_SOURCE_LISP);
   }
 }
 
@@ -440,7 +551,8 @@ lisp_gpe_tenant_add_default_routes (u32 table_id)
     /*
      * Add a deafult route that results in a control plane punt DPO
      */
-    fib_index = fib_table_find_or_create_and_lock (prefix.fp_proto, table_id);
+    fib_index = fib_table_find_or_create_and_lock (prefix.fp_proto, table_id,
+                                                  FIB_SOURCE_LISP);
     fib_table_entry_special_dpo_add (fib_index, &prefix, FIB_SOURCE_LISP,
                                     FIB_ENTRY_FLAG_EXCLUSIVE,
                                     lisp_cp_dpo_get (fib_proto_to_dpo
@@ -463,7 +575,8 @@ lisp_gpe_tenant_add_default_routes (u32 table_id)
  * @return number of vectors in frame.
  */
 u32
-lisp_gpe_add_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id)
+lisp_gpe_add_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id,
+                      u8 with_default_routes)
 {
   vnet_main_t *vnm = lgm->vnet_main;
   tunnel_lookup_t *l3_ifaces = &lgm->l3_ifaces;
@@ -491,7 +604,8 @@ lisp_gpe_add_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id)
 
   /* insert default routes that point to lisp-cp lookup */
   lisp_gpe_iface_set_table (hi->sw_if_index, table_id);
-  lisp_gpe_tenant_add_default_routes (table_id);
+  if (with_default_routes)
+    lisp_gpe_tenant_add_default_routes (table_id);
 
   /* enable interface */
   vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
@@ -548,6 +662,12 @@ lisp_gpe_add_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
   uword *hip, *si;
   u16 bd_index;
 
+  if (bd_id > L2_BD_ID_MAX)
+    {
+      clib_warning ("bridge domain ID %d exceed 16M limit", bd_id);
+      return ~0;
+    }
+
   bd_index = bd_find_or_add_bd_index (&bd_main, bd_id);
   hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index);
 
@@ -580,7 +700,7 @@ lisp_gpe_add_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
 
   /* we're ready. add iface to l2 bridge domain */
   set_int_l2_mode (lgm->vlib_main, vnm, MODE_L2_BRIDGE, hi->sw_if_index,
-                  bd_index, 0, 0, 0);
+                  bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
 
   return (hi->sw_if_index);
 }
@@ -600,11 +720,11 @@ void
 lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
 {
   tunnel_lookup_t *l2_ifaces = &lgm->l2_ifaces;
-  u16 bd_index;
-  uword *hip;
+  vnet_hw_interface_t *hi;
 
-  bd_index = bd_find_or_add_bd_index (&bd_main, bd_id);
-  hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index);
+  u32 bd_index = bd_find_index (&bd_main, bd_id);
+  ASSERT (bd_index != ~0);
+  uword *hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index);
 
   if (hip == 0)
     {
@@ -612,9 +732,80 @@ lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
                    bd_id);
       return;
     }
+
+  /* Remove interface from bridge .. by enabling L3 mode */
+  hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]);
+  set_int_l2_mode (lgm->vlib_main, lgm->vnet_main, MODE_L3, hi->sw_if_index,
+                  0, L2_BD_PORT_TYPE_NORMAL, 0, 0);
   lisp_gpe_remove_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces);
 }
 
+/**
+ * @brief Add LISP-GPE NSH interface.
+ *
+ * Creates LISP-GPE interface, sets it in L3 mode.
+ *
+ * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
+ * @param[in]   a       Parameters to create interface.
+ *
+ * @return sw_if_index.
+ */
+u32
+vnet_lisp_gpe_add_nsh_iface (lisp_gpe_main_t * lgm)
+{
+  vnet_main_t *vnm = lgm->vnet_main;
+  tunnel_lookup_t *nsh_ifaces = &lgm->nsh_ifaces;
+  vnet_hw_interface_t *hi;
+  uword *hip, *si;
+
+  hip = hash_get (nsh_ifaces->hw_if_index_by_dp_table, 0);
+
+  if (hip)
+    {
+      clib_warning ("NSH interface 0 already exists");
+      return ~0;
+    }
+
+  si = hash_get (nsh_ifaces->sw_if_index_by_vni, 0);
+  if (si)
+    {
+      clib_warning ("NSH interface already exists");
+      return ~0;
+    }
+
+  /* create lisp iface and populate tunnel tables */
+  hi = lisp_gpe_create_iface (lgm, 0, 0,
+                             &nsh_lisp_gpe_device_class, &lgm->nsh_ifaces);
+
+  /* enable interface */
+  vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
+                              VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+  vnet_hw_interface_set_flags (vnm, hi->hw_if_index,
+                              VNET_HW_INTERFACE_FLAG_LINK_UP);
+
+  return (hi->sw_if_index);
+}
+
+/**
+ * @brief Del LISP-GPE NSH interface.
+ *
+ */
+void
+vnet_lisp_gpe_del_nsh_iface (lisp_gpe_main_t * lgm)
+{
+  tunnel_lookup_t *nsh_ifaces = &lgm->nsh_ifaces;
+  uword *hip;
+
+  hip = hash_get (nsh_ifaces->hw_if_index_by_dp_table, 0);
+
+  if (hip == 0)
+    {
+      clib_warning ("The NSH 0 interface doesn't exist");
+      return;
+    }
+  lisp_gpe_remove_iface (lgm, hip[0], 0, &lgm->nsh_ifaces);
+}
+
 static clib_error_t *
 lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
                                   vlib_cli_command_t * cmd)
@@ -623,6 +814,8 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
   u8 is_add = 1;
   u32 table_id, vni, bd_id;
   u8 vni_is_set = 0, vrf_is_set = 0, bd_index_is_set = 0;
+  u8 nsh_iface = 0;
+  clib_error_t *error = NULL;
 
   if (vnet_lisp_gpe_enable_disable_status () == 0)
     {
@@ -651,29 +844,64 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
        {
          bd_index_is_set = 1;
        }
+      else if (unformat (line_input, "nsh"))
+       {
+         nsh_iface = 1;
+       }
+      else
+       {
+         error = clib_error_return (0, "parse error: '%U'",
+                                    format_unformat_error, line_input);
+         goto done;
+       }
+    }
+
+  if (nsh_iface)
+    {
+      if (is_add)
+       {
+         if (~0 == vnet_lisp_gpe_add_nsh_iface (&lisp_gpe_main))
+           {
+             error = clib_error_return (0, "NSH interface not created");
+             goto done;
+           }
+       }
       else
        {
-         return clib_error_return (0, "parse error: '%U'",
-                                   format_unformat_error, line_input);
+         vnet_lisp_gpe_del_nsh_iface (&lisp_gpe_main);
        }
+      goto done;
     }
 
   if (vrf_is_set && bd_index_is_set)
-    return clib_error_return (0,
-                             "Cannot set both vrf and brdige domain index!");
+    {
+      error = clib_error_return
+       (0, "Cannot set both vrf and brdige domain index!");
+      goto done;
+    }
 
   if (!vni_is_set)
-    return clib_error_return (0, "vni must be set!");
+    {
+      error = clib_error_return (0, "vni must be set!");
+      goto done;
+    }
 
   if (!vrf_is_set && !bd_index_is_set)
-    return clib_error_return (0, "vrf or bridge domain index must be set!");
+    {
+      error =
+       clib_error_return (0, "vrf or bridge domain index must be set!");
+      goto done;
+    }
 
   if (bd_index_is_set)
     {
       if (is_add)
        {
          if (~0 == lisp_gpe_tenant_l2_iface_add_or_lock (vni, bd_id))
-           return clib_error_return (0, "L2 interface not created");
+           {
+             error = clib_error_return (0, "L2 interface not created");
+             goto done;
+           }
        }
       else
        lisp_gpe_tenant_l2_iface_unlock (vni);
@@ -682,20 +910,28 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
     {
       if (is_add)
        {
-         if (~0 == lisp_gpe_tenant_l3_iface_add_or_lock (vni, table_id))
-           return clib_error_return (0, "L3 interface not created");
+         if (~0 == lisp_gpe_tenant_l3_iface_add_or_lock (vni, table_id, 1
+                                                         /* with_default_route */
+             ))
+           {
+             error = clib_error_return (0, "L3 interface not created");
+             goto done;
+           }
        }
       else
        lisp_gpe_tenant_l3_iface_unlock (vni);
     }
 
-  return (NULL);
+done:
+  unformat_free (line_input);
+
+  return error;
 }
 
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (add_del_lisp_gpe_iface_command, static) = {
-  .path = "lisp gpe iface",
-  .short_help = "lisp gpe iface add/del vni <vni> vrf <vrf>",
+  .path = "gpe iface",
+  .short_help = "gpe iface add/del vni <vni> vrf <vrf>",
   .function = lisp_gpe_add_del_iface_command_fn,
 };
 /* *INDENT-ON* */