diff options
-rw-r--r-- | src/vat/api_format.c | 17 | ||||
-rw-r--r-- | src/vnet.am | 1 | ||||
-rw-r--r-- | src/vnet/api_errno.h | 3 | ||||
-rw-r--r-- | src/vnet/devices/devices.c | 6 | ||||
-rw-r--r-- | src/vnet/ethernet/ethernet.h | 1 | ||||
-rw-r--r-- | src/vnet/ethernet/interface.c | 54 | ||||
-rwxr-xr-x | src/vnet/ethernet/node.c | 17 | ||||
-rw-r--r-- | src/vnet/ethernet/p2p_ethernet.api | 1 | ||||
-rw-r--r-- | src/vnet/ethernet/p2p_ethernet.c | 165 | ||||
-rw-r--r-- | src/vnet/ethernet/p2p_ethernet.h | 42 | ||||
-rw-r--r-- | src/vnet/ethernet/p2p_ethernet_api.c | 17 | ||||
-rw-r--r-- | src/vnet/ethernet/p2p_ethernet_input.c | 247 | ||||
-rw-r--r-- | src/vnet/interface.c | 4 | ||||
-rw-r--r-- | src/vnet/interface.h | 15 | ||||
-rw-r--r-- | src/vnet/interface_funcs.h | 3 | ||||
-rw-r--r-- | test/test_p2p_ethernet.py | 538 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 25 | ||||
-rw-r--r-- | test/vpp_sub_interface.py | 23 |
18 files changed, 1145 insertions, 34 deletions
diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 5a0c4580..6a2d36de 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -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) \ diff --git a/src/vnet.am b/src/vnet.am index 060e3f38..ad84c028 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -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 += \ diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index b22bb3a8..747c65e7 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -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 { diff --git a/src/vnet/devices/devices.c b/src/vnet/devices/devices.c index f64c6e0d..2eb8e30e 100644 --- a/src/vnet/devices/devices.c +++ b/src/vnet/devices/devices.c @@ -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", diff --git a/src/vnet/ethernet/ethernet.h b/src/vnet/ethernet/ethernet.h index d9ab8c10..9ca256c9 100644 --- a/src/vnet/ethernet/ethernet.h +++ b/src/vnet/ethernet/ethernet.h @@ -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; diff --git a/src/vnet/ethernet/interface.c b/src/vnet/ethernet/interface.c index 9ac30bc6..3e78a49d 100644 --- a/src/vnet/ethernet/interface.c +++ b/src/vnet/ethernet/interface.c @@ -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 = diff --git a/src/vnet/ethernet/node.c b/src/vnet/ethernet/node.c index 421d501a..f216216d 100755 --- a/src/vnet/ethernet/node.c +++ b/src/vnet/ethernet/node.c @@ -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 | diff --git a/src/vnet/ethernet/p2p_ethernet.api b/src/vnet/ethernet/p2p_ethernet.api index 72a73423..8fb66376 100644 --- a/src/vnet/ethernet/p2p_ethernet.api +++ b/src/vnet/ethernet/p2p_ethernet.api @@ -18,6 +18,7 @@ define p2p_ethernet_add u32 client_index; u32 context; u32 parent_if_index; + u32 subif_id; u8 remote_mac[6]; }; diff --git a/src/vnet/ethernet/p2p_ethernet.c b/src/vnet/ethernet/p2p_ethernet.c index 3c077318..e3f667b5 100644 --- a/src/vnet/ethernet/p2p_ethernet.c +++ b/src/vnet/ethernet/p2p_ethernet.c @@ -18,12 +18,152 @@ #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; } diff --git a/src/vnet/ethernet/p2p_ethernet.h b/src/vnet/ethernet/p2p_ethernet.h index 31b93d82..bb1e2896 100644 --- a/src/vnet/ethernet/p2p_ethernet.h +++ b/src/vnet/ethernet/p2p_ethernet.h @@ -18,6 +18,46 @@ #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 */ diff --git a/src/vnet/ethernet/p2p_ethernet_api.c b/src/vnet/ethernet/p2p_ethernet_api.c index 1d9eaeb0..f2c730b4 100644 --- a/src/vnet/ethernet/p2p_ethernet_api.c +++ b/src/vnet/ethernet/p2p_ethernet_api.c @@ -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 index 00000000..a58b832a --- /dev/null +++ b/src/vnet/ethernet/p2p_ethernet_input.c @@ -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: + */ diff --git a/src/vnet/interface.c b/src/vnet/interface.c index dad1f315..721259a7 100644 --- a/src/vnet/interface.c +++ b/src/vnet/interface.c @@ -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); diff --git a/src/vnet/interface.h b/src/vnet/interface.h index fb75ff34..5ca489db 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -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; diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index 26eef9b9..142bef57 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -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 index 00000000..37a1d18b --- /dev/null +++ b/test/test_p2p_ethernet.py @@ -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) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 204d9e31..801a6c2d 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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): """ diff --git a/test/vpp_sub_interface.py b/test/vpp_sub_interface.py index dcd82da2..cabee88d 100644 --- a/test/vpp_sub_interface.py +++ b/test/vpp_sub_interface.py @@ -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 |