P2P Ethernet
[vpp.git] / src / vnet / ethernet / p2p_ethernet.c
index 3c07731..e3f667b 100644 (file)
 #include <vppinfra/bihash_16_8.h>
 #include <vnet/vnet.h>
 #include <vnet/ethernet/p2p_ethernet.h>
+#include <vnet/l2/l2_input.h>
+
+p2p_ethernet_main_t p2p_main;
+
+static void
+create_p2pe_key (p2p_key_t * p2pe_key, u32 parent_if_index, u8 * client_mac)
+{
+  clib_memcpy (p2pe_key->mac, client_mac, 6);
+  p2pe_key->pad1 = 0;
+  p2pe_key->hw_if_index = parent_if_index;
+  p2pe_key->pad2 = 0;
+}
+
+u32
+p2p_ethernet_lookup (u32 parent_if_index, u8 * client_mac)
+{
+  p2p_ethernet_main_t *p2pm = &p2p_main;
+  p2p_key_t p2pe_key;
+  uword *p;
+
+  create_p2pe_key (&p2pe_key, parent_if_index, client_mac);
+  p = hash_get_mem (p2pm->p2p_ethernet_by_key, &p2pe_key);
+  if (p)
+    return p[0];
+
+  return ~0;
+}
 
 int
 p2p_ethernet_add_del (vlib_main_t * vm, u32 parent_if_index,
-                     u8 * client_mac, int is_add)
+                     u8 * client_mac, u32 p2pe_subif_id, int is_add,
+                     u32 * p2pe_if_index)
 {
-  return 0;
+  vnet_main_t *vnm = vnet_get_main ();
+  p2p_ethernet_main_t *p2pm = &p2p_main;
+  vnet_interface_main_t *im = &vnm->interface_main;
+
+  u32 p2pe_sw_if_index = ~0;
+  p2pe_sw_if_index = p2p_ethernet_lookup (parent_if_index, client_mac);
+
+  if (p2pe_if_index)
+    *p2pe_if_index = ~0;
+
+  if (is_add)
+    {
+      if (p2pe_sw_if_index == ~0)
+       {
+         vnet_hw_interface_t *hi;
+
+         hi = vnet_get_hw_interface (vnm, parent_if_index);
+         if (hi->bond_info == VNET_HW_INTERFACE_BOND_INFO_SLAVE)
+           return VNET_API_ERROR_BOND_SLAVE_NOT_ALLOWED;
+
+         u64 sup_and_sub_key =
+           ((u64) (hi->sw_if_index) << 32) | (u64) p2pe_subif_id;
+         uword *p;
+         p = hash_get_mem (im->sw_if_index_by_sup_and_sub, &sup_and_sub_key);
+         if (p)
+           {
+             if (CLIB_DEBUG > 0)
+               clib_warning
+                 ("p2p ethernet sub-interface on sw_if_index %d with sub id %d already exists\n",
+                  hi->sw_if_index, p2pe_subif_id);
+             return VNET_API_ERROR_SUBIF_ALREADY_EXISTS;
+           }
+         vnet_sw_interface_t template = {
+           .type = VNET_SW_INTERFACE_TYPE_P2P,
+           .flood_class = VNET_FLOOD_CLASS_NORMAL,
+           .sup_sw_if_index = hi->sw_if_index,
+           .sub.id = p2pe_subif_id
+         };
+
+         clib_memcpy (template.p2p.client_mac, client_mac,
+                      sizeof (template.p2p.client_mac));
+
+         if (vnet_create_sw_interface (vnm, &template, &p2pe_sw_if_index))
+           return VNET_API_ERROR_SUBIF_CREATE_FAILED;
+
+         vnet_interface_main_t *im = &vnm->interface_main;
+         sup_and_sub_key =
+           ((u64) (hi->sw_if_index) << 32) | (u64) p2pe_subif_id;
+         u64 *kp = clib_mem_alloc (sizeof (*kp));
+
+         *kp = sup_and_sub_key;
+         hash_set (hi->sub_interface_sw_if_index_by_id, p2pe_subif_id,
+                   p2pe_sw_if_index);
+         hash_set_mem (im->sw_if_index_by_sup_and_sub, kp, p2pe_sw_if_index);
+
+         p2p_key_t *p_p2pe_key;
+         p_p2pe_key = clib_mem_alloc (sizeof (*p_p2pe_key));
+         create_p2pe_key (p_p2pe_key, parent_if_index, client_mac);
+         hash_set_mem (p2pm->p2p_ethernet_by_key, p_p2pe_key,
+                       p2pe_sw_if_index);
+
+         if (p2pe_if_index)
+           *p2pe_if_index = p2pe_sw_if_index;
+
+         vec_validate (p2pm->p2p_ethernet_by_sw_if_index, parent_if_index);
+         if (p2pm->p2p_ethernet_by_sw_if_index[parent_if_index] == 0)
+           {
+             vnet_feature_enable_disable ("device-input",
+                                          "p2p-ethernet-input",
+                                          parent_if_index, 1, 0, 0);
+             /* Set promiscuous mode on the l2 interface */
+             ethernet_set_flags (vnm, parent_if_index,
+                                 ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
+
+           }
+         p2pm->p2p_ethernet_by_sw_if_index[parent_if_index]++;
+         /* set the interface mode */
+         set_int_l2_mode (vm, vnm, MODE_L3, p2pe_subif_id, 0, 0, 0, 0);
+         return 0;
+       }
+      return VNET_API_ERROR_SUBIF_ALREADY_EXISTS;
+    }
+  else
+    {
+      if (p2pe_sw_if_index == ~0)
+       return VNET_API_ERROR_SUBIF_DOESNT_EXIST;
+      else
+       {
+         int rv = 0;
+         rv = vnet_delete_sub_interface (p2pe_sw_if_index);
+         if (!rv)
+           {
+             vec_validate (p2pm->p2p_ethernet_by_sw_if_index,
+                           parent_if_index);
+             if (p2pm->p2p_ethernet_by_sw_if_index[parent_if_index] == 1)
+               {
+                 vnet_feature_enable_disable ("device-input",
+                                              "p2p-ethernet-input",
+                                              parent_if_index, 0, 0, 0);
+                 /* Disable promiscuous mode on the l2 interface */
+                 ethernet_set_flags (vnm, parent_if_index, 0);
+               }
+             p2pm->p2p_ethernet_by_sw_if_index[parent_if_index]--;
+
+             /* Remove p2p_ethernet from hash map */
+             p2p_key_t *p_p2pe_key;
+             p_p2pe_key = clib_mem_alloc (sizeof (*p_p2pe_key));
+             create_p2pe_key (p_p2pe_key, parent_if_index, client_mac);
+             hash_unset_mem (p2pm->p2p_ethernet_by_key, p_p2pe_key);
+           }
+         return rv;
+       }
+    }
 }
 
 static clib_error_t *
@@ -35,6 +175,7 @@ vnet_p2p_ethernet_add_del (vlib_main_t * vm, unformat_input_t * input,
   int is_add = 1;
   int remote_mac = 0;
   u32 hw_if_index = ~0;
+  u32 sub_id = ~0;
   u8 client_mac[6];
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -44,6 +185,8 @@ vnet_p2p_ethernet_add_del (vlib_main_t * vm, unformat_input_t * input,
        ;
       else if (unformat (input, "%U", unformat_ethernet_address, &client_mac))
        remote_mac = 1;
+      else if (unformat (input, "sub-id %d", &sub_id))
+       ;
       else if (unformat (input, "del"))
        is_add = 0;
       else
@@ -54,9 +197,11 @@ vnet_p2p_ethernet_add_del (vlib_main_t * vm, unformat_input_t * input,
     return clib_error_return (0, "Please specify parent interface ...");
   if (!remote_mac)
     return clib_error_return (0, "Please specify client MAC address ...");
+  if (sub_id == ~0 && is_add)
+    return clib_error_return (0, "Please specify sub-interface id ...");
 
   u32 rv;
-  rv = p2p_ethernet_add_del (vm, hw_if_index, client_mac, is_add);
+  rv = p2p_ethernet_add_del (vm, hw_if_index, client_mac, sub_id, is_add, 0);
   switch (rv)
     {
     case VNET_API_ERROR_BOND_SLAVE_NOT_ALLOWED:
@@ -77,17 +222,21 @@ vnet_p2p_ethernet_add_del (vlib_main_t * vm, unformat_input_t * input,
   return 0;
 }
 
-/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (p2p_ethernet_add_del_command, static) =
 {
-  .path = "p2p_ethernet ",
-  .function = vnet_p2p_ethernet_add_del,
-  .short_help = "p2p_ethernet <intfc> <mac-address> [del]",};
-/* *INDENT-ON* */
+.path = "p2p_ethernet ",.function = vnet_p2p_ethernet_add_del,.short_help =
+    "p2p_ethernet <intfc> <mac-address> [sub-id <id> | del]",};
 
 static clib_error_t *
 p2p_ethernet_init (vlib_main_t * vm)
 {
+  p2p_ethernet_main_t *p2pm = &p2p_main;
+
+  p2pm->vlib_main = vm;
+  p2pm->vnet_main = vnet_get_main ();
+  p2pm->p2p_ethernet_by_key =
+    hash_create_mem (0, sizeof (p2p_key_t), sizeof (uword));
+
   return 0;
 }