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 <damarion@cisco.com>
This commit is contained in:

committed by
Dave Barach

parent
64f450da8f
commit
3d9c86e9f7
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
};
|
||||
|
@@ -37,6 +37,8 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/pg/pg.h>
|
||||
|
||||
@@ -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 <interface name> pcap <filename> [count <n>]",
|
||||
.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 <interface name>",
|
||||
.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; }
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -38,15 +38,35 @@
|
||||
*/
|
||||
|
||||
#include <vlib/vlib.h>
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/pg/pg.h>
|
||||
#include <vnet/ethernet/ethernet.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -43,6 +43,10 @@
|
||||
#include <vlib/vlib.h> /* for VLIB_N_RX_TX */
|
||||
#include <vnet/pg/edit.h>
|
||||
#include <vppinfra/fifo.h> /* for buffer_fifo */
|
||||
#include <vnet/unix/pcap.h>
|
||||
#include <vnet/interface.h>
|
||||
|
||||
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)
|
||||
|
@@ -39,12 +39,13 @@
|
||||
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/pg/pg.h>
|
||||
#include <vnet/ethernet/ethernet.h>
|
||||
|
||||
/* 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);
|
||||
u8 hw_addr[6];
|
||||
f64 now = vlib_time_now(vm);
|
||||
u32 rnd;
|
||||
|
||||
pi->stream_index = stream_index;
|
||||
pi->hw_if_index = vnet_register_interface (vnm,
|
||||
pg_dev_class.index, i,
|
||||
pg_interface_class.index, stream_index);
|
||||
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)
|
||||
|
Reference in New Issue
Block a user