/* * 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. */ /* * pg_stream.c: packet generator streams * * 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 #include /* Mark stream active or inactive. */ void pg_stream_enable_disable (pg_main_t * pg, pg_stream_t * s, int want_enabled) { vlib_main_t *vm; vnet_main_t *vnm = vnet_get_main (); pg_interface_t *pi = pool_elt_at_index (pg->interfaces, s->pg_if_index); want_enabled = want_enabled != 0; if (pg_stream_is_enabled (s) == want_enabled) /* No change necessary. */ return; if (want_enabled) s->n_packets_generated = 0; /* Toggle enabled flag. */ s->flags ^= PG_STREAM_FLAGS_IS_ENABLED; ASSERT (!pool_is_free (pg->streams, s)); vec_validate (pg->enabled_streams, s->worker_index); pg->enabled_streams[s->worker_index] = clib_bitmap_set (pg->enabled_streams[s->worker_index], s - pg->streams, want_enabled); if (want_enabled) { vnet_hw_interface_set_flags (vnm, pi->hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP); vnet_sw_interface_set_flags (vnm, pi->sw_if_index, VNET_SW_INTERFACE_FLAG_ADMIN_UP); } if (vlib_num_workers ()) vm = vlib_get_worker_vlib_main (s->worker_index); else vm = vlib_get_main (); vlib_node_set_state (vm, pg_input_node.index, (clib_bitmap_is_zero (pg->enabled_streams[s->worker_index]) ? VLIB_NODE_STATE_DISABLED : VLIB_NODE_STATE_POLLING)); s->packet_accumulator = 0; s->time_last_generate = 0; } static u8 * format_pg_output_trace (u8 * s, va_list * va) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); pg_output_trace_t *t = va_arg (*va, pg_output_trace_t *); u32 indent = format_get_indent (s); s = format (s, "%Ubuffer 0x%x: %U", format_white_space, indent, t->buffer_index, format_vnet_buffer, &t->buffer); s = format (s, "\n%U%U", format_white_space, indent, format_ethernet_header_with_length, t->buffer.pre_data, sizeof (t->buffer.pre_data)); return s; } static u8 * format_pg_interface_name (u8 * s, va_list * args) { pg_main_t *pg = &pg_main; u32 if_index = va_arg (*args, u32); pg_interface_t *pi; pi = pool_elt_at_index (pg->interfaces, if_index); s = format (s, "pg%d", pi->id); return s; } static clib_error_t * pg_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) { u32 hw_flags = 0; if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) hw_flags = VNET_HW_INTERFACE_FLAG_LINK_UP; vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); return 0; } /* *INDENT-OFF* */ VNET_DEVICE_CLASS (pg_dev_class) = { .name = "pg", .tx_function = pg_output, .format_device_name = format_pg_interface_name, .format_tx_trace = format_pg_output_trace, .admin_up_down_function = pg_interface_admin_up_down, }; /* *INDENT-ON* */ static u8 * pg_build_rewrite (vnet_main_t * vnm, u32 sw_if_index, vnet_link_t link_type, const void *dst_address) { u8 *rewrite = NULL; u16 *h; vec_validate (rewrite, sizeof (*h) - 1); h = (u16 *) rewrite; h[0] = clib_host_to_net_u16 (vnet_link_to_l3_proto (link_type)); return (rewrite); } /* *INDENT-OFF* */ VNET_HW_INTERFACE_CLASS (pg_interface_class,static) = { .name = "Packet generator", .build_rewrite = pg_build_rewrite, }; /* *INDENT-ON* */ static u32 pg_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, u32 flags) { /* nothing for now */ return 0; } u32 pg_interface_add_or_get (pg_main_t * pg, uword if_id) { vnet_main_t *vnm = vnet_get_main (); vlib_main_t *vm = vlib_get_main (); pg_interface_t *pi; vnet_hw_interface_t *hi; uword *p; u32 i; p = hash_get (pg->if_index_by_if_id, if_id); if (p) { return p[0]; } else { u8 hw_addr[6]; f64 now = vlib_time_now (vm); u32 rnd; pool_get (pg->interfaces, pi); i = pi - pg->interfaces; rnd = (u32) (now * 1e6); rnd = random_u32 (&rnd); clib_memcpy (hw_addr + 2, &rnd, sizeof (rnd)); hw_addr[0] = 2; hw_addr[1] = 0xfe; pi->id = if_id; ethernet_register_interface (vnm, pg_dev_class.index, i, hw_addr, &pi->hw_if_index, pg_eth_flag_change); hi = vnet_get_hw_interface (vnm, pi->hw_if_index); pi->sw_if_index = hi->sw_if_index; hash_set (pg->if_index_by_if_id, if_id, i); if (vlib_num_workers ()) { pi->lockp = clib_mem_al