2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 * pg_cli.c: packet generator cli
18 * Copyright (c) 2008 Eliot Dresselhaus
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 #include <vnet/vnet.h>
43 #include <vnet/pg/pg.h>
46 #include <vppinfra/pcap.h>
49 /* Root of all packet generator cli commands. */
51 VLIB_CLI_COMMAND (vlib_cli_pg_command, static) = {
52 .path = "packet-generator",
53 .short_help = "Packet generator commands",
58 pg_enable_disable (u32 stream_index, int is_enable)
60 pg_main_t *pg = &pg_main;
63 if (stream_index == ~0)
65 /* No stream specified: enable/disable all streams. */
67 pool_foreach (s, pg->streams, ({
68 pg_stream_enable_disable (pg, s, is_enable);
74 /* enable/disable specified stream. */
75 s = pool_elt_at_index (pg->streams, stream_index);
76 pg_stream_enable_disable (pg, s, is_enable);
81 pg_capture (pg_capture_args_t * a)
83 pg_main_t *pg = &pg_main;
86 if (a->is_enabled == 1)
89 if (stat (a->pcap_file_name, &sb) != -1)
90 return clib_error_return (0, "pcap file '%s' already exists.",
94 pi = pool_elt_at_index (pg->interfaces, a->dev_instance);
95 vec_free (pi->pcap_file_name);
96 if ((pi->pcap_main.flags & PCAP_MAIN_INIT_DONE))
97 pcap_close (&pi->pcap_main);
98 clib_memset (&pi->pcap_main, 0, sizeof (pi->pcap_main));
99 pi->pcap_main.file_descriptor = -1;
101 if (a->is_enabled == 0)
104 pi->pcap_file_name = a->pcap_file_name;
105 pi->pcap_main.file_name = (char *) pi->pcap_file_name;
106 pi->pcap_main.n_packets_to_capture = a->count;
107 pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
112 static clib_error_t *
113 enable_disable_stream (vlib_main_t * vm,
114 unformat_input_t * input, vlib_cli_command_t * cmd)
116 unformat_input_t _line_input, *line_input = &_line_input;
117 pg_main_t *pg = &pg_main;
118 int is_enable = cmd->function_arg != 0;
119 u32 stream_index = ~0;
121 if (!unformat_user (input, unformat_line_input, line_input))
124 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
126 if (unformat (line_input, "%U", unformat_hash_vec_string,
127 pg->stream_index_by_name, &stream_index))
130 return clib_error_create ("unknown input `%U'",
131 format_unformat_error, line_input);
133 unformat_free (line_input);
136 pg_enable_disable (stream_index, is_enable);
142 VLIB_CLI_COMMAND (enable_streams_cli, static) = {
143 .path = "packet-generator enable-stream",
144 .short_help = "Enable packet generator streams",
145 .function = enable_disable_stream,
146 .function_arg = 1, /* is_enable */
151 VLIB_CLI_COMMAND (disable_streams_cli, static) = {
152 .path = "packet-generator disable-stream",
153 .short_help = "Disable packet generator streams",
154 .function = enable_disable_stream,
155 .function_arg = 0, /* is_enable */
160 format_pg_edit_group (u8 * s, va_list * va)
162 pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
165 format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
166 g->start_byte_offset);
167 if (g->edit_function)
171 function_name = format (0, "%U%c", format_clib_elf_symbol_with_address,
172 g->edit_function, 0);
173 junk_after_name = function_name;
174 while (*junk_after_name && *junk_after_name != ' ')
176 *junk_after_name = 0;
177 s = format (s, "edit-function %s, ", function_name);
178 vec_free (function_name);
185 format_pg_stream (u8 * s, va_list * va)
187 pg_stream_t *t = va_arg (*va, pg_stream_t *);
188 int verbose = va_arg (*va, int);
191 return format (s, "%-16s%=12s%=16s%s",
192 "Name", "Enabled", "Count", "Parameters");
194 s = format (s, "%-16v%=12s%=16Ld",
196 pg_stream_is_enabled (t) ? "Yes" : "No",
197 t->n_packets_generated);
199 int indent = format_get_indent (s);
201 s = format (s, "limit %Ld, ", t->n_packets_limit);
202 s = format (s, "rate %.2e pps, ", t->rate_packets_per_second);
203 s = format (s, "size %d%c%d, ",
205 t->packet_size_edit_type == PG_EDIT_RANDOM ? '+' : '-',
206 t->max_packet_bytes);
207 s = format (s, "buffer-size %d, ", t->buffer_bytes);
208 s = format (s, "worker %d, ", t->worker_index);
214 vec_foreach (g, t->edit_groups)
216 s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
224 static clib_error_t *
225 show_streams (vlib_main_t * vm,
226 unformat_input_t * input, vlib_cli_command_t * cmd)
228 pg_main_t *pg = &pg_main;
232 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
234 if (unformat (input, "verbose"))
240 if (pool_elts (pg->streams) == 0)
242 vlib_cli_output (vm, "no streams currently defined");
246 vlib_cli_output (vm, "%U", format_pg_stream, 0, 0);
248 pool_foreach (s, pg->streams, ({
249 vlib_cli_output (vm, "%U", format_pg_stream, s, verbose);
258 VLIB_CLI_COMMAND (show_streams_cli, static) = {
259 .path = "show packet-generator ",
260 .short_help = "show packet-generator [verbose]",
261 .function = show_streams,
265 static clib_error_t *
266 pg_pcap_read (pg_stream_t * s, char *file_name)
269 return clib_error_return (0, "no pcap support");
273 clib_memset (&pm, 0, sizeof (pm));
274 pm.file_name = file_name;
275 error = pcap_read (&pm);
276 s->replay_packet_templates = pm.packets_read;
277 s->replay_packet_timestamps = pm.timestamps;
278 s->min_packet_bytes = pm.min_packet_bytes;
279 s->max_packet_bytes = pm.max_packet_bytes;
280 s->buffer_bytes = pm.max_packet_bytes;
282 if (s->n_packets_limit == 0)
283 s->n_packets_limit = vec_len (pm.packets_read);
286 #endif /* CLIB_UNIX */
290 unformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
292 pg_stream_t *s = va_arg (*args, pg_stream_t *);
295 if (unformat (input, "limit %f", &x))
296 s->n_packets_limit = x;
298 else if (unformat (input, "rate %f", &x))
299 s->rate_packets_per_second = x;
301 else if (unformat (input, "size %d-%d", &s->min_packet_bytes,
302 &s->max_packet_bytes))
303 s->packet_size_edit_type = PG_EDIT_INCREMENT;
305 else if (unformat (input, "size %d+%d", &s->min_packet_bytes,
306 &s->max_packet_bytes))
307 s->packet_size_edit_type = PG_EDIT_RANDOM;
309 else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
318 static clib_error_t *
319 validate_stream (pg_stream_t * s)
321 if (s->max_packet_bytes < s->min_packet_bytes)
322 return clib_error_create ("max-size < min-size");
324 u32 hdr_size = pg_edit_group_n_bytes (s, 0);
325 if (s->min_packet_bytes < hdr_size)
326 return clib_error_create ("min-size < total header size %d", hdr_size);
327 if (s->buffer_bytes == 0)
328 return clib_error_create ("buffer-size must be positive");
330 if (s->rate_packets_per_second < 0)
331 return clib_error_create ("negative rate");
336 static clib_error_t *
337 new_stream (vlib_main_t * vm,
338 unformat_input_t * input, vlib_cli_command_t * cmd)
340 clib_error_t *error = 0;
342 u32 maxframe, hw_if_index;
343 unformat_input_t sub_input = { 0 };
344 int sub_input_given = 0;
345 vnet_main_t *vnm = vnet_get_main ();
346 pg_main_t *pg = &pg_main;
347 pg_stream_t s = { 0 };
348 char *pcap_file_name;
350 s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
352 s.max_packet_bytes = s.min_packet_bytes = 64;
353 s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
355 s.n_max_frame = VLIB_FRAME_SIZE;
358 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
360 if (unformat (input, "name %v", &tmp))
367 else if (unformat (input, "node %U",
368 unformat_vnet_hw_interface, vnm, &hw_if_index))
370 vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
372 s.node_index = hi->output_node_index;
373 s.sw_if_index[VLIB_TX] = hi->sw_if_index;
376 else if (unformat (input, "source pg%u", &s.if_id))
379 else if (unformat (input, "buffer-flags %U",
380 unformat_vnet_buffer_flags, &s.buffer_flags))
383 else if (unformat (input, "node %U",
384 unformat_vlib_node, vm, &s.node_index))
386 else if (unformat (input, "maxframe %u", &maxframe))
387 s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
388 else if (unformat (input, "worker %u", &s.worker_index))
391 else if (unformat (input, "interface %U",
392 unformat_vnet_sw_interface, vnm,
393 &s.sw_if_index[VLIB_RX]))
395 else if (unformat (input, "tx-interface %U",
396 unformat_vnet_sw_interface, vnm,
397 &s.sw_if_index[VLIB_TX]))
400 else if (unformat (input, "pcap %s", &pcap_file_name))
403 else if (!sub_input_given
404 && unformat (input, "data %U", unformat_input, &sub_input))
407 else if (unformat_user (input, unformat_pg_stream_parameter, &s))
412 error = clib_error_create ("unknown input `%U'",
413 format_unformat_error, input);
418 if (!sub_input_given && !pcap_file_name)
420 error = clib_error_create ("no packet data given");
424 if (s.node_index == ~0)
426 if (pcap_file_name != 0)
429 vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
430 s.node_index = n->index;
434 error = clib_error_create ("output interface or node not given");
442 if (s.node_index < vec_len (pg->nodes))
443 n = pg->nodes + s.node_index;
447 if (s.worker_index >= vlib_num_workers ())
450 if (pcap_file_name != 0)
452 error = pg_pcap_read (&s, pcap_file_name);
455 vec_free (pcap_file_name);
458 else if (n && n->unformat_edit
459 && unformat_user (&sub_input, n->unformat_edit, &s))
462 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
464 error = clib_error_create
465 ("failed to parse packet data from `%U'",
466 format_unformat_error, &sub_input);
471 error = validate_stream (&s);
475 pg_stream_add (pg, &s);
480 unformat_free (&sub_input);
485 VLIB_CLI_COMMAND (new_stream_cli, static) = {
486 .path = "packet-generator new",
487 .function = new_stream,
488 .short_help = "Create packet generator stream",
490 "Create packet generator stream\n"
494 "name STRING sets stream name\n"
495 "interface STRING interface for stream output \n"
496 "node NODE-NAME node for stream output\n"
497 "data STRING specifies packet data\n"
498 "pcap FILENAME read packet data from pcap file\n"
499 "rate PPS rate to transfer packet data\n"
500 "maxframe NPKTS maximum number of packets per frame\n",
504 static clib_error_t *
505 del_stream (vlib_main_t * vm,
506 unformat_input_t * input, vlib_cli_command_t * cmd)
508 pg_main_t *pg = &pg_main;
511 if (!unformat (input, "%U",
512 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
513 return clib_error_create ("expected stream name `%U'",
514 format_unformat_error, input);
516 pg_stream_del (pg, i);
521 VLIB_CLI_COMMAND (del_stream_cli, static) = {
522 .path = "packet-generator delete",
523 .function = del_stream,
524 .short_help = "Delete stream with given name",
528 static clib_error_t *
529 change_stream_parameters (vlib_main_t * vm,
530 unformat_input_t * input, vlib_cli_command_t * cmd)
532 pg_main_t *pg = &pg_main;
533 pg_stream_t *s, s_new;
534 u32 stream_index = ~0;
537 if (unformat (input, "%U", unformat_hash_vec_string,
538 pg->stream_index_by_name, &stream_index))
541 return clib_error_create ("expecting stream name; got `%U'",
542 format_unformat_error, input);
544 s = pool_elt_at_index (pg->streams, stream_index);
547 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
549 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
553 return clib_error_create ("unknown input `%U'",
554 format_unformat_error, input);
557 error = validate_stream (&s_new);
561 pg_stream_change (pg, s);
568 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
569 .path = "packet-generator configure",
570 .short_help = "Change packet generator stream parameters",
571 .function = change_stream_parameters,
575 static clib_error_t *
576 pg_capture_cmd_fn (vlib_main_t * vm,
577 unformat_input_t * input, vlib_cli_command_t * cmd)
579 clib_error_t *error = 0;
580 vnet_main_t *vnm = vnet_get_main ();
581 unformat_input_t _line_input, *line_input = &_line_input;
582 vnet_hw_interface_t *hi = 0;
583 u8 *pcap_file_name = 0;
588 if (!unformat_user (input, unformat_line_input, line_input))
591 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
593 if (unformat (line_input, "%U",
594 unformat_vnet_hw_interface, vnm, &hw_if_index))
596 hi = vnet_get_hw_interface (vnm, hw_if_index);
599 else if (unformat (line_input, "pcap %s", &pcap_file_name))
601 else if (unformat (line_input, "count %u", &count))
603 else if (unformat (line_input, "disable"))
608 error = clib_error_create ("unknown input `%U'",
609 format_unformat_error, line_input);
616 error = clib_error_return (0, "Please specify interface name");
620 if (hi->dev_class_index != pg_dev_class.index)
623 clib_error_return (0, "Please specify packet-generator interface");
627 if (!pcap_file_name && is_disable == 0)
629 error = clib_error_return (0, "Please specify pcap file name");
634 pg_capture_args_t _a, *a = &_a;
636 a->hw_if_index = hw_if_index;
637 a->dev_instance = hi->dev_instance;
638 a->is_enabled = !is_disable;
639 a->pcap_file_name = (char *) pcap_file_name;
642 error = pg_capture (a);
645 unformat_free (line_input);
651 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
652 .path = "packet-generator capture",
653 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
654 .function = pg_capture_cmd_fn,
658 static clib_error_t *
659 create_pg_if_cmd_fn (vlib_main_t * vm,
660 unformat_input_t * input, vlib_cli_command_t * cmd)
662 pg_main_t *pg = &pg_main;
663 unformat_input_t _line_input, *line_input = &_line_input;
664 u32 if_id, gso_enabled = 0, gso_size = 0;
665 clib_error_t *error = NULL;
667 if (!unformat_user (input, unformat_line_input, line_input))
670 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
672 if (unformat (line_input, "interface pg%u", &if_id))
674 else if (unformat (line_input, "gso-enabled"))
677 if (unformat (line_input, "gso-size %u", &gso_size))
681 error = clib_error_create ("gso enabled but gso size missing");
687 error = clib_error_create ("unknown input `%U'",
688 format_unformat_error, line_input);
693 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size);
696 unformat_free (line_input);
702 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
703 .path = "create packet-generator",
704 .short_help = "create packet-generator interface <interface name> [gso-enabled gso-size <size>]",
705 .function = create_pg_if_cmd_fn,
709 /* Dummy init function so that we can be linked in. */
710 static clib_error_t *
711 pg_cli_init (vlib_main_t * vm)
716 VLIB_INIT_FUNCTION (pg_cli_init);
719 * fd.io coding-style-patch-verification: ON
722 * eval: (c-set-style "gnu")