/* * 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.c: VNET interfaces/sub-interfaces * * 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. */ #include #include #include #include #include #define VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE (1 << 0) #define VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE (1 << 1) static clib_error_t *vnet_hw_interface_set_flags_helper (vnet_main_t * vnm, u32 hw_if_index, u32 flags, u32 helper_flags); static clib_error_t *vnet_sw_interface_set_flags_helper (vnet_main_t * vnm, u32 sw_if_index, u32 flags, u32 helper_flags); static clib_error_t *vnet_hw_interface_set_class_helper (vnet_main_t * vnm, u32 hw_if_index, u32 hw_class_index, u32 redistribute); typedef struct { /* Either sw or hw interface index. */ u32 sw_hw_if_index; /* Flags. */ u32 flags; } vnet_sw_hw_interface_state_t; static void serialize_vec_vnet_sw_hw_interface_state (serialize_main_t * m, va_list * va) { vnet_sw_hw_interface_state_t *s = va_arg (*va, vnet_sw_hw_interface_state_t *); u32 n = va_arg (*va, u32); u32 i; for (i = 0; i < n; i++) { serialize_integer (m, s[i].sw_hw_if_index, sizeof (s[i].sw_hw_if_index)); serialize_integer (m, s[i].flags, sizeof (s[i].flags)); } } static void unserialize_vec_vnet_sw_hw_interface_state (serialize_main_t * m, va_list * va) { vnet_sw_hw_interface_state_t *s = va_arg (*va, vnet_sw_hw_interface_state_t *); u32 n = va_arg (*va, u32); u32 i; for (i = 0; i < n; i++) { unserialize_integer (m, &s[i].sw_hw_if_index, sizeof (s[i].sw_hw_if_index)); unserialize_integer (m, &s[i].flags, sizeof (s[i].flags)); } } static void serialize_vnet_sw_hw_interface_set_flags (serialize_main_t * m, va_list * va) { vnet_sw_hw_interface_state_t *s = va_arg (*va, vnet_sw_hw_interface_state_t *); serialize (m, serialize_vec_vnet_sw_hw_interface_state, s, 1); } static void unserialize_vnet_sw_interface_set_flags (serialize_main_t * m, va_list * va) { CLIB_UNUSED (mc_main_t * mc) = va_arg (*va, mc_main_t *); vnet_sw_hw_interface_state_t s; unserialize (m, unserialize_vec_vnet_sw_hw_interface_state, &s, 1); vnet_sw_interface_set_flags_helper (vnet_get_main (), s.sw_hw_if_index, s.flags, /* helper_flags no redistribution */ 0); } void vnet_hw_interface_set_mtu (vnet_main_t * vnm, u32 hw_if_index, u32 mtu) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); if (hi->max_packet_bytes != mtu) { u16 l3_pad = hi->max_packet_bytes - hi->max_l3_packet_bytes[VLIB_TX]; hi->max_packet_bytes = mtu; hi->max_l3_packet_bytes[VLIB_TX] = hi->max_l3_packet_bytes[VLIB_RX] = mtu - l3_pad; ethernet_set_flags (vnm, hw_if_index, ETHERNET_INTERFACE_FLAG_MTU); adj_mtu_update (hw_if_index); } } static void unserialize_vnet_hw_interface_set_flags (serialize_main_t * m, va_list * va) { CLIB_UNUSED (mc_main_t * mc) = va_arg (*va, mc_main_t *); vnet_sw_hw_interface_state_t s; unserialize (m, unserialize_vec_vnet_sw_hw_interface_state, &s, 1); vnet_hw_interface_set_flags_helper (vnet_get_main (), s.sw_hw_if_index, s.flags, /* helper_flags no redistribution */ 0); } MC_SERIALIZE_MSG (vnet_sw_interface_set_flags_msg, static) = { .name = "vnet_sw_interface_set_flags",.serialize = serialize_vnet_sw_hw_interface_set_flags,.unserialize = unserialize_vnet_sw_interface_set_flags,}; MC_SERIALIZE_MSG (vnet_hw_interface_set_flags_msg, static) = { .name = "vnet_hw_interface_set_flags",.serialize = serialize_vnet_sw_hw_interface_set_flags,.unserialize = unserialize_vnet_hw_interface_set_flags,}; void serialize_vnet_interface_state (serialize_main_t * m, va_list * va) { vnet_main_t *vnm = va_arg (*va, vnet_main_t *); vnet_sw_hw_interface_state_t *sts = 0, *st; vnet_sw_interface_t *sif; vnet_hw_interface_t *hif; vnet_interface_main_t *im = &vnm->interface_main; /* Serialize hardware interface classes since they may have changed. Must do this before sending up/down flags. */ /* *INDENT-OFF* */ pool_foreach (hif, im->hw_interfaces, ({ vnet_hw_interface_class_t * hw_class = vnet_get_hw_interface_class (vnm, hif->hw_class_index); serialize_cstring (m, hw_class->name); })); /* *INDENT-ON* */ /* Send sw/hw interface state when non-zero. */ /* *INDENT-OFF* */ pool_foreach (sif, im->sw_interfaces, ({ if (sif->flags != 0) { vec_add2 (sts, st, 1); st->sw_hw_if_index = sif->sw_if_index; st->flags = sif->flags; } })); /* *INDENT-ON* */ vec_serialize (m, sts, serialize_vec_vnet_sw_hw_interface_state); if (sts) _vec_len (sts) = 0; /* *INDENT-OFF* */ pool_foreach (hif, im->hw_interfaces, ({ if (hif->flags != 0) { vec_add2 (sts, st, 1); st->sw_hw_if_index = hif->hw_if_index; st->flags = hif->flags; } })); /* *INDENT-ON* */ vec_serialize (m, sts, serialize_vec_vnet_sw_hw_interface_state); vec_free (sts); } void unserialize_vnet_interface_state (serialize_main_t * m, va_list * va) { vnet_main_t *vnm = va_arg (*va, vnet_main_t *); vnet_sw_hw_interface_state_t *sts = 0, *st; /* First set interface hardware class. */ { vnet_interface_main_t *im = &vnm->interface_main; vnet_hw_interface_t *hif; char *class_name; uword *p; clib_error_t *error; /* *INDENT-OFF* */ pool_foreach (hif, im->hw_interfaces, ({ unserialize_cstring (m, &class_name); p = hash_get_mem (im->hw_interface_class_by_name, class_name); ASSERT (p != 0); error = vnet_hw_interface_set_class_helper (vnm, hif->hw_if_index, p[0], /* redistribute */ 0); if (error) clib_error_report (error); vec_free (class_name); })); /* *INDENT-ON* */ } vec_unserialize (m, &sts, unserialize_vec_vnet_sw_hw_interface_state); vec_foreach (st, sts) vnet_sw_interface_set_flags_helper (vnm, st->sw_hw_if_index, st->flags, /* no distribute */ 0); vec_free (sts); vec_unserialize (m, &sts, unserialize_vec_vnet_sw_hw_interface_state); vec_foreach (st, sts) vnet_hw_interface_set_flags_helper (vnm, st->sw_hw_if_index, st->flags, /* no distribute */ 0); vec_free (sts); } static clib_error_t * call_elf_section_interface_callbacks (vnet_main_t * vnm, u32 if_index, u32 flags, _vnet_interface_function_list_elt_t ** elts) { _vnet_interface_function_list_elt_t *elt; vnet_interface_function_priority_t prio; clib_error_t *error = 0; for (prio = VNET_ITF_FUNC_PRIORITY_LOW; prio <= VNET_ITF_FUNC_PRIORITY_HIGH; prio++) { elt = elts[prio]; while (elt) { error = elt->fp (vnm, if_index, flags); if (error) return error; elt = elt->next_interface_function; } } return error; } static clib_error_t * call_hw_interface_add_del_callbacks (vnet_main_t * vnm, u32 hw_if_index, u32 is_create) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); vnet_hw_interface_class_t *hw_class = vnet_get_hw_interface_class (vnm, hi->hw_class_index); vnet_device_class_t *dev_class = vnet_get_device_class (vnm, hi->dev_class_index); clib_error_t *error = 0; if (hw_class->interface_add_del_function && (error = hw_class->interface_add_del_function (vnm, hw_if_index, is_create))) return error; if (dev_class->interface_add_del_function && (error = dev_class->interface_add_del_function (vnm, hw_if_index, is_create))) return error; error = call_elf_section_interface_callbacks (vnm, hw_if_index, is_create, vnm->hw_interface_add_del_functions); return error; } static clib_error_t * call_sw_interface_add_del_callbacks (vnet_main_t * vnm, u32 sw_if_index, u32 is_create) { return call_elf_section_interface_callbacks (vnm, sw_if_index, is_create, vnm->sw_interface_add_del_functions); } #define VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE (1 << 0) #define VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE (1 << 1) static clib_error_t * vnet_hw_interface_set_flags_helper (vnet_main_t * vnm, u32 hw_if_index, u32 flags, u32 helper_flags) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); vnet_hw_interface_class_t *hw_class = vnet_get_hw_interface_class (vnm, hi->hw_class_index); vnet_device_class_t *dev_class = vnet_get_device_class (vnm, hi->dev_class_index); vlib_main_t *vm = vnm->vlib_main; u32 mask; clib_error_t *error = 0; u32 is_create = (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE) != 0; mask = (VNET_HW_INTERFACE_FLAG_LINK_UP | VNET_HW_INTERFACE_FLAG_DUPLEX_MASK | VNET_HW_INTERFACE_FLAG_SPEED_MASK); flags &= mask; /* Call hardware interface add/del callbacks. */ if (is_create) call_hw_interface_add_del_callbacks (vnm, hw_if_index, is_create); /* Already in the desired state? */ if (!is_create && (hi->flags & mask) == flags) goto done; /* Some interface classes do not redistribute (e.g. are local). */ if (!dev_class->redistribute) helper_flags &= ~VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE; if (vm->mc_main && (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE)) { vnet_sw_hw_interface_state_t s; s.sw_hw_if_index = hw_if_index; s.flags = flags; mc_serialize (vm->mc_main, &vnet_hw_interface_set_flags_msg, &s); } if ((hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) != (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)) { /* Do hardware class (e.g. ethernet). */ if (hw_class->link_up_down_function && (error = hw_class->link_up_down_function (vnm, hw_if_index, flags))) goto done; error = call_elf_section_interface_callbacks (vnm, hw_if_index, flags, vnm->hw_interface_link_up_down_functions); if (error) goto done; } hi->flags &= ~mask; hi->flags |= flags; done: return error; } static clib_error_t * vnet_sw_interface_set_flags_helper (vnet_main_t * vnm, u32 sw_if_index, u32 flags, u32 helper_flags) { vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); vlib_main_t *vm = vnm->vlib_main; u32 mask; clib_error_t *error = 0; u32 is_create = (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE) != 0; u32 old_flags; mask = VNET_SW_INTERFACE_FLAG_ADMIN_UP | VNET_SW_INTERFACE_FLAG_PUNT; flags &= mask; if (is_create) { error = call_sw_interface_add_del_callbacks (vnm, sw_if_index, is_create); if (error) goto done; if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) { /* Notify everyone when the interface is created as admin up */ error = call_elf_section_interface_callbacks (vnm, sw_if_index, flags, vnm-> sw_interface_admin_up_down_functions); if (error) goto done; } } else { vnet_sw_interface_t *si_sup = si; /* Check that super interface is in correct state. */ if (si->type == VNET_SW_INTERFACE_TYPE_SUB) { si_sup = vnet_get_sw_interface (vnm, si->sup_sw_if_index); /* Check to see if we're bringing down the soft interface and if it's parent is up */ if ((flags != (si_sup->flags & mask)) && (!((flags == 0) && ((si_sup->flags & mask) == VNET_SW_INTERFACE_FLAG_ADMIN_UP)))) { error = clib_error_return (0, "super-interface %U must be %U", format_vnet_sw_interface_name, vnm, si_sup, format_vnet_sw_interface_flags, flags); goto done; } } /* Donot change state for slave link of bonded interfaces */ if (si->flags & VNET_SW_INTERFACE_FLAG_BOND_SLAVE) { error = clib_error_return (0, "not allowed as %U belong to a BondEthernet interface", format_vnet_sw_interface_name, vnm, si); goto done; } /* Already in the desired state? */ if ((si->flags & mask) == flags) goto done; /* Sub-interfaces of hardware interfaces that do no redistribute, do not redistribute themselves. */ if (si_sup->type == VNET_SW_INTERFACE_TYPE_HARDWARE) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, si_sup->hw_if_index); vnet_device_class_t *dev_class = vnet_get_device_class (vnm, hi->dev_class_index); if (!dev_class->redistribute) helper_flags &= ~VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE; } if (vm->mc_main && (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE)) { vnet_sw_hw_interface_state_t s; s.sw_hw_if_index = sw_if_index; s.flags = flags; mc_serialize (vm->mc_main, &vnet_sw_interface_set_flags_msg, &s); } /* set the flags now before invoking the registered clients * so that the state they query is consistent with the state here notified */ old_flags = si->flags; si->flags &= ~mask; si->flags |= flags; if ((flags | old_flags) & VNET_SW_INTERFACE_FLAG_ADMIN_UP) error = call_elf_section_interface_callbacks (vnm, sw_if_index, flags, vnm->sw_interface_admin_up_down_functions); si->flags = old_flags; if (error) goto done; if (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, si->hw_if_index); vnet_hw_interface_class_t *hw_class = vnet_get_hw_interface_class (vnm, hi->hw_class_index); vnet_device_class_t *dev_class = vnet_get_device_class (vnm, hi->dev_class_index); if ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) && (si->flags & VNET_SW_INTERFACE_FLAG_ERROR)) { error = clib_error_return (0, "Interface in the error state"); goto done; } /* save the si admin up flag */ old_flags = si->flags; /* update si admin up flag in advance if we are going admin down */ if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)) si->flags &= ~VNET_SW_INTERFACE_FLAG_ADMIN_UP; if (dev_class->admin_up_down_function && (error = dev_class->admin_up_down_function (vnm, si->hw_if_index, flags))) { /* restore si admin up flag to it's original state on errors */ si->flags = old_flags; goto done; } if (hw_class->admin_up_down_function && (error = hw_class->admin_up_down_function (vnm, si->hw_if_index, flags))) { /* restore si admin up flag to it's original state on errors */ si->flags = old_flags; goto done; } /* Admin down implies link down. */ if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) && (hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)) vnet_hw_interface_set_flags_helper (vnm, si->hw_if_index, hi->flags & ~VNET_HW_INTERFACE_FLAG_LINK_UP, helper_flags); } } si->flags &= ~mask; si->flags |= flags; done: return error; } clib_error_t * vnet_hw_interface_set_flags (vnet_main_t * vnm, u32 hw_if_index, u32 flags) { return vnet_hw_interface_set_flags_helper (vnm, hw_if_index, flags, VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE); } clib_error_t * vnet_sw_interface_set_flags (vnet_main_t * vnm, u32 sw_if_index, u32 flags) { return vnet_sw_interface_set_flags_helper (vnm, sw_if_index, flags, VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE); } static u32 vnet_create_sw_interface_no_callbacks (vnet_main_t * vnm, vnet_sw_interface_t * template) { vnet_interface_main_t *im = &vnm->interface_main; vnet_sw_interface_t *sw; u32 sw_if_index; pool_get (im->sw_interfaces, sw); sw_if_index = sw - im->sw_interfaces; sw[0] = template[0]; sw->flags = 0; sw->sw_if_index = sw_if_index; if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE) sw->sup_sw_if_index = sw->sw_if_index; /* Allocate counters for this interface. */ { u32 i; vnet_interface_counter_lock (im); for (i = 0; i < vec_len (im->sw_if_counters); i++) { vlib_validate_simple_counter (&im->sw_if_counters[i], sw_if_index); vlib_zero_simple_counter (&im->sw_if_counters[i], sw_if_index); } for (i = 0; i < vec_len (im->combined_sw_if_counters); i++) { vlib_validate_combined_counter (&im->combined_sw_if_counters[i], sw_if_index); vlib_zero_combined_counter (&im->combined_sw_if_counters[i], sw_if_index); } vnet_interface_counter_unlock (im); } return sw_if_index; } clib_error_t * vnet_create_sw_interface (vnet_main_t * vnm, vnet_sw_interface_t * template, u32 * sw_if_index) { clib_error_t *error; vnet_hw_interface_t *hi; vnet_device_class_t *dev_class; hi = vnet_get_sup_hw_interface (vnm, template->sup_sw_if_index); dev_class = vnet_get_device_class (vnm, hi->dev_class_index); if (template->type == VNET_SW_INTERFACE_TYPE_SUB && dev_class->subif_add_del_function) { error = dev_class->subif_add_del_function (vnm, hi->hw_if_index, (struct vnet_sw_interface_t *) template, 1); if (error) return error; } *sw_if_index = vnet_create_sw_interface_no_callbacks (vnm, template); error = vnet_sw_interface_set_flags_helper (vnm, *sw_if_index, template->flags, VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE); if (error) { /* undo the work done by vnet_create_sw_interface_no_callbacks() */ vnet_interface_main_t *im = &vnm->interface_main; vnet_sw_interface_t *sw = pool_elt_at_index (im->sw_interfaces, *sw_if_index); pool_put (im->sw_interfaces, sw); } return error; } void vnet_delete_sw_interface (vnet_main_t * vnm, u32 sw_if_index) { vnet_interface_main_t *im = &vnm->interface_main; vnet_sw_interface_t *sw = pool_elt_at_index (im->sw_interfaces, sw_if_index); /* Check if the interface has config and is removed from L2 BD or XConnect */ vlib_main_t *vm = vlib_get_main (); l2_input_config_t *config; if (sw_if_index < vec_len (l2input_main.configs)) { config = vec_elt_at_index (l2input_main.configs, sw_if_index); if (config->xconnect) set_int_l2_mode (vm, vnm, MODE_L3, config->output_sw_if_index, 0, 0, 0, 0); if (config->xconnect || config->bridge) set_int_l2_mode (vm, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); } /* Bring down interface in case it is up. */ if (sw->flags != 0) vnet_sw_interface_set_flags (vnm, sw_if_index, /* flags */ 0); call_sw_interface_add_del_callbacks (vnm, sw_if_index, /* is_create */ 0); pool_put (im->sw_interfaces, sw); } static void setup_tx_node (vlib_main_t * vm, u32 node_index, vnet_device_class_t * dev_class) { vlib_node_t *n = vlib_get_node (vm, node_index); n->function = dev_class->tx_function; n->format_trace = dev_class->format_tx_trace; vlib_register_errors (vm, node_index, dev_class->tx_function_n_errors, dev_class->tx_function_error_strings); } static void setup_output_node (vlib_main_t * vm, u32 node_index, vnet_hw_interface_class_t * hw_class) { vlib_node_t *n = vlib_get_node (vm, node_index); n->format_buffer = hw_class->format_header; n->unformat_buffer = hw_class->unformat_header; } /* Register an interface instance. */ u32 vnet_register_interface (vnet_main_t * vnm, u32 dev_class_index, u32 dev_instance, u32 hw_class_index, u32 hw_instance) { vnet_interface_main_t *im = &vnm->interface_main; vnet_hw_interface_t *hw; vnet_device_class_t *dev_class = vnet_get_device_class (vnm, dev_class_index); vnet_hw_interface_class_t *hw_class = vnet_get_hw_interface_class (vnm, hw_class_index); vlib_main_t *vm = vnm->vlib_main; vnet_feature_config_main_t *fcm; vnet_config_main_t *cm; u32 hw_index, i; char *tx_node_name, *output_node_name; pool_get (im->hw_interfaces, hw); memset (hw, 0, sizeof (*hw)); hw_index = hw - im->hw_interfaces; hw->hw_if_index = hw_index; hw->default_rx_mode = VNET_HW_INTERFACE_RX_MODE_POLLING; if (dev_class->format_device_name) hw->name = format (0, "%U", dev_class->format_device_name, dev_instance); else if (hw_class->format_interface_name) hw->name = format (0, "%U", hw_class->format_interface_name, dev_instance); else hw->name = format (0, "%s%x", hw_class->name, dev_instance); if (!im->hw_interface_by_name) im->hw_interface_by_name = hash_create_vec ( /* size */ 0, sizeof (hw->name[0]), sizeof (uword)); hash_set_mem (im->hw_interface_by_name, hw->name, hw_index); /* Make hardware interface point to software interface. */ { vnet_sw_interface_t sw = { .type = VNET_SW_INTERFACE_TYPE_HARDWARE, .flood_class = VNET_FLOOD_CLASS_NORMAL, .hw_if_index = hw_index }; hw->sw_if_index = vnet_create_sw_interface_no_callbacks (vnm, &sw); } hw->dev_class_index = dev_class_index; hw->dev_instance = dev_instance; hw->hw_class_index = hw_class_index; hw->hw_instance = hw_instance; hw->max_rate_bits_per_sec = 0; hw-
/*
 * l2_in_out_acl.c : layer 2 input/output acl processing
 *
 * Copyright (c) 2013,2018 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ethernet/packet.h>
#include <vnet/ip/ip_packet.h>
#include <vnet/ip/ip4_packet.h>
#include <vnet/ip/ip6_packet.h>
#include <vlib/cli.h>
#include <vnet/l2/l2_input.h>
#include <vnet/l2/l2_output.h>
#include <vnet/l2/feat_bitmap.h>

#include <vppinfra/error.h>
#include <vppinfra/hash.h>
#include <vppinfra/cache.h>

#include <vnet/classify/vnet_classify.h>
#include <vnet/classify/in_out_acl.h>

typedef struct
{

  /* Next nodes for each feature */
  u32 feat_next_node_index[IN_OUT_ACL_N_TABLE_GROUPS][32];

  /* convenience variables */
  vlib_main_t *vlib_main;
  vnet_main_t *vnet_main;
} l2_in_out_acl_main_t;

