P2P Ethernet 24/7224/7
authorPavel Kotucek <pkotucek@cisco.com>
Tue, 20 Jun 2017 12:00:26 +0000 (14:00 +0200)
committerJohn Lo <loj@cisco.com>
Tue, 1 Aug 2017 14:44:13 +0000 (14:44 +0000)
Change-Id: Idb97e573961b3bc2acdeef77582314590795f8c3
Signed-off-by: Pavel Kotucek <pkotucek@cisco.com>
18 files changed:
src/vat/api_format.c
src/vnet.am
src/vnet/api_errno.h
src/vnet/devices/devices.c
src/vnet/ethernet/ethernet.h
src/vnet/ethernet/interface.c
src/vnet/ethernet/node.c
src/vnet/ethernet/p2p_ethernet.api
src/vnet/ethernet/p2p_ethernet.c
src/vnet/ethernet/p2p_ethernet.h
src/vnet/ethernet/p2p_ethernet_api.c
src/vnet/ethernet/p2p_ethernet_input.c [new file with mode: 0644]
src/vnet/interface.c
src/vnet/interface.h
src/vnet/interface_funcs.h
test/test_p2p_ethernet.py [new file with mode: 0644]
test/vpp_papi_provider.py
test/vpp_sub_interface.py

index 5a0c458..6a2d36d 100644 (file)
@@ -19161,6 +19161,7 @@ api_p2p_ethernet_add (vat_main_t * vam)
   unformat_input_t *i = vam->input;
   vl_api_p2p_ethernet_add_t *mp;
   u32 parent_if_index = ~0;
+  u32 sub_id = ~0;
   u8 remote_mac[6];
   u8 mac_set = 0;
   int ret;
@@ -19176,6 +19177,8 @@ api_p2p_ethernet_add (vat_main_t * vam)
        if (unformat
            (i, "remote_mac %U", unformat_ethernet_address, remote_mac))
        mac_set++;
+      else if (unformat (i, "sub_id %d", &sub_id))
+       ;
       else
        {
          clib_warning ("parse error '%U'", format_unformat_error, i);
@@ -19193,9 +19196,15 @@ api_p2p_ethernet_add (vat_main_t * vam)
       errmsg ("missing remote mac address");
       return -99;
     }
+  if (sub_id == ~0)
+    {
+      errmsg ("missing sub-interface id");
+      return -99;
+    }
 
   M (P2P_ETHERNET_ADD, mp);
   mp->parent_if_index = ntohl (parent_if_index);
+  mp->subif_id = ntohl (sub_id);
   clib_memcpy (mp->remote_mac, remote_mac, sizeof (remote_mac));
 
   S (mp);
@@ -20094,11 +20103,10 @@ _(l2_xconnect_dump, "")                                               \
 _(sw_interface_set_mtu, "<intfc> | sw_if_index <nn> mtu <nn>")        \
 _(ip_neighbor_dump, "[ip6] <intfc> | sw_if_index <nn>")                 \
 _(sw_interface_get_table, "<intfc> | sw_if_index <id> [ipv6]")          \
-_(p2p_ethernet_add, "<intfc> | sw_if_index <nn> remote_mac <mac-address>") \
+_(p2p_ethernet_add, "<intfc> | sw_if_index <nn> remote_mac <mac-address> sub_id <id>") \
 _(p2p_ethernet_del, "<intfc> | sw_if_index <nn> remote_mac <mac-address>") \
-_(lldp_config, "system-name <name> tx-hold <nn> tx-interval <nn>")      \
-_(sw_interface_set_lldp,                                                \
-  "<intfc> | sw_if_index <nn> [port-desc <description>] [disable]")
+_(lldp_config, "system-name <name> tx-hold <nn> tx-interval <nn>") \
+_(sw_interface_set_lldp, "<intfc> | sw_if_index <nn> [port-desc <description>] [disable]")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
@@ -20122,7 +20130,6 @@ _(search_node_table, "usage: search_node_table <name>...")      \
 _(set, "usage: set <variable-name> <value>")                    \
 _(script, "usage: script <file-name>")                          \
 _(unset, "usage: unset <variable-name>")
