From 3d9c86e9f70892c82c11530e0db7db78b7e6ce21 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 4 Jul 2016 21:04:40 +0200 Subject: Add support for capturing packets on packet generator interfaces This patch introduces following changes: - 4 predefined pg/stream[0-3] interfaces are removed - Interface naming is changed form pg/streamX to pgX where X can be any u32 value - one pgX interface can handle multiple streams - keyword "source pgX" is added to "packet-generator add" command, X is 0 by default - new cli "packet-generator capture" is introduced - new cli "create packet-generator interface pgX" Change-Id: I768d075b9d4a34f0b5073debdc5dd4a0880c682c Signed-off-by: Damjan Marion --- vnet/vnet/cop/cop.c | 3 +- vnet/vnet/interface.h | 2 + vnet/vnet/misc.c | 2 +- vnet/vnet/pg/cli.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++ vnet/vnet/pg/init.c | 16 +------ vnet/vnet/pg/output.c | 22 +++++++++- vnet/vnet/pg/pg.h | 18 +++++--- vnet/vnet/pg/stream.c | 92 ++++++++++++++++++++++++++-------------- 8 files changed, 215 insertions(+), 53 deletions(-) (limited to 'vnet') diff --git a/vnet/vnet/cop/cop.c b/vnet/vnet/cop/cop.c index a352b371..465d6c97 100644 --- a/vnet/vnet/cop/cop.c +++ b/vnet/vnet/cop/cop.c @@ -22,6 +22,7 @@ cop_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) cop_main_t * cm = &cop_main; cop_config_data_t _data, *data = &_data; vlib_main_t * vm = cm->vlib_main; + vnet_hw_interface_t * hi = vnet_get_sup_hw_interface (vnm, sw_if_index);; cop_config_main_t * ccm; int address_family; u32 ci, default_next; @@ -32,7 +33,7 @@ cop_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) * Ignore local interface, pg interfaces. $$$ need a #define for the * first "real" interface. The answer is 5 at the moment. */ - if (sw_if_index < 5) + if (hi->dev_class_index == vnet_local_interface_device_class.index) return 0; for (address_family = VNET_COP_IP4; address_family < VNET_N_COPS; diff --git a/vnet/vnet/interface.h b/vnet/vnet/interface.h index 30dcf276..11bb2346 100644 --- a/vnet/vnet/interface.h +++ b/vnet/vnet/interface.h @@ -354,6 +354,8 @@ typedef struct vnet_hw_interface_t { } vnet_hw_interface_t; +extern vnet_device_class_t vnet_local_interface_device_class; + typedef enum { /* A hw interface. */ VNET_SW_INTERFACE_TYPE_HARDWARE, diff --git a/vnet/vnet/misc.c b/vnet/vnet/misc.c index 9dbed8d8..89bc15c1 100644 --- a/vnet/vnet/misc.c +++ b/vnet/vnet/misc.c @@ -56,7 +56,7 @@ vnet_local_interface_tx (vlib_main_t * vm, return f->n_vectors; } -VNET_DEVICE_CLASS (vnet_local_interface_device_class,static) = { +VNET_DEVICE_CLASS (vnet_local_interface_device_class) = { .name = "local", .tx_function = vnet_local_interface_tx, }; diff --git a/vnet/vnet/pg/cli.c b/vnet/vnet/pg/cli.c index 495eac2d..428a3b61 100644 --- a/vnet/vnet/pg/cli.c +++ b/vnet/vnet/pg/cli.c @@ -37,6 +37,8 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include + #include #include @@ -248,6 +250,7 @@ new_stream (vlib_main_t * vm, s.node_index = ~0; s.max_packet_bytes = s.min_packet_bytes = 64; s.buffer_bytes = VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES; + s.if_id = 0; pcap_file_name = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -267,6 +270,9 @@ new_stream (vlib_main_t * vm, s.sw_if_index[VLIB_TX] = hi->sw_if_index; } + else if (unformat (input, "source pg%u",&s.if_id)) + ; + else if (unformat (input, "node %U", unformat_vlib_node, vm, &s.node_index)) ; @@ -431,6 +437,113 @@ VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = { .function = change_stream_parameters, }; +static clib_error_t * +pg_capture_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + pg_main_t * pg = &pg_main; + clib_error_t * error = 0; + vnet_main_t * vnm = vnet_get_main(); + unformat_input_t _line_input, * line_input = &_line_input; + vnet_hw_interface_t * hi = 0; + pg_interface_t * pi; + u8 * pcap_file_name = 0; + u32 hw_if_index; + u32 count = ~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)) + { + hi = vnet_get_hw_interface (vnm, hw_if_index); + } + + else if (unformat (line_input, "pcap %s", &pcap_file_name)) + ; + else if (unformat (line_input, "count %u", &count)) + ; + + else + { + error = clib_error_create ("unknown input `%U'", + format_unformat_error, input); + return error; + } + } + + if (!hi) + return clib_error_return (0, "Please specify interface name"); + + if (hi->dev_class_index != pg_dev_class.index) + return clib_error_return (0, "Please specify packet-generator interface"); + + if (!pcap_file_name) + return clib_error_return (0, "Please specify pcap file name"); + + { + struct stat sb; + if (stat ((char *) pcap_file_name, &sb) != -1) + return clib_error_return (0, "Cannot create pcap file"); + } + + unformat_free (line_input); + + pi = pool_elt_at_index (pg->interfaces, hi->dev_instance); + vec_free (pi->pcap_file_name); + pi->pcap_file_name = pcap_file_name; + memset (&pi->pcap_main, 0, sizeof (pi->pcap_main)); + pi->pcap_main.file_name = (char *) pi->pcap_file_name; + pi->pcap_main.n_packets_to_capture = count; + pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet; + + return 0; +} + +VLIB_CLI_COMMAND (pg_capture_cmd, static) = { + .path = "packet-generator capture", + .short_help = "packet-generator capture pcap [count ]", + .function = pg_capture_cmd_fn, +}; + +static clib_error_t * +create_pg_if_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + pg_main_t * pg = &pg_main; + unformat_input_t _line_input, * line_input = &_line_input; + u32 if_id; + + 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, "interface pg%u", &if_id)) + ; + + else + return clib_error_create ("unknown input `%U'", + format_unformat_error, input); + } + + unformat_free (line_input); + + pg_interface_add_or_get (pg, if_id); + return 0; +} + +VLIB_CLI_COMMAND (create_pg_if_cmd, static) = { + .path = "create packet-generator", + .short_help = "create packet-generator interface ", + .function = create_pg_if_cmd_fn, +}; + /* Dummy init function so that we can be linked in. */ static clib_error_t * pg_cli_init (vlib_main_t * vm) { return 0; } diff --git a/vnet/vnet/pg/init.c b/vnet/vnet/pg/init.c index f598050c..5cbb6db0 100644 --- a/vnet/vnet/pg/init.c +++ b/vnet/vnet/pg/init.c @@ -47,9 +47,9 @@ static clib_error_t * pg_init (vlib_main_t * vm) { clib_error_t * error; pg_main_t * pg = &pg_main; - int i, j; pg->vlib_main = vm; + pg->if_index_by_if_id = hash_create (0, sizeof (uword)); if ((error = vlib_call_init_function (vm, vnet_main_init))) goto done; @@ -57,20 +57,6 @@ static clib_error_t * pg_init (vlib_main_t * vm) if ((error = vlib_call_init_function (vm, pg_cli_init))) goto done; - /* Create/free interfaces so that they exist and can be - used as a destination interface for streams. Also, create - a fixed number of pg interfaces so that interface numbering can - be made to be deterministic (at least if <= 4 streams are ever used). */ - for (i = 0; i < 4; i++) - { - j = pg_interface_find_free (pg, i); - ASSERT (j == i); - } - - /* Free interfaces. */ - for (i = j; i >= 0; i--) - vec_add1 (pg->free_interfaces, i); - done: return error; } diff --git a/vnet/vnet/pg/output.c b/vnet/vnet/pg/output.c index cc098da2..5ce3f903 100644 --- a/vnet/vnet/pg/output.c +++ b/vnet/vnet/pg/output.c @@ -38,15 +38,35 @@ */ #include +#include #include +#include uword pg_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { + pg_main_t * pg = &pg_main; u32 * buffers = vlib_frame_args (frame); uword n_buffers = frame->n_vectors; - vlib_buffer_free_no_next (vm, buffers, n_buffers); + uword n_left = n_buffers; + vnet_interface_output_runtime_t * rd = (void *) node->runtime_data; + pg_interface_t * pif = pool_elt_at_index (pg->interfaces, rd->dev_instance); + + if (pif->pcap_file_name != 0) + { + while (n_left > 0) + { + n_left--; + u32 bi0 = buffers[0]; + buffers++; + + pcap_add_buffer (&pif->pcap_main, vm, bi0, ETHERNET_MAX_PACKET_BYTES); + } + pcap_write (&pif->pcap_main); + } + + vlib_buffer_free_no_next (vm, vlib_frame_args (frame), n_buffers); return n_buffers; } diff --git a/vnet/vnet/pg/pg.h b/vnet/vnet/pg/pg.h index 63bfb18a..750e7f74 100644 --- a/vnet/vnet/pg/pg.h +++ b/vnet/vnet/pg/pg.h @@ -43,6 +43,10 @@ #include /* for VLIB_N_RX_TX */ #include #include /* for buffer_fifo */ +#include +#include + +extern vnet_device_class_t pg_dev_class; struct pg_main_t; struct pg_stream_t; @@ -140,6 +144,8 @@ typedef struct pg_stream_t { /* Output next index to reach output node from stream input node. */ u32 next_index; + u32 if_id; + /* Number of packets currently generated. */ u64 n_packets_generated; @@ -281,7 +287,10 @@ typedef struct { u32 hw_if_index, sw_if_index; /* Identifies stream for this interface. */ - u32 stream_index; + u32 id; + + pcap_main_t pcap_main; + u8 * pcap_file_name; } pg_interface_t; /* Per VLIB node data. */ @@ -303,13 +312,12 @@ typedef struct pg_main_t { /* Hash mapping name -> stream index. */ uword * stream_index_by_name; - /* Vector of interfaces. */ + /* Pool of interfaces. */ pg_interface_t * interfaces; + uword * if_index_by_if_id; /* Per VLIB node information. */ pg_node_t * nodes; - - u32 * free_interfaces; } pg_main_t; /* Global main structure. */ @@ -329,7 +337,7 @@ void pg_stream_add (pg_main_t * pg, pg_stream_t * s_init); void pg_stream_enable_disable (pg_main_t * pg, pg_stream_t * s, int is_enable); /* Find/create free packet-generator interface index. */ -u32 pg_interface_find_free (pg_main_t * pg, uword stream_index); +u32 pg_interface_add_or_get (pg_main_t * pg, uword stream_index); always_inline pg_node_t * pg_get_node (uword node_index) diff --git a/vnet/vnet/pg/stream.c b/vnet/vnet/pg/stream.c index 71469145..5883817c 100644 --- a/vnet/vnet/pg/stream.c +++ b/vnet/vnet/pg/stream.c @@ -39,12 +39,13 @@ #include #include +#include /* Mark stream active or inactive. */ void pg_stream_enable_disable (pg_main_t * pg, pg_stream_t * s, int want_enabled) { vnet_main_t * vnm = vnet_get_main(); - pg_interface_t * pi = vec_elt_at_index (pg->interfaces, s->pg_if_index); + pg_interface_t * pi = pool_elt_at_index (pg->interfaces, s->pg_if_index); want_enabled = want_enabled != 0; @@ -63,15 +64,14 @@ void pg_stream_enable_disable (pg_main_t * pg, pg_stream_t * s, int want_enabled pg->enabled_streams = clib_bitmap_set (pg->enabled_streams, s - pg->streams, want_enabled); - vnet_hw_interface_set_flags (vnm, pi->hw_if_index, - (want_enabled - ? VNET_HW_INTERFACE_FLAG_LINK_UP - : 0)); + 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, - (want_enabled - ? VNET_SW_INTERFACE_FLAG_ADMIN_UP - : 0)); + vnet_sw_interface_set_flags (vnm, pi->sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + } vlib_node_set_state (pg->vlib_main, pg_input_node.index, @@ -89,16 +89,30 @@ static u8 * format_pg_interface_name (u8 * s, va_list * args) u32 if_index = va_arg (*args, u32); pg_interface_t * pi; - pi = vec_elt_at_index (pg->interfaces, if_index); - s = format (s, "pg/stream-%d", pi->stream_index); + pi = pool_elt_at_index (pg->interfaces, if_index); + s = format (s, "pg%d", pi->id); return s; } -VNET_DEVICE_CLASS (pg_dev_class,static) = { +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; +} + +VNET_DEVICE_CLASS (pg_dev_class) = { .name = "pg", .tx_function = pg_output, .format_device_name = format_pg_interface_name, + .admin_up_down_function = pg_interface_admin_up_down, }; static uword pg_set_rewrite (vnet_main_t * vnm, @@ -122,31 +136,50 @@ VNET_HW_INTERFACE_CLASS (pg_interface_class,static) = { .set_rewrite = pg_set_rewrite, }; -u32 pg_interface_find_free (pg_main_t * pg, uword stream_index) +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; - u32 i, l; + uword *p; + u32 i; - if ((l = vec_len (pg->free_interfaces)) > 0) + p = hash_get (pg->if_index_by_if_id, if_id); + + if (p) { - i = pg->free_interfaces[l - 1]; - _vec_len (pg->free_interfaces) = l - 1; - pi = vec_elt_at_index (pg->interfaces, i); - pi->stream_index = stream_index; - } + return p[0]; + } else { - i = vec_len (pg->interfaces); - vec_add2 (pg->interfaces, pi, 1); - - pi->stream_index = stream_index; - pi->hw_if_index = vnet_register_interface (vnm, - pg_dev_class.index, i, - pg_interface_class.index, stream_index); + 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); } return i; @@ -379,10 +412,10 @@ void pg_stream_add (pg_main_t * pg, pg_stream_t * s_init) } /* Find an interface to use. */ - s->pg_if_index = pg_interface_find_free (pg, s - pg->streams); + s->pg_if_index = pg_interface_add_or_get (pg, s->if_id); { - pg_interface_t * pi = vec_elt_at_index (pg->interfaces, s->pg_if_index); + pg_interface_t * pi = pool_elt_at_index (pg->interfaces, s->pg_if_index); vlib_rx_or_tx_t rx_or_tx; vlib_foreach_rx_tx (rx_or_tx) @@ -405,7 +438,6 @@ void pg_stream_del (pg_main_t * pg, uword index) s = pool_elt_at_index (pg->streams, index); pg_stream_enable_disable (pg, s, /* want_enabled */ 0); - vec_add1 (pg->free_interfaces, s->pg_if_index); hash_unset_mem (pg->stream_index_by_name, s->name); vec_foreach (bi, s->buffer_indices) -- cgit 1.2.3-korg