typedef struct
{
  u32 sw_if_index;
  u32 next_index;
  u32 table_index;
  u32 offset;
} l2_in_out_acl_trace_t;

/* packet trace format function */
static u8 *
format_l2_in_out_acl_trace (u8 * s, u32 is_output, 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 *);
  l2_in_out_acl_trace_t *t = va_arg (*args, l2_in_out_acl_trace_t *);

  s = format (s, "%s: sw_if_index %d, next_index %d, table %d, offset %d",
	      is_output ? "OUTACL" : "INACL",
	      t->sw_if_index, t->next_index, t->table_index, t->offset);
  return s;
}

static u8 *
format_l2_inacl_trace (u8 * s, va_list * args)
{
  return format_l2_in_out_acl_trace (s, IN_OUT_ACL_INPUT_TABLE_GROUP, args);
}

static u8 *
format_l2_outacl_trace (u8 * s, va_list * args)
{
  return format_l2_in_out_acl_trace (s, IN_OUT_ACL_OUTPUT_TABLE_GROUP, args);
}

extern l2_in_out_acl_main_t l2_in_out_acl_main;

#ifndef CLIB_MARCH_VARIANT
l2_in_out_acl_main_t l2_in_out_acl_main;
#endif /* CLIB_MARCH_VARIANT */