-
 #define _(N,n)                                  \
     static void vl_api_##n##_t_handler_uni      \
     (vl_api_##n##_t * mp)                       \
index 060e3f3..ad84c02 100644 (file)
@@ -115,6 +115,7 @@ libvnet_la_SOURCES +=                               \
  vnet/ethernet/pg.c                            \
  vnet/ethernet/sfp.c                           \
  vnet/ethernet/p2p_ethernet.c                  \
+ vnet/ethernet/p2p_ethernet_input.c            \
  vnet/ethernet/p2p_ethernet_api.c
 
 nobase_include_HEADERS +=                      \
index b22bb3a..747c65e 100644 (file)
@@ -112,7 +112,8 @@ _(BD_ALREADY_EXISTS, -119, "Bridge domain already exists")              \
 _(BD_IN_USE, -120, "Bridge domain has member interfaces")              \
 _(BD_NOT_MODIFIABLE, -121, "Bridge domain 0 can't be deleted/modified") \
 _(BD_ID_EXCEED_MAX, -122, "Bridge domain ID exceed 16M limit")         \
-_(UNSUPPORTED, -123, "Unsupported")
+_(UNSUPPORTED, -123, "Unsupported")                                     \
+_(SUBIF_DOESNT_EXIST, -124, "Subinterface doesn't exist")
 
 typedef enum
 {
index f64c6e0..2eb8e30 100644 (file)
@@ -77,6 +77,12 @@ VNET_FEATURE_INIT (span_input, static) = {
   .runs_before = VNET_FEATURES ("ethernet-input"),
 };
 
+VNET_FEATURE_INIT (p2p_ethernet_node, static) = {
+  .arc_name = "device-input",
+  .node_name = "p2p-ethernet-input",
+  .runs_before = VNET_FEATURES ("ethernet-input"),
+};
+
 VNET_FEATURE_INIT (ethernet_input, static) = {
   .arc_name = "device-input",
   .node_name = "ethernet-input",
index d9ab8c1..9ca256c 100644 (file)
@@ -169,6 +169,7 @@ typedef struct
 #define SUBINT_CONFIG_MATCH_3_TAG (1<<3)
 #define SUBINT_CONFIG_VALID       (1<<4)
 #define SUBINT_CONFIG_L2          (1<<5)
+#define SUBINT_CONFIG_P2P         (1<<6)
 
 } subint_config_t;
 
index 9ac30bc..3e78a49 100644 (file)
@@ -89,7 +89,10 @@ ethernet_build_rewrite (vnet_main_t * vnm,
   ethernet_type_t type;
   uword n_bytes = sizeof (h[0]);
   u8 *rewrite = NULL;
+  u8 is_p2p = 0;
 
+  if (sub_sw->type == VNET_SW_INTERFACE_TYPE_P2P)
+    is_p2p = 1;
   if (sub_sw != sup_sw)
     {
       if (sub_sw->sub.eth.flags.one_tag)
@@ -100,13 +103,24 @@ ethernet_build_rewrite (vnet_main_t * vnm,
        {
          n_bytes += 2 * (sizeof (ethernet_vlan_header_t));
        }
-      // Check for encaps that are not supported for L3 interfaces
-      if (!(sub_sw->sub.eth.flags.exact_match) ||
-         (sub_sw->sub.eth.flags.default_sub) ||
-         (sub_sw->sub.eth.flags.outer_vlan_id_any) ||
-         (sub_sw->sub.eth.flags.inner_vlan_id_any))
+      else if (PREDICT_FALSE (is_p2p))
        {
-         return 0;
+         n_bytes = sizeof (ethernet_header_t);
+       }
+      if (PREDICT_FALSE (!is_p2p))
+       {
+         // Check for encaps that are not supported for L3 interfaces
+         if (!(sub_sw->sub.eth.flags.exact_match) ||
+             (sub_sw->sub.eth.flags.default_sub) ||
+             (sub_sw->sub.eth.flags.outer_vlan_id_any) ||
+             (sub_sw->sub.eth.flags.inner_vlan_id_any))
+           {
+             return 0;
+           }
+       }
+      else
+       {
+         n_bytes = sizeof (ethernet_header_t);
        }
     }
 
@@ -126,12 +140,20 @@ ethernet_build_rewrite (vnet_main_t * vnm,
   h = (ethernet_header_t *) rewrite;
   ei = pool_elt_at_index (em->interfaces, hw->hw_instance);
   clib_memcpy (h->src_address, ei->address, sizeof (h->src_address));
-  if (dst_address)
-    clib_memcpy (h->dst_address, dst_address, sizeof (h->dst_address));
+  if (is_p2p)
+    {
+      clib_memcpy (h->dst_address, sub_sw->p2p.client_mac,
+                  sizeof (h->dst_address));
+    }
   else
-    memset (h->dst_address, ~0, sizeof (h->dst_address));      /* broadcast */
+    {
+      if (dst_address)
+       clib_memcpy (h->dst_address, dst_address, sizeof (h->dst_address));
+      else
+       memset (h->dst_address, ~0, sizeof (h->dst_address));   /* broadcast */
+    }
 
-  if (sub_sw->sub.eth.flags.one_tag)
+  if (PREDICT_FALSE (!is_p2p) && sub_sw->sub.eth.flags.one_tag)
     {
       ethernet_vlan_header_t *outer = (void *) (h + 1);
 
@@ -143,7 +165,7 @@ ethernet_build_rewrite (vnet_main_t * vnm,
       outer->type = clib_host_to_net_u16 (type);
 
     }
-  else if (sub_sw->sub.eth.flags.two_tags)
+  else if (PREDICT_FALSE (!is_p2p) && sub_sw->sub.eth.flags.two_tags)
     {
       ethernet_vlan_header_t *outer = (void *) (h + 1);
       ethernet_vlan_header_t *inner = (void *) (outer + 1);
@@ -174,7 +196,12 @@ ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
 
   adj = adj_get (ai);
 
-  if (FIB_PROTOCOL_IP4 == adj->ia_nh_proto)
+  vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
+  if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
+    {
+      default_update_adjacency (vnm, sw_if_index, ai);
+    }
+  else if (FIB_PROTOCOL_IP4 == adj->ia_nh_proto)
     {
       arp_update_adjacency (vnm, sw_if_index, ai);
     }
@@ -719,7 +746,8 @@ vnet_delete_sub_interface (u32 sw_if_index)
   vnet_interface_main_t *im = &vnm->interface_main;
   vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
 
-  if (si->type == VNET_SW_INTERFACE_TYPE_SUB)
+  if (si->type == VNET_SW_INTERFACE_TYPE_SUB ||
+      si->type == VNET_SW_INTERFACE_TYPE_P2P)
     {
       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
       u64 sup_and_sub_key =
index 421d501..f216216 100755 (executable)
@@ -40,6 +40,7 @@
 #include <vlib/vlib.h>
 #include <vnet/pg/pg.h>
 #include <vnet/ethernet/ethernet.h>
+#include <vnet/ethernet/p2p_ethernet.h>
 #include <vppinfra/sparse_vec.h>
 #include <vnet/l2/l2_bvi.h>
 
@@ -823,7 +824,21 @@ ethernet_sw_interface_get_config (vnet_main_t * vnm,
   // Locate the subint for the given ethernet config
   si = vnet_get_sw_interface (vnm, sw_if_index);
 
-  if (si->sub.eth.flags.default_sub)
+  if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
+    {
+      p2p_ethernet_main_t *p2pm = &p2p_main;
+      u32 p2pe_sw_if_index =
+       p2p_ethernet_lookup (hi->hw_if_index, si->p2p.client_mac);
+      if (p2pe_sw_if_index == ~0)
+       {
+         pool_get (p2pm->p2p_subif_pool, subint);
+         si->p2p.pool_index = subint - p2pm->p2p_subif_pool;
+       }
+      else
+       subint = vec_elt_at_index (p2pm->p2p_subif_pool, si->p2p.pool_index);
+      *flags = SUBINT_CONFIG_P2P;
+    }
+  else if (si->sub.eth.flags.default_sub)
     {
       subint = &main_intf->default_subint;
       *flags = SUBINT_CONFIG_MATCH_0_TAG |
index 72a7342..8fb6637 100644 (file)
@@ -18,6 +18,7 @@ define p2p_ethernet_add
   u32 client_index;
   u32 context;
   u32 parent_if_index;
+  u32 subif_id;
   u8  remote_mac[6];
 };
 
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;
 }
 
index 31b93d8..bb1e289 100644 (file)
 #include <vnet/vnet.h>
 #include <vnet/ethernet/ethernet.h>
 
-int p2p_ethernet_add_del (vlib_main_t * vm, u32 parent_if_index, u8 * client_mac, int is_add);
+
+typedef struct {
+  /**
+   * Hash mapping parent sw_if_index and client mac address to p2p_ethernet sub-interface
+   */
+  uword * p2p_ethernet_by_key;
+
+  u32 *p2p_ethernet_by_sw_if_index;
+
+  // Pool of p2p subifs;
+  subint_config_t *p2p_subif_pool;
+
+  /* convenience */
+  vlib_main_t * vlib_main;
+  vnet_main_t * vnet_main;
+} p2p_ethernet_main_t;
+
+extern p2p_ethernet_main_t p2p_main;
+
+typedef struct
+{
+  u32 sw_if_index;
+  u32 p2pe_sw_if_index;
+  u8  client_mac[6];
+} p2p_ethernet_trace_t;
+
+/**
+ * @brief Key struct for P2P Ethernet
+ * Key fields: parent sw_if_index and client mac address
+ * all fields in NET byte order
+ */
+
+typedef struct {
+  u8 mac[6];
+  u16 pad1;         // padding for u64 mac address
+  u32 hw_if_index;
+  u32 pad2;         // padding for u64
+} p2p_key_t;
+
+u32 p2p_ethernet_lookup (u32 parent_sw_if_index, u8* client_mac);
+int p2p_ethernet_add_del (vlib_main_t * vm, u32 parent_if_index, u8 * client_mac, u32 sub_id, int is_add, u32 *p2pe_if_index);
 
 #endif /* included_vnet_p2p_ethernet_h */
index 1d9eaeb..f2c730b 100644 (file)
@@ -51,12 +51,21 @@ vl_api_p2p_ethernet_add_t_handler (vl_api_p2p_ethernet_add_t * mp)
   int rv;
 
   u32 parent_if_index = htonl (mp->parent_if_index);
+  u32 sub_id = htonl (mp->subif_id);
+  u32 p2pe_if_index;
   u8 remote_mac[6];
 
   clib_memcpy (remote_mac, mp->remote_mac, 6);
-  rv = p2p_ethernet_add_del (vm, parent_if_index, remote_mac, 1);
-
-  REPLY_MACRO (VL_API_P2P_ETHERNET_ADD_REPLY);
+  rv =
+    p2p_ethernet_add_del (vm, parent_if_index, remote_mac, sub_id, 1,
+                         &p2pe_if_index);
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO2(VL_API_P2P_ETHERNET_ADD_REPLY,
+  ({
+    rmp->sw_if_index = htonl(p2pe_if_index);
+  }));
+  /* *INDENT-ON* */
 }
 
 void
@@ -70,7 +79,7 @@ vl_api_p2p_ethernet_del_t_handler (vl_api_p2p_ethernet_del_t * mp)
   u8 remote_mac[6];
 
   clib_memcpy (remote_mac, mp->remote_mac, 6);
-  rv = p2p_ethernet_add_del (vm, parent_if_index, remote_mac, 0);
+  rv = p2p_ethernet_add_del (vm, parent_if_index, remote_mac, ~0, 0, 0);
 
   REPLY_MACRO (VL_API_P2P_ETHERNET_DEL_REPLY);
 }
diff --git a/src/vnet/ethernet/p2p_ethernet_input.c b/src/vnet/ethernet/p2p_ethernet_input.c
new file mode 100644 (file)
index 0000000..a58b832
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * node.c: p2p ethernet vpp node
+ *
+ * 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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vppinfra/error.h>
+
+#include <vnet/ethernet/p2p_ethernet.h>
+
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+vlib_node_registration_t p2p_ethernet_input_node;
+
+/* packet trace format function */
+u8 *
+format_p2p_ethernet_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 *);
+  p2p_ethernet_trace_t *t = va_arg (*args, p2p_ethernet_trace_t *);
+
+  vnet_main_t *vnm = &vnet_main;
+  s = format (s, "P2P ethernet: %U -> %U",
+             format_vnet_sw_if_index_name, vnm, t->sw_if_index,
+             format_vnet_sw_if_index_name, vnm, t->p2pe_sw_if_index);
+
+  return s;
+}
+
+#define foreach_p2p_ethernet_error                      \
+_(HITS, "P2P ethernet incoming packets processed")
+
+typedef enum
+{
+#define _(sym,str) P2PE_ERROR_##sym,
+  foreach_p2p_ethernet_error
+#undef _
+    P2PE_N_ERROR,
+} p2p_ethernet_error_t;
+
+static char *p2p_ethernet_error_strings[] = {
+#define _(sym,string) string,
+  foreach_p2p_ethernet_error
+#undef _
+};
+
+static uword
+p2p_ethernet_input_node_fn (vlib_main_t * vm,
+                           vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  u32 n_trace = vlib_get_trace_count (vm, node);
+  u32 n_left_from, *from, *to_next;
+  u32 next_index;
+  u32 n_p2p_ethernet_packets = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_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 >= 4 && n_left_to_next >= 2)
+       {
+         u32 bi0, bi1;
+         vlib_buffer_t *b0, *b1;
+         u32 next0 = 0, next1 = 0;
+         u32 sw_if_index0, sw_if_index1;
+         ethernet_header_t *en0, *en1;
+         u32 rx0, rx1;
+
+         bi0 = from[0];
+         bi1 = from[1];
+         to_next[0] = bi0;
+         to_next[1] = bi1;
+         from += 2;
+         to_next += 2;
+         n_left_to_next -= 2;
+         n_left_from -= 2;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+         en0 = vlib_buffer_get_current (b0);
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+         en1 = vlib_buffer_get_current (b1);
+         sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+
+         vnet_feature_next (sw_if_index0, &next0, b0);
+         vnet_feature_next (sw_if_index1, &next1, b1);
+
+         rx0 = p2p_ethernet_lookup (sw_if_index0, en0->src_address);
+         rx1 = p2p_ethernet_lookup (sw_if_index1, en1->src_address);
+
+         if (rx0 != ~0)
+           {
+             /* Send pkt to p2p_ethernet RX interface */
+             vnet_buffer (b0)->sw_if_index[VLIB_RX] = rx0;
+             n_p2p_ethernet_packets += 1;
+
+             if (PREDICT_FALSE (n_trace > 0))
+               {
+                 p2p_ethernet_trace_t *t0;
+                 vlib_trace_buffer (vm, node, next_index, b0,
+                                    1 /* follow_chain */ );
+                 vlib_set_trace_count (vm, node, --n_trace);
+                 t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+                 t0->sw_if_index = sw_if_index0;
+                 t0->p2pe_sw_if_index = rx0;
+               }
+           }
+         if (rx1 != ~0)
+           {
+             /* Send pkt to p2p_ethernet RX interface */
+             vnet_buffer (b1)->sw_if_index[VLIB_RX] = rx1;
+             n_p2p_ethernet_packets += 1;
+
+             if (PREDICT_FALSE (n_trace > 0))
+               {
+                 p2p_ethernet_trace_t *t1;
+                 vlib_trace_buffer (vm, node, next_index, b1,
+                                    1 /* follow_chain */ );
+                 vlib_set_trace_count (vm, node, --n_trace);
+                 t1 = vlib_add_trace (vm, node, b1, sizeof (*t1));
+                 t1->sw_if_index = sw_if_index1;
+                 t1->p2pe_sw_if_index = rx1;
+               }
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi1, next1);
+       }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         u32 next0 = 0;
+         u32 sw_if_index0;
+         ethernet_header_t *en0;
+         u32 rx0;
+
+         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);
+
+         en0 = vlib_buffer_get_current (b0);
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+         vnet_feature_next (sw_if_index0, &next0, b0);
+
+         rx0 = p2p_ethernet_lookup (sw_if_index0, en0->src_address);
+         if (rx0 != ~0)
+           {
+             /* Send pkt to p2p_ethernet RX interface */
+             vnet_buffer (b0)->sw_if_index[VLIB_RX] = rx0;
+             n_p2p_ethernet_packets += 1;
+
+             if (PREDICT_FALSE (n_trace > 0))
+               {
+                 p2p_ethernet_trace_t *t0;
+                 vlib_trace_buffer (vm, node, next_index, b0,
+                                    1 /* follow_chain */ );
+                 vlib_set_trace_count (vm, node, --n_trace);
+                 t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+                 t0->sw_if_index = sw_if_index0;
+                 t0->p2pe_sw_if_index = rx0;
+               }
+           }
+         else
+           {
+             if (PREDICT_FALSE (n_trace > 0))
+               {
+                 node->flags |= VLIB_NODE_FLAG_TRACE;
+               }
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         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);
+    }
+
+  vlib_node_increment_counter (vm, p2p_ethernet_input_node.index,
+                              P2PE_ERROR_HITS, n_p2p_ethernet_packets);
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (p2p_ethernet_input_node) = {
+  .function = p2p_ethernet_input_node_fn,
+  .name = "p2p-ethernet-input",
+  .vector_size = sizeof (u32),
+  .format_trace = format_p2p_ethernet_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(p2p_ethernet_error_strings),
+  .error_strings = p2p_ethernet_error_strings,
+
+  .n_next_nodes = 1,
+
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [0] = "error-drop",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (p2p_ethernet_input_node,
+                             p2p_ethernet_input_node_fn)
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index dad1f31..721259a 100644 (file)
@@ -1151,6 +1151,10 @@ vnet_hw_interface_compare (vnet_main_t * vnm,
 int
 vnet_sw_interface_is_p2p (vnet_main_t * vnm, u32 sw_if_index)
 {
+  vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
+  if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
+    return 1;
+
   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
   vnet_hw_interface_class_t *hc =
     vnet_get_hw_interface_class (vnm, hw->hw_class_index);
index fb75ff3..5ca489d 100644 (file)
@@ -505,6 +505,7 @@ typedef enum
 
   /* A sub-interface. */
   VNET_SW_INTERFACE_TYPE_SUB,
+  VNET_SW_INTERFACE_TYPE_P2P,
 } vnet_sw_interface_type_t;
 
 typedef struct
@@ -538,6 +539,17 @@ typedef struct
   } eth;
 } vnet_sub_interface_t;
 
+typedef struct
+{
+  /*
+   * Subinterface ID. A number 0-N to uniquely identify
+   * this subinterface under the main interface
+   */
+  u32 id;
+  u32 pool_index;
+  u8 client_mac[6];
+} vnet_p2p_sub_interface_t;
+
 typedef enum
 {
   /* Always flood */
@@ -594,6 +606,9 @@ typedef struct
 
     /* VNET_SW_INTERFACE_TYPE_SUB. */
     vnet_sub_interface_t sub;
+
+    /* VNET_SW_INTERFACE_TYPE_P2P. */
+    vnet_p2p_sub_interface_t p2p;
   };
 
   vnet_flood_class_t flood_class;
index 26eef9b..142bef5 100644 (file)
@@ -73,7 +73,8 @@ always_inline vnet_sw_interface_t *
 vnet_get_sup_sw_interface (vnet_main_t * vnm, u32 sw_if_index)
 {
   vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
-  if (sw->type == VNET_SW_INTERFACE_TYPE_SUB)
+  if (sw->type == VNET_SW_INTERFACE_TYPE_SUB ||
+      sw->type == VNET_SW_INTERFACE_TYPE_P2P)
     sw = vnet_get_sw_interface (vnm, sw->sup_sw_if_index);
   return sw;
 }
diff --git a/test/test_p2p_ethernet.py b/test/test_p2p_ethernet.py
new file mode 100644 (file)
index 0000000..37a1d18
--- /dev/null
@@ -0,0 +1,538 @@
+#!/usr/bin/env python
+import random
+import unittest
+import datetime
+import re
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IPv6
+
+from framework import VppTestCase, VppTestRunner, running_extended_tests
+from vpp_sub_interface import VppP2PSubint
+from vpp_ip_route import VppIpRoute, VppRoutePath
+from util import mactobinary
+
+
+class P2PEthernetAPI(VppTestCase):
+    """P2P Ethernet tests"""
+
+    p2p_sub_ifs = []
+
+    @classmethod
+    def setUpClass(cls):
+        super(P2PEthernetAPI, cls).setUpClass()
+
+        # Create pg interfaces
+        cls.create_pg_interfaces(range(4))
+
+        # Set up all interfaces
+        for i in cls.pg_interfaces:
+            i.admin_up()
+
+    def create_p2p_ethernet(self, parent_if, sub_id, remote_mac):
+        p2p = VppP2PSubint(self, parent_if, sub_id, mactobinary(remote_mac))
+        self.p2p_sub_ifs.append(p2p)
+
+    def delete_p2p_ethernet(self, parent_if, remote_mac):
+        self.vapi.delete_p2pethernet_subif(parent_if.sw_if_index,
+                                           mactobinary(remote_mac))
+
+    def test_api(self):
+        """delete/create p2p subif"""
+        self.logger.info("FFP_TEST_START_0000")
+
+        self.create_p2p_ethernet(self.pg0, 1, "de:ad:00:00:00:01")
+        self.create_p2p_ethernet(self.pg0, 2, "de:ad:00:00:00:02")
+        intfs = self.vapi.cli("show interface")
+
+        self.assertNotEqual(intfs.find('pg0.1'), -1)
+        self.assertNotEqual(intfs.find('pg0.2'), -1)
+        self.assertEqual(intfs.find('pg0.5'), -1)
+
+        # create pg2.5 subif
+        self.create_p2p_ethernet(self.pg0, 5, "de:ad:00:00:00:ff")
+        intfs = self.vapi.cli("show interface")
+        self.assertNotEqual(intfs.find('pg0.5'), -1)
+        # delete pg2.5 subif
+        self.delete_p2p_ethernet(self.pg0, "de:ad:00:00:00:ff")
+
+        intfs = self.vapi.cli("show interface")
+
+        self.assertNotEqual(intfs.find('pg0.1'), -1)
+        self.assertNotEqual(intfs.find('pg0.2'), -1)
+        self.assertEqual(intfs.find('pg0.5'), -1)
+
+        self.logger.info("FFP_TEST_FINISH_0000")
+
+    def test_p2p_subif_creation_1k(self):
+        """create 1k of p2p subifs"""
+        self.logger.info("FFP_TEST_START_0001")
+
+        macs = []
+        clients = 1000
+        mac = int("dead00000000", 16)
+
+        for i in range(1, clients+1):
+            try:
+                macs.append(':'.join(re.findall('..', '{:02x}'.format(mac+i))))
+                self.vapi.create_p2pethernet_subif(self.pg2.sw_if_index,
+                                                   mactobinary(macs[i-1]),
+                                                   i)
+            except Exception:
+                print "Failed to create subif %d %s" % (i, macs[i-1])
+                raise
+
+        intfs = self.vapi.cli("show interface").split("\n")
+        count = 0
+        for intf in intfs:
+            if intf.startswith('pg2.'):
+                count += 1
+        self.assertEqual(count, clients)
+
+        self.logger.info("FFP_TEST_FINISH_0001")
+
+    @unittest.skipUnless(running_extended_tests(), "part of extended tests")
+    def test_p2p_subif_creation_10k(self):
+        """create 100k of p2p subifs"""
+        self.logger.info("FFP_TEST_START_0001")
+
+        macs = []
+        clients = 100000
+        mac = int("dead00000000", 16)
+
+        s_time = datetime.datetime.now()
+        for i in range(1, clients+1):
+            if i % 1000 == 0:
+                e_time = datetime.datetime.now()
+                print "Created 1000 subifs in %s secs" % (e_time - s_time)
+                s_time = e_time
+            try:
+                macs.append(':'.join(re.findall('..', '{:02x}'.format(mac+i))))
+                self.vapi.create_p2pethernet_subif(self.pg3.sw_if_index,
+                                                   mactobinary(macs[i-1]),
+                                                   i)
+            except Exception:
+                print "Failed to create subif %d %s" % (i, macs[i-1])
+                raise
+
+        intfs = self.vapi.cli("show interface").split("\n")
+        count = 0
+        for intf in intfs:
+            if intf.startswith('pg3.'):
+                count += 1
+        self.assertEqual(count, clients)
+
+        self.logger.info("FFP_TEST_FINISH_0001")
+
+
+class P2PEthernetIPV6(VppTestCase):
+    """P2P Ethernet IPv6 tests"""
+
+    p2p_sub_ifs = []
+    packets = []
+
+    @classmethod
+    def setUpClass(cls):
+        super(P2PEthernetIPV6, cls).setUpClass()
+
+        # Create pg interfaces
+        cls.create_pg_interfaces(range(3))
+
+        # Packet sizes
+        cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
+
+        # Set up all interfaces
+        for i in cls.pg_interfaces:
+            i.admin_up()
+
+        cls.pg0.generate_remote_hosts(3)
+        cls.pg0.configure_ipv6_neighbors()
+
+        cls.pg1.config_ip6()
+        cls.pg1.generate_remote_hosts(3)
+        cls.pg1.configure_ipv6_neighbors()
+        cls.pg1.disable_ipv6_ra()
+
+    def setUp(self):
+        super(P2PEthernetIPV6, self).setUp()
+        for p in self.packets:
+            self.packets.remove(p)
+        self.create_p2p_ethernet(self.pg0, 1, self.pg0._remote_hosts[0].mac)
+        self.create_p2p_ethernet(self.pg0, 2, self.pg0._remote_hosts[1].mac)
+        self.p2p_sub_ifs[0].config_ip6()
+        self.p2p_sub_ifs[1].config_ip6()
+        self.vapi.cli("trace add p2p-ethernet-input 50")
+
+    def tearDown(self):
+        self.delete_p2p_ethernet(self.pg0, self.pg0._remote_hosts[0].mac)
+        self.delete_p2p_ethernet(self.pg0, self.pg0._remote_hosts[1].mac)
+        super(P2PEthernetIPV6, self).tearDown()
+
+    def create_p2p_ethernet(self, parent_if, sub_id, remote_mac):
+        p2p = VppP2PSubint(self, parent_if, sub_id, mactobinary(remote_mac))
+        p2p.admin_up()
+        p2p.config_ip6()
+        p2p.disable_ipv6_ra()
+        self.p2p_sub_ifs.append(p2p)
+
+    def delete_p2p_ethernet(self, parent_if, remote_mac):
+        self.vapi.delete_p2pethernet_subif(parent_if.sw_if_index,
+                                           mactobinary(remote_mac))
+
+    def create_stream(self, src_mac=None, dst_mac=None,
+                      src_ip=None, dst_ip=None, size=None):
+        pkt_size = size
+        if size is None:
+            pkt_size = random.choice(self.pg_if_packet_sizes)
+        p = Ether(src=src_mac, dst=dst_mac)
+        p /= IPv6(src=src_ip, dst=dst_ip)
+        p /= (UDP(sport=1234, dport=4321) / Raw('\xa5' * 20))
+        self.extend_packet(p, pkt_size)
+        return p
+
+    def send_packets(self, src_if=None, dst_if=None, packets=None, count=None):
+        self.pg_enable_capture([dst_if])
+        if packets is None:
+            packets = self.packets
+        src_if.add_stream(packets)
+        self.pg_start()
+        if count is None:
+            count = len(packets)
+        return dst_if.get_capture(count)
+
+    def verify_counters(self, counter_id, expected_value):
+        counters = self.vapi.cli("sh errors").split('\n')
+        counter_value = -1
+        for i in range(1, len(counters)-1):
+            results = counters[i].split()
+            if results[1] == counter_id:
+                counter_value = int(results[0])
+                break
+        self.assertEqual(counter_value, expected_value)
+
+    def test_no_p2p_subif(self):
+        """standard routing without p2p subinterfaces"""
+        self.logger.info("FFP_TEST_START_0001")
+
+        route_8000 = VppIpRoute(self, "8000::", 64,
+                                [VppRoutePath(self.pg0.remote_ip6,
+                                              self.pg0.sw_if_index,
+                                              is_ip6=1)],
+                                is_ip6=1)
+        route_8000.add_vpp_config()
+
+        self.packets = [(Ether(dst=self.pg1.local_mac,
+                               src=self.pg1.remote_mac) /
+                         IPv6(src="3001::1", dst="8000::100") /
+                         UDP(sport=1234, dport=1234) /
+                         Raw('\xa5' * 100))]
+        self.send_packets(self.pg1, self.pg0)
+
+        self.logger.info("FFP_TEST_FINISH_0001")
+
+    def test_ip6_rx_p2p_subif(self):
+        """receive ipv6 packet via p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0002")
+
+        route_9001 = VppIpRoute(self, "9001::", 64,
+                                [VppRoutePath(self.pg1.remote_ip6,
+                                              self.pg1.sw_if_index,
+                                              is_ip6=1)],
+                                is_ip6=1)
+        route_9001.add_vpp_config()
+
+        self.packets.append(
+            self.create_stream(src_mac=self.pg0._remote_hosts[0].mac,
+                               dst_mac=self.pg0.local_mac,
+                               src_ip=self.p2p_sub_ifs[0].remote_ip6,
+                               dst_ip="9001::100"))
+
+        self.send_packets(self.pg0, self.pg1, self.packets)
+        self.verify_counters('p2p-ethernet-input', 1)
+
+        route_9001.remove_vpp_config()
+        self.logger.info("FFP_TEST_FINISH_0002")
+
+    def test_ip6_rx_p2p_subif_route(self):
+        """route rx ip6 packet not matching p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0003")
+
+        self.pg0.config_ip6()
+
+        route_3 = VppIpRoute(self, "9000::", 64,
+                             [VppRoutePath(self.pg1._remote_hosts[0].ip6,
+                                           self.pg1.sw_if_index,
+                                           is_ip6=1)],
+                             is_ip6=1)
+        route_3.add_vpp_config()
+
+        self.packets.append(
+            self.create_stream(src_mac="02:03:00:00:ff:ff",
+                               dst_mac=self.pg0.local_mac,
+                               src_ip="a000::100",
+                               dst_ip="9000::100"))
+
+        self.send_packets(self.pg0, self.pg1)
+
+        self.pg0.unconfig_ip6()
+
+        route_3.remove_vpp_config()
+
+        self.logger.info("FFP_TEST_FINISH_0003")
+
+    def test_ip6_rx_p2p_subif_drop(self):
+        """drop rx packet not matching p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0004")
+
+        route_9001 = VppIpRoute(self, "9000::", 64,
+                                [VppRoutePath(self.pg1._remote_hosts[0].ip6,
+                                              self.pg1.sw_if_index,
+                                              is_ip6=1)],
+                                is_ip6=1)
+        route_9001.add_vpp_config()
+
+        self.packets.append(
+            self.create_stream(src_mac="02:03:00:00:ff:ff",
+                               dst_mac=self.pg0.local_mac,
+                               src_ip="a000::100",
+                               dst_ip="9000::100"))
+
+        # no packet received
+        self.send_packets(self.pg0, self.pg1, count=0)
+        self.logger.info("FFP_TEST_FINISH_0004")
+
+    def test_ip6_tx_p2p_subif(self):
+        """send packet via p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0005")
+
+        route_8000 = VppIpRoute(self, "8000::", 64,
+                                [VppRoutePath(self.pg0.remote_ip6,
+                                              self.pg0.sw_if_index,
+                                              is_ip6=1)],
+                                is_ip6=1)
+        route_8000.add_vpp_config()
+        route_8001 = VppIpRoute(self, "8001::", 64,
+                                [VppRoutePath(self.p2p_sub_ifs[0].remote_ip6,
+                                              self.p2p_sub_ifs[0].sw_if_index,
+                                              is_ip6=1)],
+                                is_ip6=1)
+        route_8001.add_vpp_config()
+        route_8002 = VppIpRoute(self, "8002::", 64,
+                                [VppRoutePath(self.p2p_sub_ifs[1].remote_ip6,
+                                              self.p2p_sub_ifs[1].sw_if_index,
+                                              is_ip6=1)],
+                                is_ip6=1)
+        route_8002.add_vpp_config()
+
+        for i in range(0, 3):
+            self.packets.append(
+                self.create_stream(src_mac=self.pg1.remote_mac,
+                                   dst_mac=self.pg1.local_mac,
+                                   src_ip=self.pg1.remote_ip6,
+                                   dst_ip="800%d::100" % i))
+
+        self.send_packets(self.pg1, self.pg0, count=3)
+
+        route_8000.remove_vpp_config()
+        route_8001.remove_vpp_config()
+        route_8002.remove_vpp_config()
+
+        self.logger.info("FFP_TEST_FINISH_0005")
+
+    def test_ip6_tx_p2p_subif_drop(self):
+        """drop tx ip6 packet not matching p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0006")
+
+        self.packets.append(
+            self.create_stream(src_mac="02:03:00:00:ff:ff",
+                               dst_mac=self.pg0.local_mac,
+                               src_ip="a000::100",
+                               dst_ip="9000::100"))
+
+        # no packet received
+        self.send_packets(self.pg0, self.pg1, count=0)
+        self.logger.info("FFP_TEST_FINISH_0006")
+
+
+class P2PEthernetIPV4(VppTestCase):
+    """P2P Ethernet IPv4 tests"""
+
+    p2p_sub_ifs = []
+    packets = []
+
+    @classmethod
+    def setUpClass(cls):
+        super(P2PEthernetIPV4, cls).setUpClass()
+
+        # Create pg interfaces
+        cls.create_pg_interfaces(range(3))
+
+        # Packet sizes
+        cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
+
+        # Set up all interfaces
+        for i in cls.pg_interfaces:
+            i.admin_up()
+
+        cls.pg0.config_ip4()
+        cls.pg0.generate_remote_hosts(5)
+        cls.pg0.configure_ipv4_neighbors()
+
+        cls.pg1.config_ip4()
+        cls.pg1.generate_remote_hosts(5)
+        cls.pg1.configure_ipv4_neighbors()
+
+    def setUp(self):
+        super(P2PEthernetIPV4, self).setUp()
+        for p in self.packets:
+            self.packets.remove(p)
+        self.create_p2p_ethernet(self.pg0, 1, self.pg0._remote_hosts[0].mac)
+        self.create_p2p_ethernet(self.pg0, 2, self.pg0._remote_hosts[1].mac)
+        self.p2p_sub_ifs[0].config_ip4()
+        self.p2p_sub_ifs[1].config_ip4()
+        self.vapi.cli("trace add p2p-ethernet-input 50")
+
+    def tearDown(self):
+        self.delete_p2p_ethernet(self.pg0, self.pg0._remote_hosts[0].mac)
+        self.delete_p2p_ethernet(self.pg0, self.pg0._remote_hosts[1].mac)
+        super(P2PEthernetIPV4, self).tearDown()
+
+    def create_stream(self, src_mac=None, dst_mac=None,
+                      src_ip=None, dst_ip=None, size=None):
+        pkt_size = size
+        if size is None:
+            pkt_size = random.choice(self.pg_if_packet_sizes)
+        p = Ether(src=src_mac, dst=dst_mac)
+        p /= IP(src=src_ip, dst=dst_ip)
+        p /= (UDP(sport=1234, dport=4321) / Raw('\xa5' * 20))
+        self.extend_packet(p, pkt_size)
+        return p
+
+    def send_packets(self, src_if=None, dst_if=None, packets=None, count=None):
+        self.pg_enable_capture([dst_if])
+        if packets is None:
+            packets = self.packets
+        src_if.add_stream(packets)
+        self.pg_start()
+        if count is None:
+            count = len(packets)
+        return dst_if.get_capture(count)
+
+    def verify_counters(self, counter_id, expected_value):
+        counters = self.vapi.cli("sh errors").split('\n')
+        counter_value = -1
+        for i in range(1, len(counters)-1):
+            results = counters[i].split()
+            if results[1] == counter_id:
+                counter_value = int(results[0])
+                break
+        self.assertEqual(counter_value, expected_value)
+
+    def create_p2p_ethernet(self, parent_if, sub_id, remote_mac):
+        p2p = VppP2PSubint(self, parent_if, sub_id, mactobinary(remote_mac))
+        p2p.admin_up()
+        p2p.config_ip4()
+        self.p2p_sub_ifs.append(p2p)
+
+    def delete_p2p_ethernet(self, parent_if, remote_mac):
+        self.vapi.delete_p2pethernet_subif(parent_if.sw_if_index,
+                                           mactobinary(remote_mac))
+
+    def test_ip4_rx_p2p_subif(self):
+        """receive ipv4 packet via p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0002")
+
+        route_9000 = VppIpRoute(self, "9.0.0.0", 16,
+                                [VppRoutePath(self.pg1.remote_ip4,
+                                              self.pg1.sw_if_index)])
+        route_9000.add_vpp_config()
+
+        self.packets.append(
+            self.create_stream(src_mac=self.pg0._remote_hosts[0].mac,
+                               dst_mac=self.pg0.local_mac,
+                               src_ip=self.p2p_sub_ifs[0].remote_ip4,
+                               dst_ip="9.0.0.100"))
+
+        self.send_packets(self.pg0, self.pg1, self.packets)
+
+        self.verify_counters('p2p-ethernet-input', 1)
+
+        route_9000.remove_vpp_config()
+        self.logger.info("FFP_TEST_FINISH_0002")
+
+    def test_ip4_rx_p2p_subif_route(self):
+        """route rx packet not matching p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0003")
+
+        route_9001 = VppIpRoute(self, "9.0.0.0", 24,
+                                [VppRoutePath(self.pg1.remote_ip4,
+                                              self.pg1.sw_if_index)])
+        route_9001.add_vpp_config()
+
+        self.packets.append(
+            self.create_stream(src_mac="02:01:00:00:ff:ff",
+                               dst_mac=self.pg0.local_mac,
+                               src_ip="8.0.0.100",
+                               dst_ip="9.0.0.100"))
+
+        self.send_packets(self.pg0, self.pg1)
+
+        route_9001.remove_vpp_config()
+
+        self.logger.info("FFP_TEST_FINISH_0003")
+
+    def test_ip4_tx_p2p_subif(self):
+        """send ip4 packet via p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0005")
+
+        route_9100 = VppIpRoute(self, "9.1.0.100", 24,
+                                [VppRoutePath(self.pg0.remote_ip4,
+                                              self.pg0.sw_if_index,
+                                              )])
+        route_9100.add_vpp_config()
+        route_9200 = VppIpRoute(self, "9.2.0.100", 24,
+                                [VppRoutePath(self.p2p_sub_ifs[0].remote_ip4,
+                                              self.p2p_sub_ifs[0].sw_if_index,
+                                              )])
+        route_9200.add_vpp_config()
+        route_9300 = VppIpRoute(self, "9.3.0.100", 24,
+                                [VppRoutePath(self.p2p_sub_ifs[1].remote_ip4,
+                                              self.p2p_sub_ifs[1].sw_if_index
+                                              )])
+        route_9300.add_vpp_config()
+
+        for i in range(0, 3):
+            self.packets.append(
+                self.create_stream(src_mac=self.pg1.remote_mac,
+                                   dst_mac=self.pg1.local_mac,
+                                   src_ip=self.pg1.remote_ip4,
+                                   dst_ip="9.%d.0.100" % (i+1)))
+
+        self.send_packets(self.pg1, self.pg0)
+
+        # route_7000.remove_vpp_config()
+        route_9100.remove_vpp_config()
+        route_9200.remove_vpp_config()
+        route_9300.remove_vpp_config()
+
+        self.logger.info("FFP_TEST_FINISH_0005")
+
+    def test_ip4_tx_p2p_subif_drop(self):
+        """drop tx ip4 packet not matching p2p subinterface"""
+        self.logger.info("FFP_TEST_START_0006")
+
+        self.packets.append(
+            self.create_stream(src_mac="02:01:00:00:ff:ff",
+                               dst_mac=self.pg0.local_mac,
+                               src_ip="8.0.0.100",
+                               dst_ip="9.0.0.100"))
+
+        # no packet received
+        self.send_packets(self.pg0, self.pg1, count=0)
+        self.logger.info("FFP_TEST_FINISH_0006")
+
+
+if __name__ == '__main__':
+    unittest.main(testRunner=VppTestRunner)
index 204d9e3..801a6c2 100644 (file)
@@ -601,6 +601,19 @@ class VppPapiProvider(object):
              'outer_vlan_id': outer_vlan,
              'inner_vlan_id': inner_vlan})
 
