1cb9cadadSEd Warnicke/*
2cb9cadadSEd Warnicke * Copyright (c) 2015 Cisco and/or its affiliates.
3cb9cadadSEd Warnicke * Licensed under the Apache License, Version 2.0 (the "License");
4cb9cadadSEd Warnicke * you may not use this file except in compliance with the License.
5cb9cadadSEd Warnicke * You may obtain a copy of the License at:
6cb9cadadSEd Warnicke *
7cb9cadadSEd Warnicke *     http://www.apache.org/licenses/LICENSE-2.0
8cb9cadadSEd Warnicke *
9cb9cadadSEd Warnicke * Unless required by applicable law or agreed to in writing, software
10cb9cadadSEd Warnicke * distributed under the License is distributed on an "AS IS" BASIS,
11cb9cadadSEd Warnicke * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12cb9cadadSEd Warnicke * See the License for the specific language governing permissions and
13cb9cadadSEd Warnicke * limitations under the License.
14cb9cadadSEd Warnicke */
15cb9cadadSEd Warnicke/*
16cb9cadadSEd Warnicke * pg_cli.c: packet generator cli
17cb9cadadSEd Warnicke *
18cb9cadadSEd Warnicke * Copyright (c) 2008 Eliot Dresselhaus
19cb9cadadSEd Warnicke *
20cb9cadadSEd Warnicke * Permission is hereby granted, free of charge, to any person obtaining
21cb9cadadSEd Warnicke * a copy of this software and associated documentation files (the
22cb9cadadSEd Warnicke * "Software"), to deal in the Software without restriction, including
23cb9cadadSEd Warnicke * without limitation the rights to use, copy, modify, merge, publish,
24cb9cadadSEd Warnicke * distribute, sublicense, and/or sell copies of the Software, and to
25cb9cadadSEd Warnicke * permit persons to whom the Software is furnished to do so, subject to
26cb9cadadSEd Warnicke * the following conditions:
27cb9cadadSEd Warnicke *
28cb9cadadSEd Warnicke * The above copyright notice and this permission notice shall be
29cb9cadadSEd Warnicke * included in all copies or substantial portions of the Software.
30cb9cadadSEd Warnicke *
38cb9cadadSEd Warnicke */
39cb9cadadSEd Warnicke
403d9c86e9SDamjan Marion#include <sys/stat.h>
413d9c86e9SDamjan Marion
42cb9cadadSEd Warnicke#include <vnet/vnet.h>
43cb9cadadSEd Warnicke#include <vnet/pg/pg.h>
44cb9cadadSEd Warnicke
454222347aSKingwel Xie#include <strings.h>
463ae2873eSDave Barach#include <vppinfra/pcap.h>
474222347aSKingwel Xie
48cb9cadadSEd Warnicke
49cb9cadadSEd Warnicke/* Root of all packet generator cli commands. */
5071e97c63SCalvin/* *INDENT-OFF* */
51cb9cadadSEd WarnickeVLIB_CLI_COMMAND (vlib_cli_pg_command, static) = {
52cb9cadadSEd Warnicke  .path = "packet-generator",
53cb9cadadSEd Warnicke  .short_help = "Packet generator commands",
54cb9cadadSEd Warnicke};
5571e97c63SCalvin/* *INDENT-ON* */
56cb9cadadSEd Warnicke
5871e97c63SCalvinpg_enable_disable (u32 stream_index, int is_enable)
599e6ed6e2SPavel Kotucek{
6071e97c63SCalvin  pg_main_t *pg = &pg_main;
6171e97c63SCalvin  pg_stream_t *s;
629e6ed6e2SPavel Kotucek
6371e97c63SCalvin  if (stream_index == ~0)
6471e97c63SCalvin    {
6571e97c63SCalvin      /* No stream specified: enable/disable all streams. */
6671e97c63SCalvin      /* *INDENT-OFF* */
679e6ed6e2SPavel Kotucek        pool_foreach (s, pg->streams, ({
689e6ed6e2SPavel Kotucek            pg_stream_enable_disable (pg, s, is_enable);
699e6ed6e2SPavel Kotucek        }));
7071e97c63SCalvin	/* *INDENT-ON* */
719e6ed6e2SPavel Kotucek    }
7271e97c63SCalvin  else
739e6ed6e2SPavel Kotucek    {
7471e97c63SCalvin      /* enable/disable specified stream. */
7571e97c63SCalvin      s = pool_elt_at_index (pg->streams, stream_index);
7671e97c63SCalvin      pg_stream_enable_disable (pg, s, is_enable);
779e6ed6e2SPavel Kotucek    }
789e6ed6e2SPavel Kotucek}
799e6ed6e2SPavel Kotucek
8071e97c63SCalvinclib_error_t *
8171e97c63SCalvinpg_capture (pg_capture_args_t * a)
829e6ed6e2SPavel Kotucek{
8371e97c63SCalvin  pg_main_t *pg = &pg_main;
8471e97c63SCalvin  pg_interface_t *pi;
859e6ed6e2SPavel Kotucek
8671e97c63SCalvin  if (a->is_enabled == 1)
879e6ed6e2SPavel Kotucek    {
8871e97c63SCalvin      struct stat sb;
89db86329aSJakub Grajciar      if (stat (a->pcap_file_name, &sb) != -1)
90e3cb1f94SAndrew Yourtchenko	return clib_error_return (0, "pcap file '%s' already exists.",
919673e3e6SPaul Vinciguerra				  a->pcap_file_name);
929e6ed6e2SPavel Kotucek    }
939e6ed6e2SPavel Kotucek
9471e97c63SCalvin  pi = pool_elt_at_index (pg->interfaces, a->dev_instance);
9571e97c63SCalvin  vec_free (pi->pcap_file_name);
9619871f25SChristian E. Hopps  if ((pi->pcap_main.flags & PCAP_MAIN_INIT_DONE))
9719871f25SChristian E. Hopps    pcap_close (&pi->pcap_main);
98b7b92993SDave Barach  clib_memset (&pi->pcap_main, 0, sizeof (pi->pcap_main));
9919871f25SChristian E. Hopps  pi->pcap_main.file_descriptor = -1;
1009e6ed6e2SPavel Kotucek
10171e97c63SCalvin  if (a->is_enabled == 0)
10271e97c63SCalvin    return 0;
1039e6ed6e2SPavel Kotucek
10471e97c63SCalvin  pi->pcap_file_name = a->pcap_file_name;
10571e97c63SCalvin  pi->pcap_main.file_name = (char *) pi->pcap_file_name;
10671e97c63SCalvin  pi->pcap_main.n_packets_to_capture = a->count;
10771e97c63SCalvin  pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
1089e6ed6e2SPavel Kotucek
10971e97c63SCalvin  return 0;
1109e6ed6e2SPavel Kotucek}
1119e6ed6e2SPavel Kotucek
112cb9cadadSEd Warnickestatic clib_error_t *
113cb9cadadSEd Warnickeenable_disable_stream (vlib_main_t * vm,
11471e97c63SCalvin		       unformat_input_t * input, vlib_cli_command_t * cmd)
115cb9cadadSEd Warnicke{
1167d31ab2aSDave Barach  unformat_input_t _line_input, *line_input = &_line_input;
11771e97c63SCalvin  pg_main_t *pg = &pg_main;
118cb9cadadSEd Warnicke  int is_enable = cmd->function_arg != 0;
119cb9cadadSEd Warnicke  u32 stream_index = ~0;
120cb9cadadSEd Warnicke
1217d31ab2aSDave Barach  if (!unformat_user (input, unformat_line_input, line_input))
1227d31ab2aSDave Barach    goto doit;
1237d31ab2aSDave Barach
1247d31ab2aSDave Barach  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1257d31ab2aSDave Barach    {
1267d31ab2aSDave Barach      if (unformat (line_input, "%U", unformat_hash_vec_string,
1277d31ab2aSDave Barach		    pg->stream_index_by_name, &stream_index))
1287d31ab2aSDave Barach	;
1297d31ab2aSDave Barach      else
1307d31ab2aSDave Barach	return clib_error_create ("unknown input `%U'",
1317d31ab2aSDave Barach				  format_unformat_error, line_input);
1327d31ab2aSDave Barach    }
1337d31ab2aSDave Barach  unformat_free (line_input);
134cb9cadadSEd Warnicke
1357d31ab2aSDave Barachdoit:
1369e6ed6e2SPavel Kotucek  pg_enable_disable (stream_index, is_enable);
1379e6ed6e2SPavel Kotucek
138cb9cadadSEd Warnicke  return 0;
139cb9cadadSEd Warnicke}
140cb9cadadSEd Warnicke
14171e97c63SCalvin/* *INDENT-OFF* */
142cb9cadadSEd WarnickeVLIB_CLI_COMMAND (enable_streams_cli, static) = {
143cb9cadadSEd Warnicke  .path = "packet-generator enable-stream",
144cb9cadadSEd Warnicke  .short_help = "Enable packet generator streams",
145cb9cadadSEd Warnicke  .function = enable_disable_stream,
146cb9cadadSEd Warnicke  .function_arg = 1,		/* is_enable */
147cb9cadadSEd Warnicke};
14871e97c63SCalvin/* *INDENT-ON* */
149cb9cadadSEd Warnicke
15071e97c63SCalvin/* *INDENT-OFF* */
151cb9cadadSEd WarnickeVLIB_CLI_COMMAND (disable_streams_cli, static) = {
152cb9cadadSEd Warnicke  .path = "packet-generator disable-stream",
153cb9cadadSEd Warnicke  .short_help = "Disable packet generator streams",
154cb9cadadSEd Warnicke  .function = enable_disable_stream,
155cb9cadadSEd Warnicke  .function_arg = 0,		/* is_enable */
156cb9cadadSEd Warnicke};
15771e97c63SCalvin/* *INDENT-ON* */
158cb9cadadSEd Warnicke
1594222347aSKingwel Xiestatic u8 *
1604222347aSKingwel Xieformat_pg_edit_group (u8 * s, va_list * va)
1614222347aSKingwel Xie{
1624222347aSKingwel Xie  pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
1634222347aSKingwel Xie
1644222347aSKingwel Xie  s =
1654222347aSKingwel Xie    format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
1664222347aSKingwel Xie	    g->start_byte_offset);
1674222347aSKingwel Xie  if (g->edit_function)
1684222347aSKingwel Xie    {
1694222347aSKingwel Xie      u8 *function_name;
1704222347aSKingwel Xie      u8 *junk_after_name;
1714222347aSKingwel Xie      function_name = format (0, "%U%c", format_clib_elf_symbol_with_address,
1724222347aSKingwel Xie			      g->edit_function, 0);
1734222347aSKingwel Xie      junk_after_name = function_name;
1744222347aSKingwel Xie      while (*junk_after_name && *junk_after_name != ' ')
1754222347aSKingwel Xie	junk_after_name++;
1764222347aSKingwel Xie      *junk_after_name = 0;
1778feeaff5SPaul Vinciguerra      s = format (s, "edit-function %s, ", function_name);
1784222347aSKingwel Xie      vec_free (function_name);
1794222347aSKingwel Xie    }
1804222347aSKingwel Xie
1814222347aSKingwel Xie  return s;
1824222347aSKingwel Xie}
1834222347aSKingwel Xie
18471e97c63SCalvinstatic u8 *
18571e97c63SCalvinformat_pg_stream (u8 * s, va_list * va)
186cb9cadadSEd Warnicke{
18771e97c63SCalvin  pg_stream_t *t = va_arg (*va, pg_stream_t *);
1884222347aSKingwel Xie  int verbose = va_arg (*va, int);
189cb9cadadSEd Warnicke
19071e97c63SCalvin  if (!t)
1914222347aSKingwel Xie    return format (s, "%-16s%=12s%=16s%s",
192cb9cadadSEd Warnicke		   "Name", "Enabled", "Count", "Parameters");
193cb9cadadSEd Warnicke
1944222347aSKingwel Xie  s = format (s, "%-16v%=12s%=16Ld",
195cb9cadadSEd Warnicke	      t->name,
196cb9cadadSEd Warnicke	      pg_stream_is_enabled (t) ? "Yes" : "No",
197cb9cadadSEd Warnicke	      t->n_packets_generated);
198cb9cadadSEd Warnicke
1994222347aSKingwel Xie  int indent = format_get_indent (s);
200cb9cadadSEd Warnicke
2014222347aSKingwel Xie  s = format (s, "limit %Ld, ", t->n_packets_limit);
2024222347aSKingwel Xie  s = format (s, "rate %.2e pps, ", t->rate_packets_per_second);
2034222347aSKingwel Xie  s = format (s, "size %d%c%d, ",
204cb9cadadSEd Warnicke	      t->min_packet_bytes,
205cb9cadadSEd Warnicke	      t->packet_size_edit_type == PG_EDIT_RANDOM ? '+' : '-',
206cb9cadadSEd Warnicke	      t->max_packet_bytes);
2074222347aSKingwel Xie  s = format (s, "buffer-size %d, ", t->buffer_bytes);
2084222347aSKingwel Xie  s = format (s, "worker %d, ", t->worker_index);
209cb9cadadSEd Warnicke
2104222347aSKingwel Xie  if (verbose)
2114222347aSKingwel Xie    {
2124222347aSKingwel Xie      pg_edit_group_t *g;
2134222347aSKingwel Xie  /* *INDENT-OFF* */
2144222347aSKingwel Xie  vec_foreach (g, t->edit_groups)
215cb9cadadSEd Warnicke    {
2164222347aSKingwel Xie      s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
2174222347aSKingwel Xie    }
2184222347aSKingwel Xie  /* *INDENT-ON* */
219cb9cadadSEd Warnicke    }
220cb9cadadSEd Warnicke
221cb9cadadSEd Warnicke  return s;
222cb9cadadSEd Warnicke}
223cb9cadadSEd Warnicke
224cb9cadadSEd Warnickestatic clib_error_t *
225cb9cadadSEd Warnickeshow_streams (vlib_main_t * vm,
22671e97c63SCalvin	      unformat_input_t * input, vlib_cli_command_t * cmd)
227cb9cadadSEd Warnicke{
22871e97c63SCalvin  pg_main_t *pg = &pg_main;
22971e97c63SCalvin  pg_stream_t *s;
2304222347aSKingwel Xie  int verbose = 0;
2314222347aSKingwel Xie
2324222347aSKingwel Xie  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2334222347aSKingwel Xie    {
2344222347aSKingwel Xie      if (unformat (input, "verbose"))
2354222347aSKingwel Xie	verbose = 1;
2364222347aSKingwel Xie      else
2374222347aSKingwel Xie	break;
2384222347aSKingwel Xie    }
239cb9cadadSEd Warnicke
240cb9cadadSEd Warnicke  if (pool_elts (pg->streams) == 0)
241cb9cadadSEd Warnicke    {
242cb9cadadSEd Warnicke      vlib_cli_output (vm, "no streams currently defined");
243cb9cadadSEd Warnicke      goto done;
244cb9cadadSEd Warnicke    }
245cb9cadadSEd Warnicke
2464222347aSKingwel Xie  vlib_cli_output (vm, "%U", format_pg_stream, 0, 0);
24771e97c63SCalvin  /* *INDENT-OFF* */
248cb9cadadSEd Warnicke  pool_foreach (s, pg->streams, ({
2494222347aSKingwel Xie      vlib_cli_output (vm, "%U", format_pg_stream, s, verbose);
250cb9cadadSEd Warnicke    }));
25171e97c63SCalvin  /* *INDENT-ON* */
252cb9cadadSEd Warnicke
254cb9cadadSEd Warnicke  return 0;
255cb9cadadSEd Warnicke}
256cb9cadadSEd Warnicke
25771e97c63SCalvin/* *INDENT-OFF* */
258cb9cadadSEd WarnickeVLIB_CLI_COMMAND (show_streams_cli, static) = {
2594222347aSKingwel Xie  .path = "show packet-generator ",
2604222347aSKingwel Xie  .short_help = "show packet-generator [verbose]",
261cb9cadadSEd Warnicke  .function = show_streams,
262cb9cadadSEd Warnicke};
26371e97c63SCalvin/* *INDENT-ON* */
264cb9cadadSEd Warnicke
265cb9cadadSEd Warnickestatic clib_error_t *
26671e97c63SCalvinpg_pcap_read (pg_stream_t * s, char *file_name)
267cb9cadadSEd Warnicke{
268cb9cadadSEd Warnicke#ifndef CLIB_UNIX
269cb9cadadSEd Warnicke  return clib_error_return (0, "no pcap support");
270cb9cadadSEd Warnicke#else
271cb9cadadSEd Warnicke  pcap_main_t pm;
27271e97c63SCalvin  clib_error_t *error;
273b7b92993SDave Barach  clib_memset (&pm, 0, sizeof (pm));
274cb9cadadSEd Warnicke  pm.file_name = file_name;
275cb9cadadSEd Warnicke  error = pcap_read (&pm);
276cb9cadadSEd Warnicke  s->replay_packet_templates = pm.packets_read;
27778c56890SDave Barach  s->replay_packet_timestamps = pm.timestamps;
278cb9cadadSEd Warnicke  s->min_packet_bytes = pm.min_packet_bytes;
279cb9cadadSEd Warnicke  s->max_packet_bytes = pm.max_packet_bytes;
280cb9cadadSEd Warnicke  s->buffer_bytes = pm.max_packet_bytes;
281ddbdd4f9SDamjan Marion
282ddbdd4f9SDamjan Marion  if (s->n_packets_limit == 0)
283ddbdd4f9SDamjan Marion    s->n_packets_limit = vec_len (pm.packets_read);
284ddbdd4f9SDamjan Marion
285cb9cadadSEd Warnicke  return error;
286cb9cadadSEd Warnicke#endif /* CLIB_UNIX */
287cb9cadadSEd Warnicke}
288cb9cadadSEd Warnicke
289cb9cadadSEd Warnickestatic uword
290cb9cadadSEd Warnickeunformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
291cb9cadadSEd Warnicke{
29271e97c63SCalvin  pg_stream_t *s = va_arg (*args, pg_stream_t *);
293cb9cadadSEd Warnicke  f64 x;
294cb9cadadSEd Warnicke
295cb9cadadSEd Warnicke  if (unformat (input, "limit %f", &x))
296cb9cadadSEd Warnicke    s->n_packets_limit = x;
297cb9cadadSEd Warnicke
298cb9cadadSEd Warnicke  else if (unformat (input, "rate %f", &x))
299cb9cadadSEd Warnicke    s->rate_packets_per_second = x;
300cb9cadadSEd Warnicke
301cb9cadadSEd Warnicke  else if (unformat (input, "size %d-%d", &s->min_packet_bytes,
302cb9cadadSEd Warnicke		     &s->max_packet_bytes))
303cb9cadadSEd Warnicke    s->packet_size_edit_type = PG_EDIT_INCREMENT;
304cb9cadadSEd Warnicke
305cb9cadadSEd Warnicke  else if (unformat (input, "size %d+%d", &s->min_packet_bytes,
306cb9cadadSEd Warnicke		     &s->max_packet_bytes))
307cb9cadadSEd Warnicke    s->packet_size_edit_type = PG_EDIT_RANDOM;
308cb9cadadSEd Warnicke
309cb9cadadSEd Warnicke  else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
310cb9cadadSEd Warnicke    ;
311cb9cadadSEd Warnicke
312cb9cadadSEd Warnicke  else
313cb9cadadSEd Warnicke    return 0;
314cb9cadadSEd Warnicke
315cb9cadadSEd Warnicke  return 1;
316cb9cadadSEd Warnicke}
317cb9cadadSEd Warnicke
318cb9cadadSEd Warnickestatic clib_error_t *
319cb9cadadSEd Warnickevalidate_stream (pg_stream_t * s)
320cb9cadadSEd Warnicke{
321cb9cadadSEd Warnicke  if (s->max_packet_bytes < s->min_packet_bytes)
322cb9cadadSEd Warnicke    return clib_error_create ("max-size < min-size");
323cb9cadadSEd Warnicke
3244222347aSKingwel Xie  u32 hdr_size = pg_edit_group_n_bytes (s, 0);
3254222347aSKingwel Xie  if (s->min_packet_bytes < hdr_size)
3264222347aSKingwel Xie    return clib_error_create ("min-size < total header size %d", hdr_size);
3274222347aSKingwel Xie  if (s->buffer_bytes == 0)
3284222347aSKingwel Xie    return clib_error_create ("buffer-size must be positive");
329cb9cadadSEd Warnicke
330cb9cadadSEd Warnicke  if (s->rate_packets_per_second < 0)
331cb9cadadSEd Warnicke    return clib_error_create ("negative rate");
332cb9cadadSEd Warnicke
333cb9cadadSEd Warnicke  return 0;
334cb9cadadSEd Warnicke}
335cb9cadadSEd Warnicke
336cb9cadadSEd Warnickestatic clib_error_t *
337cb9cadadSEd Warnickenew_stream (vlib_main_t * vm,
33871e97c63SCalvin	    unformat_input_t * input, vlib_cli_command_t * cmd)
339cb9cadadSEd Warnicke{
34071e97c63SCalvin  clib_error_t *error = 0;
34171e97c63SCalvin  u8 *tmp = 0;
34287d7bac5SChristian E. Hopps  u32 maxframe, hw_if_index;
34371e97c63SCalvin  unformat_input_t sub_input = { 0 };
344cb9cadadSEd Warnicke  int sub_input_given = 0;
34571e97c63SCalvin  vnet_main_t *vnm = vnet_get_main ();
34671e97c63SCalvin  pg_main_t *pg = &pg_main;
34771e97c63SCalvin  pg_stream_t s = { 0 };
34871e97c63SCalvin  char *pcap_file_name;
350cb9cadadSEd Warnicke  s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
351cb9cadadSEd Warnicke  s.node_index = ~0;
352cb9cadadSEd Warnicke  s.max_packet_bytes = s.min_packet_bytes = 64;
3538934a045SDamjan Marion  s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
3543d9c86e9SDamjan Marion  s.if_id = 0;
35587d7bac5SChristian E. Hopps  s.n_max_frame = VLIB_FRAME_SIZE;
356cb9cadadSEd Warnicke  pcap_file_name = 0;
35787d7bac5SChristian E. Hopps
358cb9cadadSEd Warnicke  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
359cb9cadadSEd Warnicke    {
360cb9cadadSEd Warnicke      if (unformat (input, "name %v", &tmp))
361cb9cadadSEd Warnicke	{
362cb9cadadSEd Warnicke	  if (s.name)
363cb9cadadSEd Warnicke	    vec_free (s.name);
364cb9cadadSEd Warnicke	  s.name = tmp;
365cb9cadadSEd Warnicke	}
366cb9cadadSEd Warnicke
367cb9cadadSEd Warnicke      else if (unformat (input, "node %U",
368cb9cadadSEd Warnicke			 unformat_vnet_hw_interface, vnm, &hw_if_index))
369cb9cadadSEd Warnicke	{
37071e97c63SCalvin	  vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
371cb9cadadSEd Warnicke
372cb9cadadSEd Warnicke	  s.node_index = hi->output_node_index;
373cb9cadadSEd Warnicke	  s.sw_if_index[VLIB_TX] = hi->sw_if_index;
374cb9cadadSEd Warnicke	}
375cb9cadadSEd Warnicke
37671e97c63SCalvin      else if (unformat (input, "source pg%u", &s.if_id))
3773d9c86e9SDamjan Marion	;
3783d9c86e9SDamjan Marion
37908eb2bb2SDave Barach      else if (unformat (input, "buffer-flags %U",
38008eb2bb2SDave Barach			 unformat_vnet_buffer_flags, &s.buffer_flags))
38108eb2bb2SDave Barach	;
38208eb2bb2SDave Barach
383cb9cadadSEd Warnicke      else if (unformat (input, "node %U",
384cb9cadadSEd Warnicke			 unformat_vlib_node, vm, &s.node_index))
385cb9cadadSEd Warnicke	;
38687d7bac5SChristian E. Hopps      else if (unformat (input, "maxframe %u", &maxframe))
38787d7bac5SChristian E. Hopps	s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
38864034367SDamjan Marion      else if (unformat (input, "worker %u", &s.worker_index))
38964034367SDamjan Marion	;
39064034367SDamjan Marion
391cb9cadadSEd Warnicke      else if (unformat (input, "interface %U",
39271e97c63SCalvin			 unformat_vnet_sw_interface, vnm,
39371e97c63SCalvin			 &s.sw_if_index[VLIB_RX]))
394cb9cadadSEd Warnicke	;
3957d31ab2aSDave Barach      else if (unformat (input, "tx-interface %U",
3967d31ab2aSDave Barach			 unformat_vnet_sw_interface, vnm,
3977d31ab2aSDave Barach			 &s.sw_if_index[VLIB_TX]))
3987d31ab2aSDave Barach	;
399cb9cadadSEd Warnicke
400cb9cadadSEd Warnicke      else if (unformat (input, "pcap %s", &pcap_file_name))
401cb9cadadSEd Warnicke	;
402cb9cadadSEd Warnicke
40371e97c63SCalvin      else if (!sub_input_given
404cb9cadadSEd Warnicke	       && unformat (input, "data %U", unformat_input, &sub_input))
405cb9cadadSEd Warnicke	sub_input_given++;
407cb9cadadSEd Warnicke      else if (unformat_user (input, unformat_pg_stream_parameter, &s))
408cb9cadadSEd Warnicke	;
409cb9cadadSEd Warnicke
410cb9cadadSEd Warnicke      else
411cb9cadadSEd Warnicke	{
412cb9cadadSEd Warnicke	  error = clib_error_create ("unknown input `%U'",
413cb9cadadSEd Warnicke				     format_unformat_error, input);
414cb9cadadSEd Warnicke	  goto done;
415cb9cadadSEd Warnicke	}
416cb9cadadSEd Warnicke    }
417cb9cadadSEd Warnicke
41871e97c63SCalvin  if (!sub_input_given && !pcap_file_name)
419cb9cadadSEd Warnicke    {
420cb9cadadSEd Warnicke      error = clib_error_create ("no packet data given");
421cb9cadadSEd Warnicke      goto done;
422cb9cadadSEd Warnicke    }
423cb9cadadSEd Warnicke
424cb9cadadSEd Warnicke  if (s.node_index == ~0)
425cb9cadadSEd Warnicke    {
426ddbdd4f9SDamjan Marion      if (pcap_file_name != 0)
427ddbdd4f9SDamjan Marion	{
42871e97c63SCalvin	  vlib_node_t *n =
42971e97c63SCalvin	    vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
430ddbdd4f9SDamjan Marion	  s.node_index = n->index;
431ddbdd4f9SDamjan Marion	}
432ddbdd4f9SDamjan Marion      else
433ddbdd4f9SDamjan Marion	{
434ddbdd4f9SDamjan Marion	  error = clib_error_create ("output interface or node not given");
435ddbdd4f9SDamjan Marion	  goto done;
436ddbdd4f9SDamjan Marion	}
437cb9cadadSEd Warnicke    }
438cb9cadadSEd Warnicke
439cb9cadadSEd Warnicke  {
44071e97c63SCalvin    pg_node_t *n;
441cb9cadadSEd Warnicke
442cb9cadadSEd Warnicke    if (s.node_index < vec_len (pg->nodes))
443cb9cadadSEd Warnicke      n = pg->nodes + s.node_index;
444cb9cadadSEd Warnicke    else
445cb9cadadSEd Warnicke      n = 0;
446cb9cadadSEd Warnicke
44764034367SDamjan Marion    if (s.worker_index >= vlib_num_workers ())
44864034367SDamjan Marion      s.worker_index = 0;
44964034367SDamjan Marion
450cb9cadadSEd Warnicke    if (pcap_file_name != 0)
451cb9cadadSEd Warnicke      {
452cb9cadadSEd Warnicke	error = pg_pcap_read (&s, pcap_file_name);
453cb9cadadSEd Warnicke	if (error)
454cb9cadadSEd Warnicke	  goto done;
455cb9cadadSEd Warnicke	vec_free (pcap_file_name);
456cb9cadadSEd Warnicke      }
457cb9cadadSEd Warnicke
458cb9cadadSEd Warnicke    else if (n && n->unformat_edit
45971e97c63SCalvin	     && unformat_user (&sub_input, n->unformat_edit, &s))
460cb9cadadSEd Warnicke      ;
461cb9cadadSEd Warnicke
46271e97c63SCalvin    else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
463cb9cadadSEd Warnicke      {
464cb9cadadSEd Warnicke	error = clib_error_create
465cb9cadadSEd Warnicke	  ("failed to parse packet data from `%U'",
466cb9cadadSEd Warnicke	   format_unformat_error, &sub_input);
467cb9cadadSEd Warnicke	goto done;
468cb9cadadSEd Warnicke      }
469cb9cadadSEd Warnicke  }
470cb9cadadSEd Warnicke
4714222347aSKingwel Xie  error = validate_stream (&s);
4724222347aSKingwel Xie  if (error)
4734222347aSKingwel Xie    return error;
4744222347aSKingwel Xie
475cb9cadadSEd Warnicke  pg_stream_add (pg, &s);
476cb9cadadSEd Warnicke  return 0;
477cb9cadadSEd Warnicke
479cb9cadadSEd Warnicke  pg_stream_free (&s);
480cb9cadadSEd Warnicke  unformat_free (&sub_input);
481cb9cadadSEd Warnicke  return error;
482cb9cadadSEd Warnicke}
483cb9cadadSEd Warnicke
48471e97c63SCalvin/* *INDENT-OFF* */
485cb9cadadSEd WarnickeVLIB_CLI_COMMAND (new_stream_cli, static) = {
486cb9cadadSEd Warnicke  .path = "packet-generator new",
487cb9cadadSEd Warnicke  .function = new_stream,
488cb9cadadSEd Warnicke  .short_help = "Create packet generator stream",
489cb9cadadSEd Warnicke  .long_help =
490cb9cadadSEd Warnicke  "Create packet generator stream\n"
491cb9cadadSEd Warnicke  "\n"
492cb9cadadSEd Warnicke  "Arguments:\n"
493cb9cadadSEd Warnicke  "\n"
494cb9cadadSEd Warnicke  "name STRING          sets stream name\n"
495cb9cadadSEd Warnicke  "interface STRING     interface for stream output \n"
496cb9cadadSEd Warnicke  "node NODE-NAME       node for stream output\n"
497ddbdd4f9SDamjan Marion  "data STRING          specifies packet data\n"
49887d7bac5SChristian E. Hopps  "pcap FILENAME        read packet data from pcap file\n"
49987d7bac5SChristian E. Hopps  "rate PPS             rate to transfer packet data\n"
50087d7bac5SChristian E. Hopps  "maxframe NPKTS       maximum number of packets per frame\n",
501cb9cadadSEd Warnicke};
50271e97c63SCalvin/* *INDENT-ON* */
503cb9cadadSEd Warnicke
504cb9cadadSEd Warnickestatic clib_error_t *
505cb9cadadSEd Warnickedel_stream (vlib_main_t * vm,
50671e97c63SCalvin	    unformat_input_t * input, vlib_cli_command_t * cmd)
507cb9cadadSEd Warnicke{
50871e97c63SCalvin  pg_main_t *pg = &pg_main;
509cb9cadadSEd Warnicke  u32 i;
51171e97c63SCalvin  if (!unformat (input, "%U",
51271e97c63SCalvin		 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
513cb9cadadSEd Warnicke    return clib_error_create ("expected stream name `%U'",
514cb9cadadSEd Warnicke			      format_unformat_error, input);
515cb9cadadSEd Warnicke
516cb9cadadSEd Warnicke  pg_stream_del (pg, i);
517cb9cadadSEd Warnicke  return 0;
518cb9cadadSEd Warnicke}
519cb9cadadSEd Warnicke
52071e97c63SCalvin/* *INDENT-OFF* */
521cb9cadadSEd WarnickeVLIB_CLI_COMMAND (del_stream_cli, static) = {
522cb9cadadSEd Warnicke  .path = "packet-generator delete",
523cb9cadadSEd Warnicke  .function = del_stream,
524cb9cadadSEd Warnicke  .short_help = "Delete stream with given name",
525cb9cadadSEd Warnicke};
52671e97c63SCalvin/* *INDENT-ON* */
527cb9cadadSEd Warnicke
528cb9cadadSEd Warnickestatic clib_error_t *
529cb9cadadSEd Warnickechange_stream_parameters (vlib_main_t * vm,
53071e97c63SCalvin			  unformat_input_t * input, vlib_cli_command_t * cmd)
531cb9cadadSEd Warnicke{
53271e97c63SCalvin  pg_main_t *pg = &pg_main;
53371e97c63SCalvin  pg_stream_t *s, s_new;
534cb9cadadSEd Warnicke  u32 stream_index = ~0;
53571e97c63SCalvin  clib_error_t *error;
536cb9cadadSEd Warnicke
537cb9cadadSEd Warnicke  if (unformat (input, "%U", unformat_hash_vec_string,
538cb9cadadSEd Warnicke		pg->stream_index_by_name, &stream_index))
539cb9cadadSEd Warnicke    ;
540cb9cadadSEd Warnicke  else
541cb9cadadSEd Warnicke    return clib_error_create ("expecting stream name; got `%U'",
542cb9cadadSEd Warnicke			      format_unformat_error, input);
543cb9cadadSEd Warnicke
544cb9cadadSEd Warnicke  s = pool_elt_at_index (pg->streams, stream_index);
545cb9cadadSEd Warnicke  s_new = s[0];
546cb9cadadSEd Warnicke
547cb9cadadSEd Warnicke  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
548cb9cadadSEd Warnicke    {
549cb9cadadSEd Warnicke      if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
550cb9cadadSEd Warnicke	;
551cb9cadadSEd Warnicke
552cb9cadadSEd Warnicke      else
553cb9cadadSEd Warnicke	return clib_error_create ("unknown input `%U'",
554cb9cadadSEd Warnicke				  format_unformat_error, input);
555cb9cadadSEd Warnicke    }
556cb9cadadSEd Warnicke
557cb9cadadSEd Warnicke  error = validate_stream (&s_new);
55871e97c63SCalvin  if (!error)
5594222347aSKingwel Xie    {
5604222347aSKingwel Xie      s[0] = s_new;
5614222347aSKingwel Xie      pg_stream_change (pg, s);
5624222347aSKingwel Xie    }
563cb9cadadSEd Warnicke
564cb9cadadSEd Warnicke  return error;
565cb9cadadSEd Warnicke}
566cb9cadadSEd Warnicke
56771e97c63SCalvin/* *INDENT-OFF* */
568cb9cadadSEd WarnickeVLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
569cb9cadadSEd Warnicke  .path = "packet-generator configure",
570cb9cadadSEd Warnicke  .short_help = "Change packet generator stream parameters",
571cb9cadadSEd Warnicke  .function = change_stream_parameters,
572cb9cadadSEd Warnicke};
57371e97c63SCalvin/* *INDENT-ON* */
574cb9cadadSEd Warnicke
5753d9c86e9SDamjan Marionstatic clib_error_t *
5763d9c86e9SDamjan Marionpg_capture_cmd_fn (vlib_main_t * vm,
57771e97c63SCalvin		   unformat_input_t * input, vlib_cli_command_t * cmd)
5783d9c86e9SDamjan Marion{
57971e97c63SCalvin  clib_error_t *error = 0;
58071e97c63SCalvin  vnet_main_t *vnm = vnet_get_main ();
58171e97c63SCalvin  unformat_input_t _line_input, *line_input = &_line_input;
58271e97c63SCalvin  vnet_hw_interface_t *hi = 0;
58371e97c63SCalvin  u8 *pcap_file_name = 0;
5843d9c86e9SDamjan Marion  u32 hw_if_index;
58592217f3cSDamjan Marion  u32 is_disable = 0;
5863d9c86e9SDamjan Marion  u32 count = ~0;
5873d9c86e9SDamjan Marion
58871e97c63SCalvin  if (!unformat_user (input, unformat_line_input, line_input))
5893d9c86e9SDamjan Marion    return 0;
5903d9c86e9SDamjan Marion
5913d9c86e9SDamjan Marion  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
5923d9c86e9SDamjan Marion    {
5933d9c86e9SDamjan Marion      if (unformat (line_input, "%U",
59471e97c63SCalvin		    unformat_vnet_hw_interface, vnm, &hw_if_index))
5953d9c86e9SDamjan Marion	{
5963d9c86e9SDamjan Marion	  hi = vnet_get_hw_interface (vnm, hw_if_index);
5973d9c86e9SDamjan Marion	}
5983d9c86e9SDamjan Marion
5993d9c86e9SDamjan Marion      else if (unformat (line_input, "pcap %s", &pcap_file_name))
6003d9c86e9SDamjan Marion	;
6013d9c86e9SDamjan Marion      else if (unformat (line_input, "count %u", &count))
6023d9c86e9SDamjan Marion	;
60392217f3cSDamjan Marion      else if (unformat (line_input, "disable"))
60492217f3cSDamjan Marion	is_disable = 1;
6053d9c86e9SDamjan Marion
6063d9c86e9SDamjan Marion      else
6073d9c86e9SDamjan Marion	{
6083d9c86e9SDamjan Marion	  error = clib_error_create ("unknown input `%U'",
609a9a20e7fSBilly McFall				     format_unformat_error, line_input);
610a9a20e7fSBilly McFall	  goto done;
6113d9c86e9SDamjan Marion	}
6123d9c86e9SDamjan Marion    }
6133d9c86e9SDamjan Marion
6143d9c86e9SDamjan Marion  if (!hi)
615a9a20e7fSBilly McFall    {
616a9a20e7fSBilly McFall      error = clib_error_return (0, "Please specify interface name");
617a9a20e7fSBilly McFall      goto done;
618a9a20e7fSBilly McFall    }
6193d9c86e9SDamjan Marion
6203d9c86e9SDamjan Marion  if (hi->dev_class_index != pg_dev_class.index)
621a9a20e7fSBilly McFall    {
622a9a20e7fSBilly McFall      error =
623a9a20e7fSBilly McFall	clib_error_return (0, "Please specify packet-generator interface");
624a9a20e7fSBilly McFall      goto done;
625a9a20e7fSBilly McFall    }
6263d9c86e9SDamjan Marion
62792217f3cSDamjan Marion  if (!pcap_file_name && is_disable == 0)
628a9a20e7fSBilly McFall    {
629a9a20e7fSBilly McFall      error = clib_error_return (0, "Please specify pcap file name");
630a9a20e7fSBilly McFall      goto done;
631a9a20e7fSBilly McFall    }
6323d9c86e9SDamjan Marion
6333d9c86e9SDamjan Marion
63471e97c63SCalvin  pg_capture_args_t _a, *a = &_a;
63592217f3cSDamjan Marion
6369e6ed6e2SPavel Kotucek  a->hw_if_index = hw_if_index;
6379e6ed6e2SPavel Kotucek  a->dev_instance = hi->dev_instance;
6389e6ed6e2SPavel Kotucek  a->is_enabled = !is_disable;
639db86329aSJakub Grajciar  a->pcap_file_name = (char *) pcap_file_name;
6409e6ed6e2SPavel Kotucek  a->count = count;
6413d9c86e9SDamjan Marion
6429e6ed6e2SPavel Kotucek  error = pg_capture (a);
643a9a20e7fSBilly McFall
644a9a20e7fSBilly McFalldone:
645a9a20e7fSBilly McFall  unformat_free (line_input);
646a9a20e7fSBilly McFall
6479e6ed6e2SPavel Kotucek  return error;
6483d9c86e9SDamjan Marion}
6493d9c86e9SDamjan Marion
65071e97c63SCalvin/* *INDENT-OFF* */
6513d9c86e9SDamjan MarionVLIB_CLI_COMMAND (pg_capture_cmd, static) = {
6523d9c86e9SDamjan Marion  .path = "packet-generator capture",
6533d9c86e9SDamjan Marion  .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
6543d9c86e9SDamjan Marion  .function = pg_capture_cmd_fn,
6553d9c86e9SDamjan Marion};
65671e97c63SCalvin/* *INDENT-ON* */
6573d9c86e9SDamjan Marion
6583d9c86e9SDamjan Marionstatic clib_error_t *
6593d9c86e9SDamjan Marioncreate_pg_if_cmd_fn (vlib_main_t * vm,
66071e97c63SCalvin		     unformat_input_t * input, vlib_cli_command_t * cmd)
6613d9c86e9SDamjan Marion{
66271e97c63SCalvin  pg_main_t *pg = &pg_main;
66371e97c63SCalvin  unformat_input_t _line_input, *line_input = &_line_input;
66422e9cfd7SMohsin Kazmi  u32 if_id, gso_enabled = 0, gso_size = 0;
665a9a20e7fSBilly McFall  clib_error_t *error = NULL;
6663d9c86e9SDamjan Marion
66771e97c63SCalvin  if (!unformat_user (input, unformat_line_input, line_input))
6683d9c86e9SDamjan Marion    return 0;
6693d9c86e9SDamjan Marion
6703d9c86e9SDamjan Marion  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
6713d9c86e9SDamjan Marion    {
6723d9c86e9SDamjan Marion      if (unformat (line_input, "interface pg%u", &if_id))
6733d9c86e9SDamjan Marion	;
67422e9cfd7SMohsin Kazmi      else if (unformat (line_input, "gso-enabled"))
67522e9cfd7SMohsin Kazmi	{
67622e9cfd7SMohsin Kazmi	  gso_enabled = 1;
67722e9cfd7SMohsin Kazmi	  if (unformat (line_input, "gso-size %u", &gso_size))
67822e9cfd7SMohsin Kazmi	    ;
67922e9cfd7SMohsin Kazmi	  else
68022e9cfd7SMohsin Kazmi	    {
68122e9cfd7SMohsin Kazmi	      error = clib_error_create ("gso enabled but gso size missing");
68222e9cfd7SMohsin Kazmi	      goto done;
68322e9cfd7SMohsin Kazmi	    }
68422e9cfd7SMohsin Kazmi	}
6853d9c86e9SDamjan Marion      else
686a9a20e7fSBilly McFall	{
687a9a20e7fSBilly McFall	  error = clib_error_create ("unknown input `%U'",
688a9a20e7fSBilly McFall				     format_unformat_error, line_input);
689a9a20e7fSBilly McFall	  goto done;
690a9a20e7fSBilly McFall	}
6913d9c86e9SDamjan Marion    }
6923d9c86e9SDamjan Marion
69322e9cfd7SMohsin Kazmi  pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size);
694a9a20e7fSBilly McFall
695a9a20e7fSBilly McFalldone:
6963d9c86e9SDamjan Marion  unformat_free (line_input);
6973d9c86e9SDamjan Marion
698a9a20e7fSBilly McFall  return error;
6993d9c86e9SDamjan Marion}
7003d9c86e9SDamjan Marion
70171e97c63SCalvin/* *INDENT-OFF* */
7023d9c86e9SDamjan MarionVLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
7033d9c86e9SDamjan Marion  .path = "create packet-generator",
70422e9cfd7SMohsin Kazmi  .short_help = "create packet-generator interface <interface name> [gso-enabled gso-size <size>]",
7053d9c86e9SDamjan Marion  .function = create_pg_if_cmd_fn,
7063d9c86e9SDamjan Marion};
70771e97c63SCalvin/* *INDENT-ON* */
7083d9c86e9SDamjan Marion
709cb9cadadSEd Warnicke/* Dummy init function so that we can be linked in. */
71071e97c63SCalvinstatic clib_error_t *
71171e97c63SCalvinpg_cli_init (vlib_main_t * vm)
71371e97c63SCalvin  return 0;
715cb9cadadSEd Warnicke
716cb9cadadSEd WarnickeVLIB_INIT_FUNCTION (pg_cli_init);
71971e97c63SCalvin * fd.io coding-style-patch-verification: ON
72071e97c63SCalvin *
72171e97c63SCalvin * Local Variables:
72271e97c63SCalvin * eval: (c-set-style "gnu")
72371e97c63SCalvin * End:
72471e97c63SCalvin */