extern vlib_node_registration_t l2_inacl_node;
extern vlib_node_registration_t l2_outacl_node;

#define foreach_l2_inacl_error                  \
_(NONE, "valid input ACL packets")              \
_(MISS, "input ACL misses")                     \
_(HIT, "input ACL hits")                        \
_(CHAIN_HIT, "input ACL hits after chain walk") \
_(TABLE_MISS, "input ACL table-miss drops")     \
_(SESSION_DENY, "input ACL session deny drops")

#define foreach_l2_outacl_error                  \
_(NONE, "valid output ACL packets")              \
_(MISS, "output ACL misses")                     \
_(HIT, "output ACL hits")                        \
_(CHAIN_HIT, "output ACL hits after chain walk") \
_(TABLE_MISS, "output ACL table-miss drops")     \
_(SESSION_DENY, "output ACL session deny drops")


typedef enum
{
#define _(sym,str) L2_INACL_ERROR_##sym,
  foreach_l2_inacl_error
#undef _
    L2_INACL_N_ERROR,
} l2_inacl_error_t;

static char *l2_inacl_error_strings[] = {
#define _(sym,string) string,
  foreach_l2_inacl_error
#undef _
};

typedef enum
{
#define _(sym,str) L2_OUTACL_ERROR_##sym,
  foreach_l2_outacl_error
#undef _
    L2_OUTACL_N_ERROR,
} l2_outacl_error_t;

static char *l2_outacl_error_strings[] = {
#define _(sym,string) string,
  foreach_l2_outacl_error
#undef _
};


static inline uword
l2_in_out_acl_node_fn (vlib_main_t * vm,
		       vlib_node_runtime_t * node, vlib_frame_t * frame,
		       int is_output)
{
  u32 n_left_from, *from, *to_next;
  acl_next_index_t next_index;
  l2_in_out_acl_main_t *msm = &l2_in_out_acl_main;
  in_out_acl_main_t *am = &in_out_acl_main;
  vnet_classify_main_t *vcm = am->vnet_classify_main;
  <