From 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 19 Dec 2016 23:05:39 +0100 Subject: Reorganize source tree to use single autotools instance Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion --- src/vnet/interface_funcs.h | 318 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 src/vnet/interface_funcs.h (limited to 'src/vnet/interface_funcs.h') diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h new file mode 100644 index 00000000..b84d151c --- /dev/null +++ b/src/vnet/interface_funcs.h @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2015 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. + */ +/* + * interface_funcs.h: VNET interfaces/sub-interfaces exported functions + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef included_vnet_interface_funcs_h +#define included_vnet_interface_funcs_h + +always_inline vnet_hw_interface_t * +vnet_get_hw_interface (vnet_main_t * vnm, u32 hw_if_index) +{ + return pool_elt_at_index (vnm->interface_main.hw_interfaces, hw_if_index); +} + +always_inline vnet_sw_interface_t * +vnet_get_sw_interface (vnet_main_t * vnm, u32 sw_if_index) +{ + return pool_elt_at_index (vnm->interface_main.sw_interfaces, sw_if_index); +} + +always_inline vnet_sw_interface_t * +vnet_get_hw_sw_interface (vnet_main_t * vnm, u32 hw_if_index) +{ + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); + vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, hw->sw_if_index); + ASSERT (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE); + return sw; +} + +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) + sw = vnet_get_sw_interface (vnm, sw->sup_sw_if_index); + return sw; +} + +always_inline vnet_hw_interface_t * +vnet_get_sup_hw_interface (vnet_main_t * vnm, u32 sw_if_index) +{ + vnet_sw_interface_t *sw = vnet_get_sup_sw_interface (vnm, sw_if_index); + ASSERT (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE); + return vnet_get_hw_interface (vnm, sw->hw_if_index); +} + +always_inline vnet_hw_interface_class_t * +vnet_get_hw_interface_class (vnet_main_t * vnm, u32 hw_class_index) +{ + return vec_elt_at_index (vnm->interface_main.hw_interface_classes, + hw_class_index); +} + +always_inline vnet_device_class_t * +vnet_get_device_class (vnet_main_t * vnm, u32 dev_class_index) +{ + return vec_elt_at_index (vnm->interface_main.device_classes, + dev_class_index); +} + +static inline u8 * +vnet_get_sw_interface_tag (vnet_main_t * vnm, u32 sw_if_index) +{ + uword *p; + p = hash_get (vnm->interface_tag_by_sw_if_index, sw_if_index); + if (p) + return ((u8 *) p[0]); + return 0; +} + +static inline void +vnet_set_sw_interface_tag (vnet_main_t * vnm, u8 * tag, u32 sw_if_index) +{ + uword *p; + p = hash_get (vnm->interface_tag_by_sw_if_index, sw_if_index); + if (p) + { + u8 *oldtag = (u8 *) p[0]; + hash_unset (vnm->interface_tag_by_sw_if_index, sw_if_index); + vec_free (oldtag); + } + + hash_set (vnm->interface_tag_by_sw_if_index, sw_if_index, tag); +} + +static inline void +vnet_clear_sw_interface_tag (vnet_main_t * vnm, u32 sw_if_index) +{ + uword *p; + p = hash_get (vnm->interface_tag_by_sw_if_index, sw_if_index); + if (p) + { + u8 *oldtag = (u8 *) p[0]; + hash_unset (vnm->interface_tag_by_sw_if_index, sw_if_index); + vec_free (oldtag); + } +} + +/** + * Call back walk type for walking SW indices on a HW interface + */ +typedef void (*vnet_hw_sw_interface_walk_t) (vnet_main_t * vnm, + u32 sw_if_index, void *ctx); + +/** + * @brief + * Walk the SW interfaces on a HW interface - this is the super + * interface and any sub-interfaces. + */ +void vnet_hw_interface_walk_sw (vnet_main_t * vnm, + u32 hw_if_index, + vnet_hw_sw_interface_walk_t fn, void *ctx); + +/* Register a hardware interface instance. */ +u32 vnet_register_interface (vnet_main_t * vnm, + u32 dev_class_index, + u32 dev_instance, + u32 hw_class_index, u32 hw_instance); + +/* Creates a software interface given template. */ +clib_error_t *vnet_create_sw_interface (vnet_main_t * vnm, + vnet_sw_interface_t * template, + u32 * sw_if_index); + +void vnet_delete_hw_interface (vnet_main_t * vnm, u32 hw_if_index); +void vnet_delete_sw_interface (vnet_main_t * vnm, u32 sw_if_index); +int vnet_sw_interface_is_p2p (vnet_main_t * vnm, u32 sw_if_index); + +always_inline uword +vnet_sw_interface_get_flags (vnet_main_t * vnm, u32 sw_if_index) +{ + vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index); + return sw->flags; +} + +always_inline uword +vnet_sw_interface_is_admin_up (vnet_main_t * vnm, u32 sw_if_index) +{ + return (vnet_sw_interface_get_flags (vnm, sw_if_index) & + VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0; +} + +always_inline uword +vnet_hw_interface_get_flags (vnet_main_t * vnm, u32 hw_if_index) +{ + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); + return hw->flags; +} + +always_inline uword +vnet_hw_interface_get_mtu (vnet_main_t * vnm, u32 hw_if_index, + vlib_rx_or_tx_t dir) +{ + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); + return hw->max_l3_packet_bytes[dir]; +} + +always_inline uword +vnet_sw_interface_get_mtu (vnet_main_t * vnm, u32 sw_if_index, + vlib_rx_or_tx_t dir) +{ + vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); + return (hw->max_l3_packet_bytes[dir]); +} + +always_inline uword +vnet_hw_interface_is_link_up (vnet_main_t * vnm, u32 hw_if_index) +{ + return (vnet_hw_interface_get_flags (vnm, hw_if_index) & + VNET_HW_INTERFACE_FLAG_LINK_UP) != 0; +} + +always_inline vlib_frame_t * +vnet_get_frame_to_sw_interface (vnet_main_t * vnm, u32 sw_if_index) +{ + vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); + return vlib_get_frame_to_node (vnm->vlib_main, hw->output_node_index); +} + +always_inline void +vnet_put_frame_to_sw_interface (vnet_main_t * vnm, u32 sw_if_index, + vlib_frame_t * f) +{ + vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); + return vlib_put_frame_to_node (vnm->vlib_main, hw->output_node_index, f); +} + +/* Change interface flags (e.g. up, down, enable, disable). */ +clib_error_t *vnet_hw_interface_set_flags (vnet_main_t * vnm, u32 hw_if_index, + u32 flags); + +/* Change interface flags (e.g. up, down, enable, disable). */ +clib_error_t *vnet_sw_interface_set_flags (vnet_main_t * vnm, u32 sw_if_index, + u32 flags); + +/* Change interface class. */ +clib_error_t *vnet_hw_interface_set_class (vnet_main_t * vnm, u32 hw_if_index, + u32 new_hw_class_index); + +/* Redirect rx pkts to node */ +int vnet_hw_interface_rx_redirect_to_node (vnet_main_t * vnm, u32 hw_if_index, + u32 node_index); + +void vnet_hw_interface_init_for_class (vnet_main_t * vnm, u32 hw_if_index, + u32 hw_class_index, u32 hw_instance); + +/* Rename interface */ +clib_error_t *vnet_rename_interface (vnet_main_t * vnm, u32 hw_if_index, + char *new_name); + +/* Change interface mac address*/ +clib_error_t *vnet_hw_interface_change_mac_address (vnet_main_t * vnm, + u32 hw_if_index, + u64 mac_address); + +/* Formats sw/hw interface. */ +format_function_t format_vnet_hw_interface; +format_function_t format_vnet_sw_interface; +format_function_t format_vnet_sw_interface_name; +format_function_t format_vnet_sw_interface_name_override; +format_function_t format_vnet_sw_if_index_name; +format_function_t format_vnet_sw_interface_flags; + +/* Parses sw/hw interface name -> index. */ +unformat_function_t unformat_vnet_sw_interface; +unformat_function_t unformat_vnet_hw_interface; + +/* Parses interface flags (up, down, enable, disable, etc.) */ +unformat_function_t unformat_vnet_hw_interface_flags; +unformat_function_t unformat_vnet_sw_interface_flags; + +/* Node runtime for interface output function. */ +typedef struct +{ + u32 hw_if_index; + u32 sw_if_index; + u32 dev_instance; + u32 is_deleted; +} vnet_interface_output_runtime_t; + +/* Interface output functions. */ +void *vnet_interface_output_node_multiarch_select (void); +void *vnet_interface_output_node_flatten_multiarch_select (void); + +word vnet_sw_interface_compare (vnet_main_t * vnm, uword sw_if_index0, + uword sw_if_index1); +word vnet_hw_interface_compare (vnet_main_t * vnm, uword hw_if_index0, + uword hw_if_index1); + +typedef enum +{ + VNET_INTERFACE_OUTPUT_NEXT_DROP, + VNET_INTERFACE_OUTPUT_NEXT_TX, +} vnet_interface_output_next_t; + +typedef enum +{ + VNET_INTERFACE_TX_NEXT_DROP, + VNET_INTERFACE_TX_N_NEXT, +} vnet_interface_tx_next_t; + +#define VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT VNET_INTERFACE_TX_N_NEXT + +typedef enum +{ + VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN, + VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED, +} vnet_interface_output_error_t; + +/* Format for interface output traces. */ +u8 *format_vnet_interface_output_trace (u8 * s, va_list * va); + +serialize_function_t serialize_vnet_interface_state, + unserialize_vnet_interface_state; + +#endif /* included_vnet_interface_funcs_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg From 75152289284aaf1116d62c6cdef5a3b0c793fa15 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 9 Jan 2017 01:00:45 -0800 Subject: IPv6 NS RS tests and fixes includes Fix for VPP-584 with API change to remove prefix length from LL programming Change-Id: If860751c35e60255fb977f73bc33e8c2649e728e Signed-off-by: Neale Ranns --- src/vat/api_format.c | 5 +- src/vnet/interface_funcs.h | 8 ++ src/vnet/ip/icmp6.h | 4 - src/vnet/ip/ip.api | 2 - src/vnet/ip/ip6.h | 3 +- src/vnet/ip/ip6_forward.c | 6 +- src/vnet/ip/ip6_neighbor.c | 42 ++------ src/vnet/ip/ip_api.c | 3 +- src/vnet/rewrite.c | 7 +- src/vpp/api/custom_dump.c | 3 +- src/vpp/api/test_client.c | 2 - test/test_ip6.py | 237 ++++++++++++++++++++++++++++++++++++++++++++- test/vpp_interface.py | 14 +++ test/vpp_papi_provider.py | 16 +++ test/vpp_pg_interface.py | 5 +- 15 files changed, 300 insertions(+), 57 deletions(-) (limited to 'src/vnet/interface_funcs.h') diff --git a/src/vat/api_format.c b/src/vat/api_format.c index c00104de..75148207 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -7671,7 +7671,6 @@ api_sw_interface_ip6_set_link_local_address (vat_main_t * vam) f64 timeout; u32 sw_if_index; u8 sw_if_index_set = 0; - u32 address_length = 0; u8 v6_address_set = 0; ip6_address_t v6address; @@ -7682,8 +7681,7 @@ api_sw_interface_ip6_set_link_local_address (vat_main_t * vam) sw_if_index_set = 1; else if (unformat (i, "sw_if_index %d", &sw_if_index)) sw_if_index_set = 1; - else if (unformat (i, "%U/%d", - unformat_ip6_address, &v6address, &address_length)) + else if (unformat (i, "%U", unformat_ip6_address, &v6address)) v6_address_set = 1; else break; @@ -7706,7 +7704,6 @@ api_sw_interface_ip6_set_link_local_address (vat_main_t * vam) mp->sw_if_index = ntohl (sw_if_index); clib_memcpy (mp->address, &v6address, sizeof (v6address)); - mp->address_length = address_length; /* send it... */ S; diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index b84d151c..ab808dfa 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -52,6 +52,14 @@ vnet_get_sw_interface (vnet_main_t * vnm, u32 sw_if_index) return pool_elt_at_index (vnm->interface_main.sw_interfaces, sw_if_index); } +always_inline vnet_sw_interface_t * +vnet_get_sw_interface_safe (vnet_main_t * vnm, u32 sw_if_index) +{ + if (!pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index)) + return pool_elt_at_index (vnm->interface_main.sw_interfaces, sw_if_index); + return (NULL); +} + always_inline vnet_sw_interface_t * vnet_get_hw_sw_interface (vnet_main_t * vnm, u32 hw_if_index) { diff --git a/src/vnet/ip/icmp6.h b/src/vnet/ip/icmp6.h index a426512e..9a3487b1 100644 --- a/src/vnet/ip/icmp6.h +++ b/src/vnet/ip/icmp6.h @@ -37,10 +37,6 @@ "neighbor discovery unsupported interface") \ _ (ROUTER_SOLICITATION_RADV_NOT_CONFIG, \ "neighbor discovery not configured") \ - _ (ROUTER_SOLICITATION_DEST_UNKNOWN, \ - "router solicitations for unknown destination") \ - _ (ROUTER_SOLICITATION_SOURCE_UNKNOWN, \ - "router solicitations for unknown source") \ _ (ROUTER_ADVERTISEMENT_SOURCE_NOT_LINK_LOCAL, \ "router advertisement source not link local") \ _ (ROUTER_ADVERTISEMENTS_TX, "router advertisements sent") \ diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api index c811e465..f2444805 100644 --- a/src/vnet/ip/ip.api +++ b/src/vnet/ip/ip.api @@ -311,7 +311,6 @@ define sw_interface_ip6_enable_disable_reply @param context - sender context, to match reply w/ request @param sw_if_index - interface to set link local on @param address[] - the new link local address - @param address_length - link local address length */ define sw_interface_ip6_set_link_local_address { @@ -319,7 +318,6 @@ define sw_interface_ip6_set_link_local_address u32 context; u32 sw_if_index; u8 address[16]; - u8 address_length; }; /** \brief IPv6 set link local address on interface response diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h index 586b7c1b..f493db01 100644 --- a/src/vnet/ip/ip6.h +++ b/src/vnet/ip/ip6.h @@ -376,8 +376,7 @@ int ip6_interface_enabled (vlib_main_t * vm, u32 sw_if_index); clib_error_t *set_ip6_link_local_address (vlib_main_t * vm, u32 sw_if_index, - ip6_address_t * address, - u8 address_length); + ip6_address_t * address); void vnet_register_ip6_neighbor_resolution_event (vnet_main_t * vnm, void *address_arg, diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c index b5c79552..866a44e6 100644 --- a/src/vnet/ip/ip6_forward.c +++ b/src/vnet/ip/ip6_forward.c @@ -404,8 +404,10 @@ ip6_sw_interface_enable_disable (u32 sw_if_index, u32 is_enable) } else { - ASSERT (im->ip_enabled_by_sw_if_index[sw_if_index] > 0); - if (0 != --im->ip_enabled_by_sw_if_index[sw_if_index]) + /* The ref count is 0 when an address is removed from an interface that has + * no address - this is not a ciritical error */ + if (0 == im->ip_enabled_by_sw_if_index[sw_if_index] || + 0 != --im->ip_enabled_by_sw_if_index[sw_if_index]) return; } diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c index 5a1c9e86..46c0e316 100644 --- a/src/vnet/ip/ip6_neighbor.c +++ b/src/vnet/ip/ip6_neighbor.c @@ -155,8 +155,6 @@ typedef struct /* Link local address to use (defaults to underlying physical for logical interfaces */ ip6_address_t link_local_address; - u8 link_local_prefix_len; - } ip6_radv_t; typedef struct @@ -1316,7 +1314,8 @@ icmp6_router_solicitation (vlib_main_t * vm, /* for solicited adverts - need to rate limit */ if (is_solicitation) { - if ((now - radv_info->last_radv_time) < + if (0 != radv_info->last_radv_time && + (now - radv_info->last_radv_time) < MIN_DELAY_BETWEEN_RAS) is_dropped = 1; else @@ -1523,16 +1522,6 @@ icmp6_router_solicitation (vlib_main_t * vm, error0 = ICMP6_ERROR_DST_LOOKUP_MISS; else { - ip_adjacency_t *adj0 = - ip_get_adjacency (&im->lookup_main, - adj_index0); - error0 = - ((adj0->rewrite_header.sw_if_index != - sw_if_index0 - || adj0->lookup_next_index != - IP_LOOKUP_NEXT_REWRITE) ? - ICMP6_ERROR_ROUTER_SOLICITATION_DEST_UNKNOWN - : error0); next0 = is_dropped ? next0 : ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_RW; @@ -2022,7 +2011,6 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, /* fill in default link-local address (this may be overridden) */ ip6_link_local_address_from_ethernet_address (&a->link_local_address, eth_if0->address); - a->link_local_prefix_len = 64; mhash_init (&a->address_to_prefix_index, sizeof (uword), sizeof (ip6_address_t)); @@ -3266,9 +3254,7 @@ disable_ip6_interface (vlib_main_t * vm, u32 sw_if_index) /* essentially "disables" ipv6 on this interface */ error = ip6_add_del_interface_address (vm, sw_if_index, &radv_info-> - link_local_address, - radv_info-> - link_local_prefix_len, + link_local_address, 128, 1 /* is_del */ ); ip6_neighbor_sw_interface_add_del (vnm, sw_if_index, @@ -3372,7 +3358,6 @@ enable_ip6_interface (vlib_main_t * vm, u32 sw_if_index) else { radv_info->link_local_address = link_local_address; - radv_info->link_local_prefix_len = 64; } } } @@ -3585,8 +3570,7 @@ VLIB_CLI_COMMAND (ip6_nd_command, static) = clib_error_t * set_ip6_link_local_address (vlib_main_t * vm, - u32 sw_if_index, - ip6_address_t * address, u8 address_length) + u32 sw_if_index, ip6_address_t * address) { clib_error_t *error = 0; ip6_neighbor_main_t *nm = &ip6_neighbor_main; @@ -3615,22 +3599,18 @@ set_ip6_link_local_address (vlib_main_t * vm, /* delete the old one */ error = ip6_add_del_interface_address (vm, sw_if_index, &radv_info->link_local_address, - radv_info->link_local_prefix_len - /* address width */ , - 1 /* is_del */ ); + 128, 1 /* is_del */ ); if (!error) { /* add the new one */ error = ip6_add_del_interface_address (vm, sw_if_index, - address, address_length - /* address width */ , + address, 128, 0 /* is_del */ ); if (!error) { radv_info->link_local_address = *address; - radv_info->link_local_prefix_len = address_length; } } } @@ -3652,21 +3632,19 @@ set_ip6_link_local_address_cmd (vlib_main_t * vm, clib_error_t *error = 0; u32 sw_if_index; ip6_address_t ip6_addr; - u32 addr_len = 0; if (unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) { /* get the rest of the command */ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - if (unformat (input, "%U/%d", - unformat_ip6_address, &ip6_addr, &addr_len)) + if (unformat (input, "%U", unformat_ip6_address, &ip6_addr)) break; else return (unformat_parse_error (input)); } } - error = set_ip6_link_local_address (vm, sw_if_index, &ip6_addr, addr_len); + error = set_ip6_link_local_address (vm, sw_if_index, &ip6_addr); return error; } @@ -3678,13 +3656,13 @@ set_ip6_link_local_address_cmd (vlib_main_t * vm, * * @cliexpar * Example of how to assign an IPv6 Link-local address to an interface: - * @cliexcmd{set ip6 link-local address GigabitEthernet2/0/0 FE80::AB8/64} + * @cliexcmd{set ip6 link-local address GigabitEthernet2/0/0 FE80::AB8} ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (set_ip6_link_local_address_command, static) = { .path = "set ip6 link-local address", - .short_help = "set ip6 link-local address /", + .short_help = "set ip6 link-local address ", .function = set_ip6_link_local_address_cmd, }; /* *INDENT-ON* */ diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index cd9b7397..aafde464 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -1132,8 +1132,7 @@ static void error = set_ip6_link_local_address (vm, ntohl (mp->sw_if_index), - (ip6_address_t *) mp->address, - mp->address_length); + (ip6_address_t *) mp->address); if (error) { clib_error_report (error); diff --git a/src/vnet/rewrite.c b/src/vnet/rewrite.c index 53d548bc..8925ad61 100644 --- a/src/vnet/rewrite.c +++ b/src/vnet/rewrite.c @@ -79,8 +79,11 @@ format_vnet_rewrite (u8 * s, va_list * args) if (rw->sw_if_index != ~0) { vnet_sw_interface_t *si; - si = vnet_get_sw_interface (vnm, rw->sw_if_index); - s = format (s, "%U: ", format_vnet_sw_interface_name, vnm, si); + si = vnet_get_sw_interface_safe (vnm, rw->sw_if_index); + if (NULL != si) + s = format (s, "%U: ", format_vnet_sw_interface_name, vnm, si); + else + s = format (s, "DELETED"); } else s = format (s, "%v: ", next->name); diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index c2cd3d15..f14a031d 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -911,8 +911,7 @@ static void *vl_api_sw_interface_ip6_set_link_local_address_t_print s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); - s = format (s, "%U/%d ", format_ip6_address, mp->address, - mp->address_length); + s = format (s, "%U ", format_ip6_address, mp->address); FINISH; } diff --git a/src/vpp/api/test_client.c b/src/vpp/api/test_client.c index 5c568950..ceafc357 100644 --- a/src/vpp/api/test_client.c +++ b/src/vpp/api/test_client.c @@ -1196,8 +1196,6 @@ ip6_set_link_local_address (test_main_t * tm) clib_memcpy (mp->address, &tmp[0], 8); clib_memcpy (&mp->address[8], &tmp[1], 8); - mp->address_length = 64; - mp->_vl_msg_id = ntohs (VL_API_SW_INTERFACE_IP6_SET_LINK_LOCAL_ADDRESS); vl_msg_api_send_shmem (tm->vl_input_queue, (u8 *) & mp); diff --git a/test/test_ip6.py b/test/test_ip6.py index e8b12f68..cd9c4b95 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -8,8 +8,18 @@ from vpp_sub_interface import VppSubInterface, VppDot1QSubint from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q -from scapy.layers.inet6 import IPv6, UDP +from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, ICMPv6ND_RA, \ + ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation from util import ppp +from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \ + in6_mactoifaceid +from scapy.utils import inet_pton, inet_ntop + + +def mk_ll_addr(mac): + euid = in6_mactoifaceid(mac) + addr = "fe80::" + euid + return addr class TestIPv6(VppTestCase): @@ -76,6 +86,12 @@ class TestIPv6(VppTestCase): def tearDown(self): """Run standard test teardown and log ``show ip6 neighbors``.""" + for i in self.sub_interfaces: + i.unconfig_ip6() + i.ip6_disable() + i.admin_down() + i.remove_vpp_config() + super(TestIPv6, self).tearDown() if not self.vpp_dead: self.logger.info(self.vapi.cli("show ip6 neighbors")) @@ -206,6 +222,225 @@ class TestIPv6(VppTestCase): pkts = i.parent.get_capture() self.verify_capture(i, pkts) + def send_and_assert_no_replies(self, intf, pkts, remark): + intf.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + intf.assert_nothing_captured(remark=remark) + + def test_ns(self): + """ IPv6 Neighbour Soliciatation Exceptions + + Test sceanrio: + - Send an NS Sourced from an address not covered by the link sub-net + - Send an NS to an mcast address the router has not joined + - Send NS for a target address the router does not onn. + """ + + # + # An NS from a non link source address + # + nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(socket.AF_INET6, nsma) + + p = (Ether(dst=in6_getnsmac(nsma)) / + IPv6(dst=d, src="2002::2") / + ICMPv6ND_NS(tgt=self.pg0.local_ip6) / + ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + pkts = [p] + + self.send_and_assert_no_replies(self.pg0, pkts, + "No response to NS source by address not on sub-net") + + # + # An NS for sent to a solicited mcast group the router is not a member of + # FAILS + # + if 0: + nsma = in6_getnsma(inet_pton(socket.AF_INET6, "fd::ffff")) + d = inet_ntop(socket.AF_INET6, nsma) + + p = (Ether(dst=in6_getnsmac(nsma)) / + IPv6(dst=d, src=self.pg0.remote_ip6) / + ICMPv6ND_NS(tgt=self.pg0.local_ip6) / + ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + pkts = [p] + + self.send_and_assert_no_replies(self.pg0, pkts, + "No response to NS sent to unjoined mcast address") + + # + # An NS whose target address is one the router does not own + # + nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(socket.AF_INET6, nsma) + + p = (Ether(dst=in6_getnsmac(nsma)) / + IPv6(dst=d, src=self.pg0.remote_ip6) / + ICMPv6ND_NS(tgt="fd::ffff") / + ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + pkts = [p] + + self.send_and_assert_no_replies(self.pg0, pkts, + "No response to NS for unknown target") + + def send_and_expect_ra(self, intf, pkts, remark, src_ip=None): + if not src_ip: + src_ip = intf.remote_ip6 + intf.add_stream(pkts) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = intf.get_capture(1) + + self.assertEqual(len(rx), 1) + rx = rx[0] + + # the rx'd RA should be addressed to the sender's source + self.assertTrue(rx.haslayer(ICMPv6ND_RA)) + self.assertEqual(in6_ptop(rx[IPv6].dst), + in6_ptop(src_ip)) + + # and come from the router's link local + self.assertTrue(in6_islladdr(rx[IPv6].src)) + self.assertEqual(in6_ptop(rx[IPv6].src), + in6_ptop(mk_ll_addr(intf.local_mac))) + + def test_rs(self): + """ IPv6 Router Soliciatation Exceptions + + Test sceanrio: + """ + + # + # Before we begin change the IPv6 RA responses to use the unicast address + # that way we will not confuse them with the periodic Ras which go to the Mcast + # address + # + self.pg0.ip6_ra_config(send_unicast=1) + + # + # An RS from a link source address + # - expect an RA in return + # + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) / + ICMPv6ND_RS()) + pkts = [p] + self.send_and_expect_ra(self.pg0, pkts, "Genuine RS") + + # + # For the next RS sent the RA should be rate limited + # + self.send_and_assert_no_replies(self.pg0, pkts, "RA rate limited") + + # + # When we reconfiure the IPv6 RA config, we reset the RA rate limiting, + # so we need to do this before each test below so as not to drop packets for + # rate limiting reasons. Test this works here. + # + self.pg0.ip6_ra_config(send_unicast=1) + self.send_and_expect_ra(self.pg0, pkts, "Rate limit reset RS") + + # + # An RS sent from a non-link local source + # + self.pg0.ip6_ra_config(send_unicast=1) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(dst=self.pg0.local_ip6, src="2002::ffff") / + ICMPv6ND_RS()) + pkts = [p] + self.send_and_assert_no_replies(self.pg0, pkts, + "RS from non-link source") + + # + # Source an RS from a link local address + # + self.pg0.ip6_ra_config(send_unicast=1) + ll = mk_ll_addr(self.pg0.remote_mac) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(dst=self.pg0.local_ip6, src=ll) / + ICMPv6ND_RS()) + pkts = [p] + self.send_and_expect_ra( + self.pg0, pkts, "RS sourced from link-local", src_ip=ll) + + # + # Source from the unspecified address ::. This happens when the RS is sent before + # the host has a configured address/sub-net, i.e. auto-config. + # Since the sender has no IP address, the reply comes back mcast - so the + # capture needs to not filter this. + # If we happen to pick up the periodic RA at this point then so be it, it's not + # an error. + # + self.pg0.ip6_ra_config(send_unicast=1) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(dst=self.pg0.local_ip6, src="::") / + ICMPv6ND_RS()) + pkts = [p] + + self.pg0.add_stream(pkts) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1, filter_out_fn=None) + found = 0 + for rx in capture: + if (rx.haslayer(ICMPv6ND_RA)): + # and come from the router's link local + self.assertTrue(in6_islladdr(rx[IPv6].src)) + self.assertEqual(in6_ptop(rx[IPv6].src), + in6_ptop(mk_ll_addr(self.pg0.local_mac))) + # sent to the all hosts mcast + self.assertEqual(in6_ptop(rx[IPv6].dst), "ff02::1") + + found = 1 + self.assertTrue(found) + + @unittest.skip("Unsupported") + def test_mrs(self): + """ IPv6 Multicast Router Soliciatation Exceptions + + Test sceanrio: + """ + + # + # An RS from a link source address + # - expect an RA in return + # + nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(socket.AF_INET6, nsma) + + p = (Ether(dst=getmacbyip6("ff02::2")) / + IPv6(dst=d, src=self.pg0.remote_ip6) / + ICMPv6MRD_Solicitation()) + pkts = [p] + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.assert_nothing_captured( + remark="No response to NS source by address not on sub-net") + + # + # An RS from a non link source address + # + nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(socket.AF_INET6, nsma) + + p = (Ether(dst=getmacbyip6("ff02::2")) / + IPv6(dst=d, src="2002::2") / + ICMPv6MRD_Solicitation()) + pkts = [p] + + self.send_and_assert_no_replies(self.pg0, pkts, + "RA rate limited") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.assert_nothing_captured( + remark="No response to NS source by address not on sub-net") + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_interface.py b/test/vpp_interface.py index 6ccb92bb..856a5cc2 100644 --- a/test/vpp_interface.py +++ b/test/vpp_interface.py @@ -249,6 +249,12 @@ class VppInterface(object): """Configure IPv6 RA suppress on the VPP interface.""" self.test.vapi.sw_interface_ra_suppress(self.sw_if_index) + def ip6_ra_config(self, suppress=0, send_unicast=0): + """Configure IPv6 RA suppress on the VPP interface.""" + self.test.vapi.ip6_sw_interface_ra_config(self.sw_if_index, + suppress, + send_unicast) + def admin_up(self): """Put interface ADMIN-UP.""" self.test.vapi.sw_interface_set_flags(self.sw_if_index, admin_up_down=1) @@ -257,6 +263,14 @@ class VppInterface(object): """Put interface ADMIN-down.""" self.test.vapi.sw_interface_set_flags(self.sw_if_index, admin_up_down=0) + def ip6_enable(self): + """IPv6 Enable interface""" + self.test.vapi.ip6_sw_interface_enable_disable(self.sw_if_index, enable=1) + + def ip6_disable(self): + """Put interface ADMIN-DOWN.""" + self.test.vapi.ip6_sw_interface_enable_disable(self.sw_if_index, enable=0) + def add_sub_if(self, sub_if): """Register a sub-interface with this interface. diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index db530d98..0e4c0cdb 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -219,6 +219,22 @@ class VppPapiProvider(object): return self.api(self.papi.sw_interface_ip6nd_ra_config, {'sw_if_index': sw_if_index}) + def ip6_sw_interface_ra_config(self, sw_if_index, + suppress, + send_unicast,): + return self.api(self.papi.sw_interface_ip6nd_ra_config, + {'sw_if_index': sw_if_index, + 'suppress' : suppress, + 'send_unicast' : send_unicast}) + + def ip6_sw_interface_enable_disable(self, sw_if_index, enable): + """ + Enable/Disable An interface for IPv6 + """ + return self.api(self.papi.sw_interface_ip6_enable_disable, + {'sw_if_index': sw_if_index, + 'enable': enable}) + def vxlan_add_del_tunnel( self, src_addr, diff --git a/test/vpp_pg_interface.py b/test/vpp_pg_interface.py index aef0052e..b5929a4b 100644 --- a/test/vpp_pg_interface.py +++ b/test/vpp_pg_interface.py @@ -10,13 +10,14 @@ from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA,\ ICMPv6NDOptSrcLLAddr, ICMPv6NDOptDstLLAddr, ICMPv6ND_RA, RouterAlert, \ IPv6ExtHdrHopByHop from util import ppp, ppc -from scapy.utils6 import in6_getnsma, in6_getnsmac +from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ismaddr from scapy.utils import inet_pton, inet_ntop def is_ipv6_misc(p): """ Is packet one of uninteresting IPv6 broadcasts? """ if p.haslayer(ICMPv6ND_RA): - return True + if in6_ismaddr(p[IPv6].dst): + return True if p.haslayer(IPv6ExtHdrHopByHop): for o in p[IPv6ExtHdrHopByHop].options: if isinstance(o, RouterAlert): -- cgit 1.2.3-korg From 3212c57087069c5137abe4919ac1336e79fa93e9 Mon Sep 17 00:00:00 2001 From: Eyal Bari Date: Mon, 6 Mar 2017 11:47:50 +0200 Subject: CLI: hide deleted interfaces Added a new interface flag - HIDDEN Indicates that the interface does not appear in CLI/API. Added three new interface functions: vnet_sw_interface_is_api_visible - indicates if the sw_if_index should be displayed vnet_swif_is_api_visible - variant for sw_interface vnet_sw_interface_is_api_valid - tests if the given if_index exists and is visible for future use by api functions Changed the unformat function to only accept visible interfaces Changed vxlan to add the HIDDEN flag to deleted interfaces This is the first part in a series to hide deleted interfaces from the API Change-Id: Ib43cc5cf1c450856560faf4e84126eb3671038e2 Signed-off-by: Eyal Bari --- src/vnet/interface.h | 3 +++ src/vnet/interface_cli.c | 5 +++-- src/vnet/interface_format.c | 10 +++++++--- src/vnet/interface_funcs.h | 20 ++++++++++++++++++++ src/vnet/vxlan/vxlan.c | 7 +++++++ 5 files changed, 40 insertions(+), 5 deletions(-) (limited to 'src/vnet/interface_funcs.h') diff --git a/src/vnet/interface.h b/src/vnet/interface.h index 7b791751..ef8f9118 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -538,6 +538,9 @@ typedef struct #define VNET_SW_INTERFACE_FLAG_BOND_SLAVE (1 << 4) +/* Interface does not appear in CLI/API */ +#define VNET_SW_INTERFACE_FLAG_HIDDEN (1 << 5) + /* Index for this interface. */ u32 sw_if_index; diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c index 7dbee867..bd715e4e 100644 --- a/src/vnet/interface_cli.c +++ b/src/vnet/interface_cli.c @@ -285,8 +285,9 @@ show_sw_interfaces (vlib_main_t * vm, _vec_len (sorted_sis) = 0; pool_foreach (si, im->sw_interfaces, ( { - vec_add1 (sorted_sis, si[0]); - } + if (vnet_swif_is_api_visible + (si)) vec_add1 (sorted_sis, + si[0]);} )); /* Sort by name. */ diff --git a/src/vnet/interface_format.c b/src/vnet/interface_format.c index b3a30622..b961c778 100644 --- a/src/vnet/interface_format.c +++ b/src/vnet/interface_format.c @@ -321,6 +321,7 @@ unformat_vnet_sw_interface (unformat_input_t * input, va_list * args) u32 *result = va_arg (*args, u32 *); vnet_hw_interface_t *hi; u32 hw_if_index, id, id_specified; + u32 sw_if_index; u8 *if_name = 0; uword *p, error = 0; @@ -340,14 +341,17 @@ unformat_vnet_sw_interface (unformat_input_t * input, va_list * args) hi = vnet_get_hw_interface (vnm, hw_if_index); if (!id_specified) { - *result = hi->sw_if_index; + sw_if_index = hi->sw_if_index; } else { if (!(p = hash_get (hi->sub_interface_sw_if_index_by_id, id))) - return 0; - *result = p[0]; + goto done; + sw_if_index = p[0]; } + if (!vnet_sw_interface_is_api_visible (vnm, sw_if_index)) + goto done; + *result = sw_if_index; error = 1; done: vec_free (if_name); diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index ab808dfa..b3aca2fd 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -182,6 +182,26 @@ vnet_sw_interface_is_admin_up (vnet_main_t * vnm, u32 sw_if_index) VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0; } +always_inline uword +vnet_swif_is_api_visible (vnet_sw_interface_t * si) +{ + return !(si->flags & VNET_SW_INTERFACE_FLAG_HIDDEN); +} + +always_inline uword +vnet_sw_interface_is_api_visible (vnet_main_t * vnm, u32 sw_if_index) +{ + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); + return vnet_swif_is_api_visible (si); +} + +always_inline uword +vnet_sw_interface_is_api_valid (vnet_main_t * vnm, u32 sw_if_index) +{ + return !pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index) + && vnet_sw_interface_is_api_visible (vnm, sw_if_index); +} + always_inline uword vnet_hw_interface_get_flags (vnet_main_t * vnm, u32 hw_if_index) { diff --git a/src/vnet/vxlan/vxlan.c b/src/vnet/vxlan/vxlan.c index f373a283..58c7becb 100644 --- a/src/vnet/vxlan/vxlan.c +++ b/src/vnet/vxlan/vxlan.c @@ -18,6 +18,7 @@ #include #include #include +#include #include /** @@ -462,8 +463,11 @@ int vnet_vxlan_add_del_tunnel l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP; l2im->configs[sw_if_index].bd_index = 0; + vnet_sw_interface_t * si = vnet_get_sw_interface (vnm, sw_if_index); + si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN; vnet_sw_interface_set_flags (vnm, sw_if_index, VNET_SW_INTERFACE_FLAG_ADMIN_UP); + fib_node_init(&t->node, FIB_NODE_TYPE_VXLAN_TUNNEL); fib_prefix_t tun_dst_pfx; u32 encap_index = !is_ip6 ? @@ -573,6 +577,9 @@ int vnet_vxlan_add_del_tunnel t = pool_elt_at_index (vxm->tunnels, p[0]); vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */); + vnet_sw_interface_t * si = vnet_get_sw_interface (vnm, t->sw_if_index); + si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; + /* make sure tunnel is removed from l2 bd or xconnect */ set_int_l2_mode(vxm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, 0, 0, 0); vec_add1 (vxm->free_vxlan_tunnel_hw_if_indices, t->hw_if_index); -- cgit 1.2.3-korg From 4403690cda44134af3b9ea78d33a5cbf78a5acc9 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Fri, 28 Apr 2017 12:29:15 +0200 Subject: Add interface rx mode commands, unify rx mode and placement CLI Change-Id: Ib506c3e9d66170f29e3266ad6dc4d32b829befba Signed-off-by: Damjan Marion --- src/plugins/dpdk/device/init.c | 10 +- src/vnet/api_errno.h | 3 +- src/vnet/devices/af_packet/af_packet.c | 18 ++- src/vnet/devices/devices.c | 172 +++--------------------- src/vnet/devices/devices.h | 34 ++--- src/vnet/interface.h | 15 +++ src/vnet/interface_cli.c | 238 +++++++++++++++++++++++++++++++++ src/vnet/interface_format.c | 17 +++ src/vnet/interface_funcs.h | 1 + 9 files changed, 326 insertions(+), 182 deletions(-) (limited to 'src/vnet/interface_funcs.h') diff --git a/src/plugins/dpdk/device/init.c b/src/plugins/dpdk/device/init.c index 6f51ff64..2d21bfd9 100755 --- a/src/plugins/dpdk/device/init.c +++ b/src/plugins/dpdk/device/init.c @@ -704,8 +704,8 @@ dpdk_lib_init (dpdk_main_t * dm) sw = vnet_get_hw_sw_interface (dm->vnet_main, xd->hw_if_index); xd->vlib_sw_if_index = sw->sw_if_index; - vnet_set_device_input_node (dm->vnet_main, xd->hw_if_index, - dpdk_input_node.index); + vnet_hw_interface_set_input_node (dm->vnet_main, xd->hw_if_index, + dpdk_input_node.index); if (devconf->workers) { @@ -713,7 +713,7 @@ dpdk_lib_init (dpdk_main_t * dm) q = 0; /* *INDENT-OFF* */ clib_bitmap_foreach (i, devconf->workers, ({ - vnet_device_input_assign_thread (dm->vnet_main, xd->hw_if_index, q++, + vnet_hw_interface_assign_rx_thread (dm->vnet_main, xd->hw_if_index, q++, vdm->first_worker_thread_index + i); })); /* *INDENT-ON* */ @@ -721,8 +721,8 @@ dpdk_lib_init (dpdk_main_t * dm) else for (q = 0; q < xd->rx_q_used; q++) { - vnet_device_input_assign_thread (dm->vnet_main, xd->hw_if_index, q, /* any */ - ~1); + vnet_hw_interface_assign_rx_thread (dm->vnet_main, xd->hw_if_index, q, /* any */ + ~1); } hi = vnet_get_hw_interface (dm->vnet_main, xd->hw_if_index); diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index 0d5b2227..b87c197f 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -109,7 +109,8 @@ _(ENTRY_ALREADY_EXISTS, -116, "Entry already exists") \ _(SVM_SEGMENT_CREATE_FAIL, -117, "svm segment create fail") \ _(APPLICATION_NOT_ATTACHED, -118, "application not attached") \ _(BD_ALREADY_EXISTS, -119, "Bridge domain already exists") \ -_(BD_IN_USE, -120, "Bridge domain has member interfaces") +_(BD_IN_USE, -120, "Bridge domain has member interfaces") \ +_(UNSUPPORTED, -121, "Unsupported") typedef enum { diff --git a/src/vnet/devices/af_packet/af_packet.c b/src/vnet/devices/af_packet/af_packet.c index 92bd1092..cb52e6da 100644 --- a/src/vnet/devices/af_packet/af_packet.c +++ b/src/vnet/devices/af_packet/af_packet.c @@ -195,6 +195,7 @@ af_packet_create_if (vlib_main_t * vm, u8 * host_if_name, u8 * hw_addr_set, u8 hw_addr[6]; clib_error_t *error; vnet_sw_interface_t *sw; + vnet_hw_interface_t *hw; vlib_thread_main_t *tm = vlib_get_thread_main (); vnet_main_t *vnm = vnet_get_main (); uword *p; @@ -294,17 +295,21 @@ af_packet_create_if (vlib_main_t * vm, u8 * host_if_name, u8 * hw_addr_set, } sw = vnet_get_hw_sw_interface (vnm, apif->hw_if_index); + hw = vnet_get_hw_interface (vnm, apif->hw_if_index); apif->sw_if_index = sw->sw_if_index; - vnet_set_device_input_node (vnm, apif->hw_if_index, - af_packet_input_node.index); - vnet_device_input_assign_thread (vnm, apif->hw_if_index, 0, /* queue */ - ~0 /* any cpu */ ); - vnet_device_input_set_mode (vnm, apif->hw_if_index, 0, - VNET_DEVICE_INPUT_MODE_INTERRUPT); + vnet_hw_interface_set_input_node (vnm, apif->hw_if_index, + af_packet_input_node.index); + vnet_hw_interface_assign_rx_thread (vnm, apif->hw_if_index, 0, /* queue */ + ~0 /* any cpu */ ); + + hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE; vnet_hw_interface_set_flags (vnm, apif->hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP); + vnet_hw_interface_set_rx_mode (vnm, apif->hw_if_index, 0, + VNET_HW_INTERFACE_RX_MODE_INTERRUPT); + mhash_set_mem (&apm->if_index_by_host_if_name, host_if_name_dup, &if_index, 0); if (sw_if_index) @@ -340,6 +345,7 @@ af_packet_delete_if (vlib_main_t * vm, u8 * host_if_name) /* bring down the interface */ vnet_hw_interface_set_flags (vnm, apif->hw_if_index, 0); + vnet_hw_interface_unassign_rx_thread (vnm, apif->hw_if_index, 0); /* clean up */ if (apif->unix_file_index != ~0) diff --git a/src/vnet/devices/devices.c b/src/vnet/devices/devices.c index 2f55adcb..d75d905a 100644 --- a/src/vnet/devices/devices.c +++ b/src/vnet/devices/devices.c @@ -119,8 +119,8 @@ vnet_device_queue_update (vnet_main_t * vnm, vnet_device_input_runtime_t * rt) } void -vnet_device_input_assign_thread (vnet_main_t * vnm, u32 hw_if_index, - u16 queue_id, uword thread_index) +vnet_hw_interface_assign_rx_thread (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id, uword thread_index) { vnet_device_main_t *vdm = &vnet_device_main; vlib_main_t *vm; @@ -149,16 +149,19 @@ vnet_device_input_assign_thread (vnet_main_t * vnm, u32 hw_if_index, dq->hw_if_index = hw_if_index; dq->dev_instance = hw->dev_instance; dq->queue_id = queue_id; + dq->mode = VNET_HW_INTERFACE_RX_MODE_POLLING; vnet_device_queue_update (vnm, rt); vec_validate (hw->input_node_thread_index_by_queue, queue_id); + vec_validate (hw->rx_mode_by_queue, queue_id); hw->input_node_thread_index_by_queue[queue_id] = thread_index; + hw->rx_mode_by_queue[queue_id] = VNET_HW_INTERFACE_RX_MODE_POLLING; vlib_node_set_state (vm, hw->input_node_index, rt->enabled_node_state); } int -vnet_device_input_unassign_thread (vnet_main_t * vnm, u32 hw_if_index, - u16 queue_id, uword thread_index) +vnet_hw_interface_unassign_rx_thread (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id) { vlib_main_t *vm; vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); @@ -190,6 +193,7 @@ vnet_device_input_unassign_thread (vnet_main_t * vnm, u32 hw_if_index, deleted: vnet_device_queue_update (vnm, rt); + hw->rx_mode_by_queue[queue_id] = VNET_HW_INTERFACE_RX_MODE_UNKNOWN; if (vec_len (rt->devices_and_queues) == 0) vlib_node_set_state (vm, hw->input_node_index, VLIB_NODE_STATE_DISABLED); @@ -199,21 +203,28 @@ deleted: int -vnet_device_input_set_mode (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id, - vnet_device_input_mode_t mode) +vnet_hw_interface_set_rx_mode (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id, vnet_hw_interface_rx_mode mode) { vlib_main_t *vm; uword thread_index; vnet_device_and_queue_t *dq; vlib_node_state_t enabled_node_state; - ASSERT (mode < VNET_DEVICE_INPUT_N_MODES); + ASSERT (mode < VNET_HW_INTERFACE_NUM_RX_MODES); vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); vnet_device_input_runtime_t *rt; int is_polling = 0; - if (hw->input_node_thread_index_by_queue == 0) + if (hw->input_node_thread_index_by_queue == 0 || hw->rx_mode_by_queue == 0) return VNET_API_ERROR_INVALID_INTERFACE; + if (hw->rx_mode_by_queue[queue_id] == mode) + return 0; + + if (mode != VNET_HW_INTERFACE_RX_MODE_POLLING && + (hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE) == 0) + return VNET_API_ERROR_UNSUPPORTED; + thread_index = hw->input_node_thread_index_by_queue[queue_id]; vm = vlib_mains[thread_index]; @@ -223,7 +234,7 @@ vnet_device_input_set_mode (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id, { if (dq->hw_if_index == hw_if_index && dq->queue_id == queue_id) dq->mode = mode; - if (dq->mode == VNET_DEVICE_INPUT_MODE_POLLING) + if (dq->mode == VNET_HW_INTERFACE_RX_MODE_POLLING) is_polling = 1; } @@ -244,8 +255,8 @@ vnet_device_input_set_mode (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id, } int -vnet_device_input_get_mode (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id, - vnet_device_input_mode_t * mode) +vnet_hw_interface_get_rx_mode (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id, vnet_hw_interface_rx_mode * mode) { vlib_main_t *vm; uword thread_index; @@ -271,146 +282,7 @@ vnet_device_input_get_mode (vnet_main_t * vnm, u32 hw_if_index, u16 queue_id, return VNET_API_ERROR_INVALID_INTERFACE; } -static clib_error_t * -show_device_placement_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - u8 *s = 0; - vnet_main_t *vnm = vnet_get_main (); - vnet_device_input_runtime_t *rt; - vnet_device_and_queue_t *dq; - vlib_node_t *pn = vlib_get_node_by_name (vm, (u8 *) "device-input"); - uword si; - int index = 0; - - /* *INDENT-OFF* */ - foreach_vlib_main (({ - clib_bitmap_foreach (si, pn->sibling_bitmap, - ({ - rt = vlib_node_get_runtime_data (this_vlib_main, si); - - if (vec_len (rt->devices_and_queues)) - s = format (s, " node %U:\n", format_vlib_node_name, vm, si); - - vec_foreach (dq, rt->devices_and_queues) - { - s = format (s, " %U queue %u (%s)\n", - format_vnet_sw_if_index_name, vnm, dq->hw_if_index, - dq->queue_id, - dq->mode == VNET_DEVICE_INPUT_MODE_POLLING ? - "polling" : "interrupt"); - } - })); - if (vec_len (s) > 0) - { - vlib_cli_output(vm, "Thread %u (%v):\n%v", index, - vlib_worker_threads[index].name, s); - vec_reset_length (s); - } - index++; - })); - /* *INDENT-ON* */ - - vec_free (s); - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (memif_delete_command, static) = { - .path = "show interface placement", - .short_help = "show interface placement", - .function = show_device_placement_fn, -}; -/* *INDENT-ON* */ - -static clib_error_t * -set_device_placement (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - clib_error_t *error = 0; - unformat_input_t _line_input, *line_input = &_line_input; - vnet_main_t *vnm = vnet_get_main (); - vnet_device_main_t *vdm = &vnet_device_main; - vnet_device_input_mode_t mode; - u32 hw_if_index = (u32) ~ 0; - u32 queue_id = (u32) 0; - u32 thread_index = (u32) ~ 0; - int rv; - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat - (line_input, "%U", unformat_vnet_hw_interface, vnm, &hw_if_index)) - ; - else if (unformat (line_input, "queue %d", &queue_id)) - ; - else if (unformat (line_input, "main", &thread_index)) - thread_index = 0; - else if (unformat (line_input, "worker %d", &thread_index)) - thread_index += vdm->first_worker_thread_index; - else - { - error = clib_error_return (0, "parse error: '%U'", - format_unformat_error, line_input); - unformat_free (line_input); - return error; - } - } - - unformat_free (line_input); - - if (hw_if_index == (u32) ~ 0) - return clib_error_return (0, "please specify valid interface name"); - - if (thread_index > vdm->last_worker_thread_index) - return clib_error_return (0, - "please specify valid worker thread or main"); - - rv = vnet_device_input_get_mode (vnm, hw_if_index, queue_id, &mode); - - if (rv) - return clib_error_return (0, "not found"); - - rv = vnet_device_input_unassign_thread (vnm, hw_if_index, queue_id, - thread_index); - if (rv) - return clib_error_return (0, "not found"); - - vnet_device_input_assign_thread (vnm, hw_if_index, queue_id, thread_index); - vnet_device_input_set_mode (vnm, hw_if_index, queue_id, mode); - - return 0; -} - -/*? - * This command is used to assign a given interface, and optionally a - * given queue, to a different thread. If the 'queue' is not provided, - * it defaults to 0. - * - * @cliexpar - * Example of how to display the interface placement: - * @cliexstart{show interface placement} - * Thread 1 (vpp_wk_0): - * GigabitEthernet0/8/0 queue 0 - * GigabitEthernet0/9/0 queue 0 - * Thread 2 (vpp_wk_1): - * GigabitEthernet0/8/0 queue 1 - * GigabitEthernet0/9/0 queue 1 - * @cliexend - * Example of how to assign a interface and queue to a thread: - * @cliexcmd{set interface placement GigabitEthernet0/8/0 queue 1 thread 1} -?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (cmd_set_dpdk_if_placement,static) = { - .path = "set interface placement", - .short_help = "set interface placement [queue ] [thread | main]", - .function = set_device_placement, -}; -/* *INDENT-ON* */ static clib_error_t * vnet_device_init (vlib_main_t * vm) diff --git a/src/vnet/devices/devices.h b/src/vnet/devices/devices.h index baf03b7c..f1f7e778 100644 --- a/src/vnet/devices/devices.h +++ b/src/vnet/devices/devices.h @@ -55,19 +55,12 @@ typedef struct uword next_worker_thread_index; } vnet_device_main_t; -typedef enum -{ - VNET_DEVICE_INPUT_MODE_POLLING = 0, - VNET_DEVICE_INPUT_MODE_INTERRUPT, - VNET_DEVICE_INPUT_N_MODES, -} vnet_device_input_mode_t; - typedef struct { u32 hw_if_index; u32 dev_instance; u16 queue_id; - vnet_device_input_mode_t mode; + vnet_hw_interface_rx_mode mode; uword interrupt_pending; } vnet_device_and_queue_t; @@ -82,22 +75,23 @@ extern vlib_node_registration_t device_input_node; extern const u32 device_input_next_node_advance[]; static inline void -vnet_set_device_input_node (vnet_main_t * vnm, u32 hw_if_index, - u32 node_index) +vnet_hw_interface_set_input_node (vnet_main_t * vnm, u32 hw_if_index, + u32 node_index) { vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); hw->input_node_index = node_index; } -void vnet_device_input_assign_thread (vnet_main_t * vnm, u32 hw_if_index, - u16 queue_id, uword thread_index); -int vnet_device_input_unassign_thread (vnet_main_t * vnm, u32 hw_if_index, - u16 queue_id, uword thread_index); -int vnet_device_input_set_mode (vnet_main_t * vnm, u32 hw_if_index, - u16 queue_id, vnet_device_input_mode_t mode); -int vnet_device_input_get_mode (vnet_main_t * vnm, u32 hw_if_index, - u16 queue_id, - vnet_device_input_mode_t * mode); +void vnet_hw_interface_assign_rx_thread (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id, uword thread_index); +int vnet_hw_interface_unassign_rx_thread (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id); +int vnet_hw_interface_set_rx_mode (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id, + vnet_hw_interface_rx_mode mode); +int vnet_hw_interface_get_rx_mode (vnet_main_t * vnm, u32 hw_if_index, + u16 queue_id, + vnet_hw_interface_rx_mode * mode); static inline u64 vnet_get_aggregate_rx_packets (void) @@ -161,7 +155,7 @@ vnet_device_input_set_interrupt_pending (vnet_main_t * vnm, u32 hw_if_index, #define foreach_device_and_queue(var,vec) \ for (var = (vec); var < vec_end (vec); var++) \ if (clib_smp_swap (&((var)->interrupt_pending), 0) || \ - var->mode == VNET_DEVICE_INPUT_MODE_POLLING) + var->mode == VNET_HW_INTERFACE_RX_MODE_POLLING) #endif /* included_vnet_vnet_device_h */ diff --git a/src/vnet/interface.h b/src/vnet/interface.h index 9c223040..2344348b 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -405,6 +405,9 @@ typedef struct vnet_hw_interface_t #define VNET_HW_INTERFACE_FLAG_L2OUTPUT_SHIFT 9 #define VNET_HW_INTERFACE_FLAG_L2OUTPUT_MAPPED (1 << 9) + /* rx mode flags */ +#define VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE (1 << 10) + /* Hardware address as vector. Zero (e.g. zero-length vector) if no address for this class (e.g. PPP). */ u8 *hw_address; @@ -470,6 +473,9 @@ typedef struct vnet_hw_interface_t /* input node cpu index by queue */ u32 *input_node_thread_index_by_queue; + /* vnet_hw_interface_rx_mode by queue */ + u8 *rx_mode_by_queue; + /* device input device_and_queue runtime index */ uword *dq_runtime_index_by_queue; @@ -486,6 +492,15 @@ typedef enum VNET_SW_INTERFACE_TYPE_SUB, } vnet_sw_interface_type_t; +typedef enum +{ + VNET_HW_INTERFACE_RX_MODE_UNKNOWN, + VNET_HW_INTERFACE_RX_MODE_POLLING, + VNET_HW_INTERFACE_RX_MODE_INTERRUPT, + VNET_HW_INTERFACE_RX_MODE_ADAPTIVE, + VNET_HW_INTERFACE_NUM_RX_MODES, +} vnet_hw_interface_rx_mode; + typedef struct { /* diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c index 94eb7ea9..bfce03e1 100644 --- a/src/vnet/interface_cli.c +++ b/src/vnet/interface_cli.c @@ -1175,7 +1175,245 @@ VLIB_CLI_COMMAND (clear_tag_command, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +set_interface_rx_mode (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + vnet_hw_interface_t *hw; + u32 hw_if_index = (u32) ~ 0; + u32 queue_id = (u32) ~ 0; + vnet_hw_interface_rx_mode mode = VNET_HW_INTERFACE_RX_MODE_UNKNOWN; + int i, rv = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat + (line_input, "%U", unformat_vnet_hw_interface, vnm, &hw_if_index)) + ; + else if (unformat (line_input, "queue %d", &queue_id)) + ; + else if (unformat (line_input, "polling")) + mode = VNET_HW_INTERFACE_RX_MODE_POLLING; + else if (unformat (line_input, "interrupt")) + mode = VNET_HW_INTERFACE_RX_MODE_INTERRUPT; + else if (unformat (line_input, "adaptive")) + mode = VNET_HW_INTERFACE_RX_MODE_ADAPTIVE; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + unformat_free (line_input); + return error; + } + } + + unformat_free (line_input); + + if (hw_if_index == (u32) ~ 0) + return clib_error_return (0, "please specify valid interface name"); + if (mode == VNET_HW_INTERFACE_RX_MODE_UNKNOWN) + return clib_error_return (0, "please specify valid rx-mode"); + + hw = vnet_get_hw_interface (vnm, hw_if_index); + + if (queue_id == ~0) + for (i = 0; i < vec_len (hw->dq_runtime_index_by_queue); i++) + { + rv = vnet_hw_interface_set_rx_mode (vnm, hw_if_index, i, mode); + if (rv) + goto error; + } + else + rv = vnet_hw_interface_set_rx_mode (vnm, hw_if_index, queue_id, mode); + + if (rv) + goto error; + + return 0; + +error: + if (rv == VNET_API_ERROR_UNSUPPORTED) + return clib_error_return (0, "unsupported"); + + if (rv == VNET_API_ERROR_INVALID_INTERFACE) + return clib_error_return (0, "invalid interfaace"); + + return clib_error_return (0, "unknown error"); +} + +/*? + * This command is used to assign a given interface, and optionally a + * given queue, to a different thread. If the 'queue' is not provided, + * it defaults to 0. + * + * @cliexpar + * Example of how to display the interface placement: + * @cliexstart{show interface rx-placement} + * Thread 1 (vpp_wk_0): + * GigabitEthernet0/8/0 queue 0 + * GigabitEthernet0/9/0 queue 0 + * Thread 2 (vpp_wk_1): + * GigabitEthernet0/8/0 queue 1 + * GigabitEthernet0/9/0 queue 1 + * @cliexend + * Example of how to assign a interface and queue to a thread: + * @cliexcmd{set interface placement GigabitEthernet0/8/0 queue 1 thread 1} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cmd_set_if_rx_mode,static) = { + .path = "set interface rx-mode", + .short_help = "set interface rx-mode [queue ] [polling | interrupt | adaptive]", + .function = set_interface_rx_mode, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_interface_rx_placement_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u8 *s = 0; + vnet_main_t *vnm = vnet_get_main (); + vnet_device_input_runtime_t *rt; + vnet_device_and_queue_t *dq; + vlib_node_t *pn = vlib_get_node_by_name (vm, (u8 *) "device-input"); + uword si; + int index = 0; + + /* *INDENT-OFF* */ + foreach_vlib_main (({ + clib_bitmap_foreach (si, pn->sibling_bitmap, + ({ + rt = vlib_node_get_runtime_data (this_vlib_main, si); + + if (vec_len (rt->devices_and_queues)) + s = format (s, " node %U:\n", format_vlib_node_name, vm, si); + + vec_foreach (dq, rt->devices_and_queues) + { + s = format (s, " %U queue %u (%U)\n", + format_vnet_sw_if_index_name, vnm, dq->hw_if_index, + dq->queue_id, + format_vnet_hw_interface_rx_mode, dq->mode); + } + })); + if (vec_len (s) > 0) + { + vlib_cli_output(vm, "Thread %u (%v):\n%v", index, + vlib_worker_threads[index].name, s); + vec_reset_length (s); + } + index++; + })); + /* *INDENT-ON* */ + + vec_free (s); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_interface_rx_placement, static) = { + .path = "show interface rx-placement", + .short_help = "show interface rx-placement", + .function = show_interface_rx_placement_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +set_interface_rx_placement (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + vnet_device_main_t *vdm = &vnet_device_main; + vnet_hw_interface_rx_mode mode; + u32 hw_if_index = (u32) ~ 0; + u32 queue_id = (u32) 0; + u32 thread_index = (u32) ~ 0; + int rv; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat + (line_input, "%U", unformat_vnet_hw_interface, vnm, &hw_if_index)) + ; + else if (unformat (line_input, "queue %d", &queue_id)) + ; + else if (unformat (line_input, "main", &thread_index)) + thread_index = 0; + else if (unformat (line_input, "worker %d", &thread_index)) + thread_index += vdm->first_worker_thread_index; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + unformat_free (line_input); + return error; + } + } + + unformat_free (line_input); + + if (hw_if_index == (u32) ~ 0) + return clib_error_return (0, "please specify valid interface name"); + + if (thread_index > vdm->last_worker_thread_index) + return clib_error_return (0, + "please specify valid worker thread or main"); + + rv = vnet_hw_interface_get_rx_mode (vnm, hw_if_index, queue_id, &mode); + + if (rv) + return clib_error_return (0, "not found"); + + rv = vnet_hw_interface_unassign_rx_thread (vnm, hw_if_index, queue_id); + + if (rv) + return clib_error_return (0, "not found"); + + vnet_hw_interface_assign_rx_thread (vnm, hw_if_index, queue_id, + thread_index); + vnet_hw_interface_set_rx_mode (vnm, hw_if_index, queue_id, mode); + + return 0; +} + +/*? + * This command is used to assign a given interface, and optionally a + * given queue, to a different thread. If the 'queue' is not provided, + * it defaults to 0. + * + * @cliexpar + * Example of how to display the interface placement: + * @cliexstart{show interface placement} + * Thread 1 (vpp_wk_0): + * GigabitEthernet0/8/0 queue 0 + * GigabitEthernet0/9/0 queue 0 + * Thread 2 (vpp_wk_1): + * GigabitEthernet0/8/0 queue 1 + * GigabitEthernet0/9/0 queue 1 + * @cliexend + * Example of how to assign a interface and queue to a thread: + * @cliexcmd{set interface placement GigabitEthernet0/8/0 queue 1 thread 1} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cmd_set_if_rx_placement,static) = { + .path = "set interface rx-placement", + .short_help = "set interface rx-placement [queue ] [thread | main]", + .function = set_interface_rx_placement, +}; + +/* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/interface_format.c b/src/vnet/interface_format.c index b961c778..03caf5c6 100644 --- a/src/vnet/interface_format.c +++ b/src/vnet/interface_format.c @@ -58,6 +58,23 @@ format_vnet_sw_interface_flags (u8 * s, va_list * args) return s; } +u8 * +format_vnet_hw_interface_rx_mode (u8 * s, va_list * args) +{ + vnet_hw_interface_rx_mode mode = va_arg (*args, vnet_hw_interface_rx_mode); + + if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) + return format (s, "polling"); + + if (mode == VNET_HW_INTERFACE_RX_MODE_INTERRUPT) + return format (s, "interrupt"); + + if (mode == VNET_HW_INTERFACE_RX_MODE_ADAPTIVE) + return format (s, "adaptive"); + + return format (s, "unknown"); +} + u8 * format_vnet_hw_interface (u8 * s, va_list * args) { diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index b3aca2fd..999b72e5 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -277,6 +277,7 @@ clib_error_t *vnet_hw_interface_change_mac_address (vnet_main_t * vnm, /* Formats sw/hw interface. */ format_function_t format_vnet_hw_interface; +format_function_t format_vnet_hw_interface_rx_mode; format_function_t format_vnet_sw_interface; format_function_t format_vnet_sw_interface_name; format_function_t format_vnet_sw_interface_name_override; -- cgit 1.2.3-korg From 10ae76627b19f26d857bdeffae9e2c2fac6e38a8 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Fri, 30 Jun 2017 19:53:03 +0200 Subject: Deprecate support for flattened output nodes Change-Id: Id117e219146d9994340fb38c00233ea67db8929b Signed-off-by: Damjan Marion --- src/plugins/ixge/ixge.c | 1 - src/vnet/interface.c | 8 +- src/vnet/interface.h | 3 - src/vnet/interface_funcs.h | 3 +- src/vnet/interface_output.c | 265 -------------------------------------------- 5 files changed, 3 insertions(+), 277 deletions(-) (limited to 'src/vnet/interface_funcs.h') diff --git a/src/plugins/ixge/ixge.c b/src/plugins/ixge/ixge.c index 0d287250..628d6d71 100644 --- a/src/plugins/ixge/ixge.c +++ b/src/plugins/ixge/ixge.c @@ -2454,7 +2454,6 @@ VNET_DEVICE_CLASS (ixge_device_class) = { .clear_counters = ixge_clear_hw_interface_counters, .admin_up_down_function = ixge_interface_admin_up_down, .rx_redirect_to_node = ixge_set_interface_next_node, - .flatten_output_chains = 1, }; /* *INDENT-ON* */ diff --git a/src/vnet/interface.c b/src/vnet/interface.c index 1370d048..7c0272b4 100644 --- a/src/vnet/interface.c +++ b/src/vnet/interface.c @@ -783,9 +783,7 @@ vnet_register_interface (vnet_main_t * vnm, /* The new class may differ from the old one. * Functions have to be updated. */ node = vlib_get_node (vm, hw->output_node_index); - node->function = dev_class->flatten_output_chains ? - vnet_interface_output_node_flatten_multiarch_select () : - vnet_interface_output_node_multiarch_select (); + node->function = vnet_interface_output_node_multiarch_select (); node->format_trace = format_vnet_interface_output_trace; nrt = vlib_node_get_runtime (vm, hw->output_node_index); nrt->function = node->function; @@ -827,9 +825,7 @@ vnet_register_interface (vnet_main_t * vnm, r.flags = 0; r.name = output_node_name; - r.function = dev_class->flatten_output_chains ? - vnet_interface_output_node_flatten_multiarch_select () : - vnet_interface_output_node_multiarch_select (); + r.function = vnet_interface_output_node_multiarch_select (); r.format_trace = format_vnet_interface_output_trace; { diff --git a/src/vnet/interface.h b/src/vnet/interface.h index d684e356..ce7700e4 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -198,9 +198,6 @@ typedef struct _vnet_device_class /* Link-list of all device classes set up by constructors created below */ struct _vnet_device_class *next_class_registration; - /* Splice vnet_interface_output_node into TX path */ - u8 flatten_output_chains; - /* Function to set mac address. */ vnet_interface_set_mac_address_function_t *mac_addr_change_function; } vnet_device_class_t; diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index 999b72e5..26eef9b9 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -301,9 +301,8 @@ typedef struct u32 is_deleted; } vnet_interface_output_runtime_t; -/* Interface output functions. */ +/* Interface output function. */ void *vnet_interface_output_node_multiarch_select (void); -void *vnet_interface_output_node_flatten_multiarch_select (void); word vnet_sw_interface_compare (vnet_main_t * vnm, uword sw_if_index0, uword sw_if_index1); diff --git a/src/vnet/interface_output.c b/src/vnet/interface_output.c index 846eb57b..cdf18738 100644 --- a/src/vnet/interface_output.c +++ b/src/vnet/interface_output.c @@ -153,271 +153,6 @@ vnet_interface_output_trace (vlib_main_t * vm, } } -static never_inline u32 -slow_path (vlib_main_t * vm, - u32 bi, - vlib_buffer_t * b, - u32 n_left_to_tx, u32 * to_tx, u32 * n_slow_bytes_result) -{ - /* We've already enqueued a single buffer. */ - u32 n_buffers = 0; - u32 n_slow_bytes = 0; - - while (n_left_to_tx > 0) - { - to_tx[0] = bi; - to_tx += 1; - n_left_to_tx -= 1; - n_buffers += 1; - n_slow_bytes += vlib_buffer_length_in_chain (vm, b); - - /* Be grumpy about zero length buffers for benefit of - driver tx function. */ - ASSERT (b->current_length > 0); - - if (!(b->flags & VLIB_BUFFER_NEXT_PRESENT)) - break; - - bi = b->next_buffer; - b = vlib_get_buffer (vm, bi); - } - - /* Ran out of space in next frame trying to enqueue buffers? */ - if (b->flags & VLIB_BUFFER_NEXT_PRESENT) - return 0; - - *n_slow_bytes_result = n_slow_bytes; - return n_buffers; -} - -/* - * Increment TX stats. Roll up consecutive increments to the same sw_if_index - * into one increment. - */ -static_always_inline void -incr_output_stats (vnet_main_t * vnm, - u32 thread_index, - u32 length, - u32 sw_if_index, - u32 * last_sw_if_index, u32 * n_packets, u32 * n_bytes) -{ - vnet_interface_main_t *im; - - if (PREDICT_TRUE (sw_if_index == *last_sw_if_index)) - { - *n_packets += 1; - *n_bytes += length; - } - else - { - if (PREDICT_TRUE (*last_sw_if_index != ~0)) - { - im = &vnm->interface_main; - - vlib_increment_combined_counter (im->combined_sw_if_counters - + VNET_INTERFACE_COUNTER_TX, - thread_index, - *last_sw_if_index, - *n_packets, *n_bytes); - } - *last_sw_if_index = sw_if_index; - *n_packets = 1; - *n_bytes = length; - } -} - - -/* Interface output functions. */ -uword -vnet_interface_output_node_flatten (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - vnet_main_t *vnm = vnet_get_main (); - vnet_interface_output_runtime_t *rt = (void *) node->runtime_data; - vnet_sw_interface_t *si; - vnet_hw_interface_t *hi; - u32 n_left_to_tx, *from, *from_end, *to_tx; - u32 n_bytes, n_buffers, n_packets; - u32 last_sw_if_index; - u32 thread_index = vm->thread_index; - - n_buffers = frame->n_vectors; - - if (node->flags & VLIB_NODE_FLAG_TRACE) - vnet_interface_output_trace (vm, node, frame, n_buffers); - - from = vlib_frame_args (frame); - - if (rt->is_deleted) - return vlib_error_drop_buffers (vm, node, from, - /* buffer stride */ 1, - n_buffers, - VNET_INTERFACE_OUTPUT_NEXT_DROP, - node->node_index, - VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED); - - si = vnet_get_sw_interface (vnm, rt->sw_if_index); - hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index); - if (!(si->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP | - VNET_SW_INTERFACE_FLAG_BOND_SLAVE)) || - !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)) - { - vlib_simple_counter_main_t *cm; - - cm = vec_elt_at_index (vnm->interface_main.sw_if_counters, - VNET_INTERFACE_COUNTER_TX_ERROR); - vlib_increment_simple_counter (cm, thread_index, - rt->sw_if_index, n_buffers); - return vlib_error_drop_buffers (vm, node, from, - /* buffer stride */ 1, - n_buffers, - VNET_INTERFACE_OUTPUT_NEXT_DROP, - node->node_index, - VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN); - } - - from_end = from + n_buffers; - - /* Total byte count of all buffers. */ - n_bytes = 0; - n_packets = 0; - last_sw_if_index = ~0; - - while (from < from_end) - { - /* Get new next frame since previous incomplete frame may have less - than VNET_FRAME_SIZE vectors in it. */ - vlib_get_new_next_frame (vm, node, VNET_INTERFACE_OUTPUT_NEXT_TX, - to_tx, n_left_to_tx); - - while (from + 4 <= from_end && n_left_to_tx >= 2) - { - u32 bi0, bi1; - vlib_buffer_t *b0, *b1; - - /* Prefetch next iteration. */ - vlib_prefetch_buffer_with_index (vm, from[2], LOAD); - vlib_prefetch_buffer_with_index (vm, from[3], LOAD); - - bi0 = from[0]; - bi1 = from[1]; - to_tx[0] = bi0; - to_tx[1] = bi1; - from += 2; - to_tx += 2; - n_left_to_tx -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - /* Be grumpy about zero length buffers for benefit of - driver tx function. */ - ASSERT (b0->current_length > 0); - ASSERT (b1->current_length > 0); - - if (PREDICT_FALSE - ((b0->flags | b1->flags) & VLIB_BUFFER_NEXT_PRESENT)) - { - u32 n_buffers, n_slow_bytes, i; - - /* Undo. */ - from -= 2; - to_tx -= 2; - n_left_to_tx += 2; - - /* Do slow path two times. */ - for (i = 0; i < 2; i++) - { - u32 bi = i ? bi1 : bi0; - vlib_buffer_t *b = i ? b1 : b0; - - n_buffers = slow_path (vm, bi, b, - n_left_to_tx, to_tx, &n_slow_bytes); - - /* Not enough room for single packet? */ - if (n_buffers == 0) - goto put; - - from += 1; - to_tx += n_buffers; - n_left_to_tx -= n_buffers; - incr_output_stats (vnm, thread_index, n_slow_bytes, - vnet_buffer (b)->sw_if_index[VLIB_TX], - &last_sw_if_index, &n_packets, &n_bytes); - } - } - else - { - incr_output_stats (vnm, thread_index, - vlib_buffer_length_in_chain (vm, b0), - vnet_buffer (b0)->sw_if_index[VLIB_TX], - &last_sw_if_index, &n_packets, &n_bytes); - incr_output_stats (vnm, thread_index, - vlib_buffer_length_in_chain (vm, b0), - vnet_buffer (b1)->sw_if_index[VLIB_TX], - &last_sw_if_index, &n_packets, &n_bytes); - } - } - - while (from + 1 <= from_end && n_left_to_tx >= 1) - { - u32 bi0; - vlib_buffer_t *b0; - - bi0 = from[0]; - to_tx[0] = bi0; - from += 1; - to_tx += 1; - n_left_to_tx -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - /* Be grumpy about zero length buffers for benefit of - driver tx function. */ - ASSERT (b0->current_length > 0); - - if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_NEXT_PRESENT)) - { - u32 n_buffers, n_slow_bytes; - - /* Undo. */ - from -= 1; - to_tx -= 1; - n_left_to_tx += 1; - - n_buffers = slow_path (vm, bi0, b0, - n_left_to_tx, to_tx, &n_slow_bytes); - - /* Not enough room for single packet? */ - if (n_buffers == 0) - goto put; - - from += 1; - to_tx += n_buffers; - n_left_to_tx -= n_buffers; - } - incr_output_stats (vnm, thread_index, - vlib_buffer_length_in_chain (vm, b0), - vnet_buffer (b0)->sw_if_index[VLIB_TX], - &last_sw_if_index, &n_packets, &n_bytes); - } - - put: - vlib_put_next_frame (vm, node, VNET_INTERFACE_OUTPUT_NEXT_TX, - n_left_to_tx); - } - - /* Final update of interface stats. */ - incr_output_stats (vnm, thread_index, 0, ~0, /* ~0 will flush stats */ - &last_sw_if_index, &n_packets, &n_bytes); - - return n_buffers; -} - -VLIB_NODE_FUNCTION_MULTIARCH_CLONE (vnet_interface_output_node_flatten); -CLIB_MULTIARCH_SELECT_FN (vnet_interface_output_node_flatten); - uword vnet_interface_output_node (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -- cgit 1.2.3-korg From 15ac81c16fba83033090299413a3a2dbb848a0f9 Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Tue, 20 Jun 2017 14:00:26 +0200 Subject: P2P Ethernet Change-Id: Idb97e573961b3bc2acdeef77582314590795f8c3 Signed-off-by: Pavel Kotucek --- src/vat/api_format.c | 17 +- src/vnet.am | 1 + src/vnet/api_errno.h | 3 +- src/vnet/devices/devices.c | 6 + src/vnet/ethernet/ethernet.h | 1 + src/vnet/ethernet/interface.c | 54 +++- src/vnet/ethernet/node.c | 17 +- src/vnet/ethernet/p2p_ethernet.api | 1 + src/vnet/ethernet/p2p_ethernet.c | 165 +++++++++- src/vnet/ethernet/p2p_ethernet.h | 42 ++- src/vnet/ethernet/p2p_ethernet_api.c | 17 +- src/vnet/ethernet/p2p_ethernet_input.c | 247 +++++++++++++++ src/vnet/interface.c | 4 + src/vnet/interface.h | 15 + src/vnet/interface_funcs.h | 3 +- test/test_p2p_ethernet.py | 538 +++++++++++++++++++++++++++++++++ test/vpp_papi_provider.py | 25 ++ test/vpp_sub_interface.py | 23 ++ 18 files changed, 1145 insertions(+), 34 deletions(-) create mode 100644 src/vnet/ethernet/p2p_ethernet_input.c create mode 100644 test/test_p2p_ethernet.py (limited to 'src/vnet/interface_funcs.h') 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, " | sw_if_index mtu ") \ _(ip_neighbor_dump, "[ip6] | sw_if_index ") \ _(sw_interface_get_table, " | sw_if_index [ipv6]") \ -_(p2p_ethernet_add, " | sw_if_index remote_mac ") \ +_(p2p_ethernet_add, " | sw_if_index remote_mac sub_id ") \ _(p2p_ethernet_del, " | sw_if_index remote_mac ") \ -_(lldp_config, "system-name tx-hold tx-interval ") \ -_(sw_interface_set_lldp, \ - " | sw_if_index [port-desc ] [disable]") +_(lldp_config, "system-name tx-hold tx-interval ") \ +_(sw_interface_set_lldp, " | sw_if_index [port-desc ] [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 ...") \ _(set, "usage: set ") \ _(script, "usage: script ") \ _(unset, "usage: unset ") - #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 #include #include +#include #include #include @@ -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 #include #include +#include + +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 [del]",}; -/* *INDENT-ON* */ +.path = "p2p_ethernet ",.function = vnet_p2p_ethernet_add_del,.short_help = + "p2p_ethernet [sub-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 #include -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 +#include +#include + +#include + +#include +#include + +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 -- cgit 1.2.3-korg