+    def create_p2pethernet_subif(self, sw_if_index, remote_mac, subif_id):
+        """Create p2p ethernet subinterface
+
+        :param sw_if_index: main (parent) interface
+        :param remote_mac: client (remote) mac address
+
+        """
+        return self.api(
+            self.papi.p2p_ethernet_add,
+            {'parent_if_index': sw_if_index,
+             'remote_mac': remote_mac,
+             'subif_id': subif_id})
+
     def delete_subif(self, sw_if_index):
         """Delete subinterface
 
@@ -609,6 +622,18 @@ class VppPapiProvider(object):
         return self.api(self.papi.delete_subif,
                         {'sw_if_index': sw_if_index})
 
+    def delete_p2pethernet_subif(self, sw_if_index, remote_mac):
+        """Delete p2p ethernet subinterface
+
+        :param sw_if_index: main (parent) interface
+        :param remote_mac: client (remote) mac address
+
+        """
+        return self.api(
+            self.papi.p2p_ethernet_del,
+            {'parent_if_index': sw_if_index,
+             'remote_mac': remote_mac})
+
     def create_vlan_subif(self, sw_if_index, vlan):
         """
 
index dcd82da..cabee88 100644 (file)
@@ -188,3 +188,26 @@ class VppDot1ADSubint(VppSubInterface):
     def remove_dot1_layer(self, packet):
         return self.remove_dot1ad_layer(packet, self.outer_vlan,
                                         self.inner_vlan)
+
+
+class VppP2PSubint(VppSubInterface):
+
+    def __init__(self, test, parent, sub_id, remote_mac):
+        r = test.vapi.create_p2pethernet_subif(parent.sw_if_index,
+                                               remote_mac, sub_id)
+        self._sw_if_index = r.sw_if_index
+        super(VppP2PSubint, self).__init__(test, parent, sub_id)
+
+    def add_dot1_layer(self, packet):
+        return packet
+
+    def remove_dot1_layer(self, packet):
+        return packet
+
+    def create_arp_req(self):
+        packet = VppPGInterface.create_arp_req(self)
+        return packet
+
+    def create_ndp_req(self):
+        packet = VppPGInterface.create_ndp_req(self)
+        return packet