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))
382 else if (unformat (input, "buffer-offload-flags %U",
383 unformat_vnet_buffer_offload_flags, &s.buffer_oflags))
385 else if (unformat (input, "node %U",
386 unformat_vlib_node, vm, &s.node_index))
388 else if (unformat (input, "maxframe %u", &maxframe))
389 s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
390 else if (unformat (input, "worker %u", &s.worker_index))
393 else if (unformat (input, "interface %U",
394 unformat_vnet_sw_interface, vnm,
395 &s.sw_if_index[VLIB_RX]))
397 else if (unformat (input, "tx-interface %U",
398 unformat_vnet_sw_interface, vnm,
399 &s.sw_if_index[VLIB_TX]))
402 else if (unformat (input, "pcap %s", &pcap_file_name))
405 else if (!sub_input_given
406 && unformat (input, "data %U", unformat_input, &sub_input))
409 else if (unformat_user (input, unformat_pg_stream_parameter, &s))
414 error = clib_error_create ("unknown input `%U'",
415 format_unformat_error, input);
420 if (!sub_input_given && !pcap_file_name)
422 error = clib_error_create ("no packet data given");
426 if (s.node_index == ~0)
428 if (pcap_file_name != 0)
431 vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
432 s.node_index = n->index;
436 error = clib_error_create ("output interface or node not given");
444 if (s.node_index < vec_len (pg->nodes))
445 n = pg->nodes + s.node_index;
449 if (s.worker_index >= vlib_num_workers ())
452 if (pcap_file_name != 0)
454 error = pg_pcap_read (&s, pcap_file_name);
457 vec_free (pcap_file_name);
460 else if (n && n->unformat_edit
461 && unformat_user (&sub_input, n->unformat_edit, &s))
464 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
466 error = clib_error_create
467 ("failed to parse packet data from `%U'",
468 format_unformat_error, &sub_input);
473 error = validate_stream (&s);
477 pg_stream_add (pg, &s);
482 unformat_free (&sub_input);
487 VLIB_CLI_COMMAND (new_stream_cli, static) = {
488 .path = "packet-generator new",
489 .function = new_stream,
490 .short_help = "Create packet generator stream",
492 "Create packet generator stream\n"
496 "name STRING sets stream name\n"
497 "interface STRING interface for stream output \n"
498 "node NODE-NAME node for stream output\n"
499 "data STRING specifies packet data\n"
500 "pcap FILENAME read packet data from pcap file\n"
501 "rate PPS rate to transfer packet data\n"
502 "maxframe NPKTS maximum number of packets per frame\n",
506 static clib_error_t *
507 del_stream (vlib_main_t * vm,
508 unformat_input_t * input, vlib_cli_command_t * cmd)
510 pg_main_t *pg = &pg_main;
513 if (!unformat (input, "%U",
514 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
515 return clib_error_create ("expected stream name `%U'",
516 format_unformat_error, input);
518 pg_stream_del (pg, i);
523 VLIB_CLI_COMMAND (del_stream_cli, static) = {
524 .path = "packet-generator delete",
525 .function = del_stream,
526 .short_help = "Delete stream with given name",
530 static clib_error_t *
531 change_stream_parameters (vlib_main_t * vm,
532 unformat_input_t * input, vlib_cli_command_t * cmd)
534 pg_main_t *pg = &pg_main;
535 pg_stream_t *s, s_new;
536 u32 stream_index = ~0;
539 if (unformat (input, "%U", unformat_hash_vec_string,
540 pg->stream_index_by_name, &stream_index))
543 return clib_error_create ("expecting stream name; got `%U'",
544 format_unformat_error, input);
546 s = pool_elt_at_index (pg->streams, stream_index);
549 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
551 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
555 return clib_error_create ("unknown input `%U'",
556 format_unformat_error, input);
559 error = validate_stream (&s_new);
563 pg_stream_change (pg, s);
570 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
571 .path = "packet-generator configure",
572 .short_help = "Change packet generator stream parameters",
573 .function = change_stream_parameters,
577 static clib_error_t *
578 pg_capture_cmd_fn (vlib_main_t * vm,
579 unformat_input_t * input, vlib_cli_command_t * cmd)
581 clib_error_t *error = 0;
582 vnet_main_t *vnm = vnet_get_main ();
583 unformat_input_t _line_input, *line_input = &_line_input;
584 vnet_hw_interface_t *hi = 0;
585 u8 *pcap_file_name = 0;
590 if (!unformat_user (input, unformat_line_input, line_input))
593 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
595 if (unformat (line_input, "%U",
596 unformat_vnet_hw_interface, vnm, &hw_if_index))
598 hi = vnet_get_hw_interface (vnm, hw_if_index);
601 else if (unformat (line_input, "pcap %s", &pcap_file_name))
603 else if (unformat (line_input, "count %u", &count))
605 else if (unformat (line_input, "disable"))
610 error = clib_error_create ("unknown input `%U'",
611 format_unformat_error, line_input);
618 error = clib_error_return (0, "Please specify interface name");
622 if (hi->dev_class_index != pg_dev_class.index)
625 clib_error_return (0, "Please specify packet-generator interface");
629 if (!pcap_file_name && is_disable == 0)
631 error = clib_error_return (0, "Please specify pcap file name");
636 pg_capture_args_t _a, *a = &_a;
638 a->hw_if_index = hw_if_index;
639 a->dev_instance = hi->dev_instance;
640 a->is_enabled = !is_disable;
641 a->pcap_file_name = (char *) pcap_file_name;
644 error = pg_capture (a);
647 unformat_free (line_input);
653 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
654 .path = "packet-generator capture",
655 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
656 .function = pg_capture_cmd_fn,
660 static clib_error_t *
661 create_pg_if_cmd_fn (vlib_main_t * vm,
662 unformat_input_t * input, vlib_cli_command_t * cmd)
664 pg_main_t *pg = &pg_main;
665 unformat_input_t _line_input, *line_input = &_line_input;
666 u32 if_id, gso_enabled = 0, gso_size = 0, coalesce_enabled = 0;
667 clib_error_t *error = NULL;
669 if (!unformat_user (input, unformat_line_input, line_input))
672 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
674 if (unformat (line_input, "interface pg%u", &if_id))
676 else if (unformat (line_input, "gso-enabled"))
679 if (unformat (line_input, "gso-size %u", &gso_size))
683 error = clib_error_create ("gso enabled but gso size missing");
686 if (unformat (line_input, "coalesce-enabled"))
687 coalesce_enabled = 1;
691 error = clib_error_create ("unknown input `%U'",
692 format_unformat_error, line_input);
697 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size,
701 unformat_free (line_input);
707 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
708 .path = "create packet-generator",
709 .short_help = "create packet-generator interface <interface name>"
710 " [gso-enabled gso-size <size> [coalesce-enabled]]",
711 .function = create_pg_if_cmd_fn,
715 /* Dummy init function so that we can be linked in. */
716 static clib_error_t *
717 pg_cli_init (vlib_main_t * vm)
722 VLIB_INIT_FUNCTION (pg_cli_init);
725 * fd.io coding-style-patch-verification: ON
728 * eval: (c-set-style "gnu")