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");
337 pg_interface_get_input_node (pg_interface_t *pi)
341 case PG_MODE_ETHERNET:
342 return ("ethernet-input");
344 return ("ip4-input");
346 return ("ip6-input");
350 return ("ethernet-input");
353 static clib_error_t *
354 new_stream (vlib_main_t * vm,
355 unformat_input_t * input, vlib_cli_command_t * cmd)
357 clib_error_t *error = 0;
359 u32 maxframe, hw_if_index;
360 unformat_input_t sub_input = { 0 };
361 int sub_input_given = 0;
362 vnet_main_t *vnm = vnet_get_main ();
363 pg_main_t *pg = &pg_main;
364 pg_stream_t s = { 0 };
365 char *pcap_file_name;
367 s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
369 s.max_packet_bytes = s.min_packet_bytes = 64;
370 s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
372 s.n_max_frame = VLIB_FRAME_SIZE;
375 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
377 if (unformat (input, "name %v", &tmp))
384 else if (unformat (input, "node %U",
385 unformat_vnet_hw_interface, vnm, &hw_if_index))
387 vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
389 s.node_index = hi->output_node_index;
390 s.sw_if_index[VLIB_TX] = hi->sw_if_index;
393 else if (unformat (input, "source pg%u", &s.if_id))
396 else if (unformat (input, "buffer-flags %U",
397 unformat_vnet_buffer_flags, &s.buffer_flags))
399 else if (unformat (input, "buffer-offload-flags %U",
400 unformat_vnet_buffer_offload_flags, &s.buffer_oflags))
402 else if (unformat (input, "node %U",
403 unformat_vlib_node, vm, &s.node_index))
405 else if (unformat (input, "maxframe %u", &maxframe))
406 s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
407 else if (unformat (input, "worker %u", &s.worker_index))
410 else if (unformat (input, "interface %U",
411 unformat_vnet_sw_interface, vnm,
412 &s.sw_if_index[VLIB_RX]))
414 else if (unformat (input, "tx-interface %U",
415 unformat_vnet_sw_interface, vnm,
416 &s.sw_if_index[VLIB_TX]))
419 else if (unformat (input, "pcap %s", &pcap_file_name))
422 else if (!sub_input_given
423 && unformat (input, "data %U", unformat_input, &sub_input))
426 else if (unformat_user (input, unformat_pg_stream_parameter, &s))
431 error = clib_error_create ("unknown input `%U'",
432 format_unformat_error, input);
437 if (!sub_input_given && !pcap_file_name)
439 error = clib_error_create ("no packet data given");
443 if (s.node_index == ~0)
445 if (pcap_file_name != 0)
450 n = vlib_get_node_by_name (vm, (u8 *) pg_interface_get_input_node (
451 &pg->interfaces[s.if_id]));
453 n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
454 s.node_index = n->index;
458 error = clib_error_create ("output interface or node not given");
466 if (s.node_index < vec_len (pg->nodes))
467 n = pg->nodes + s.node_index;
471 if (s.worker_index >= vlib_num_workers ())
474 if (pcap_file_name != 0)
476 error = pg_pcap_read (&s, pcap_file_name);
479 vec_free (pcap_file_name);
482 else if (n && n->unformat_edit
483 && unformat_user (&sub_input, n->unformat_edit, &s))
486 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
488 error = clib_error_create
489 ("failed to parse packet data from `%U'",
490 format_unformat_error, &sub_input);
495 error = validate_stream (&s);
499 pg_stream_add (pg, &s);
504 unformat_free (&sub_input);
509 VLIB_CLI_COMMAND (new_stream_cli, static) = {
510 .path = "packet-generator new",
511 .function = new_stream,
512 .short_help = "Create packet generator stream",
514 "Create packet generator stream\n"
518 "name STRING sets stream name\n"
519 "interface STRING interface for stream output \n"
520 "node NODE-NAME node for stream output\n"
521 "data STRING specifies packet data\n"
522 "pcap FILENAME read packet data from pcap file\n"
523 "rate PPS rate to transfer packet data\n"
524 "maxframe NPKTS maximum number of packets per frame\n",
528 static clib_error_t *
529 del_stream (vlib_main_t * vm,
530 unformat_input_t * input, vlib_cli_command_t * cmd)
532 pg_main_t *pg = &pg_main;
535 if (!unformat (input, "%U",
536 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
537 return clib_error_create ("expected stream name `%U'",
538 format_unformat_error, input);
540 pg_stream_del (pg, i);
545 VLIB_CLI_COMMAND (del_stream_cli, static) = {
546 .path = "packet-generator delete",
547 .function = del_stream,
548 .short_help = "Delete stream with given name",
552 static clib_error_t *
553 change_stream_parameters (vlib_main_t * vm,
554 unformat_input_t * input, vlib_cli_command_t * cmd)
556 pg_main_t *pg = &pg_main;
557 pg_stream_t *s, s_new;
558 u32 stream_index = ~0;
561 if (unformat (input, "%U", unformat_hash_vec_string,
562 pg->stream_index_by_name, &stream_index))
565 return clib_error_create ("expecting stream name; got `%U'",
566 format_unformat_error, input);
568 s = pool_elt_at_index (pg->streams, stream_index);
571 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
573 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
577 return clib_error_create ("unknown input `%U'",
578 format_unformat_error, input);
581 error = validate_stream (&s_new);
585 pg_stream_change (pg, s);
592 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
593 .path = "packet-generator configure",
594 .short_help = "Change packet generator stream parameters",
595 .function = change_stream_parameters,
599 static clib_error_t *
600 pg_capture_cmd_fn (vlib_main_t * vm,
601 unformat_input_t * input, vlib_cli_command_t * cmd)
603 clib_error_t *error = 0;
604 vnet_main_t *vnm = vnet_get_main ();
605 unformat_input_t _line_input, *line_input = &_line_input;
606 vnet_hw_interface_t *hi = 0;
607 u8 *pcap_file_name = 0;
612 if (!unformat_user (input, unformat_line_input, line_input))
615 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
617 if (unformat (line_input, "%U",
618 unformat_vnet_hw_interface, vnm, &hw_if_index))
620 hi = vnet_get_hw_interface (vnm, hw_if_index);
623 else if (unformat (line_input, "pcap %s", &pcap_file_name))
625 else if (unformat (line_input, "count %u", &count))
627 else if (unformat (line_input, "disable"))
632 error = clib_error_create ("unknown input `%U'",
633 format_unformat_error, line_input);
640 error = clib_error_return (0, "Please specify interface name");
644 if (hi->dev_class_index != pg_dev_class.index)
647 clib_error_return (0, "Please specify packet-generator interface");
651 if (!pcap_file_name && is_disable == 0)
653 error = clib_error_return (0, "Please specify pcap file name");
658 pg_capture_args_t _a, *a = &_a;
660 a->hw_if_index = hw_if_index;
661 a->dev_instance = hi->dev_instance;
662 a->is_enabled = !is_disable;
663 a->pcap_file_name = (char *) pcap_file_name;
666 error = pg_capture (a);
669 unformat_free (line_input);
675 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
676 .path = "packet-generator capture",
677 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
678 .function = pg_capture_cmd_fn,
682 static clib_error_t *
683 create_pg_if_cmd_fn (vlib_main_t * vm,
684 unformat_input_t * input, vlib_cli_command_t * cmd)
686 pg_main_t *pg = &pg_main;
687 unformat_input_t _line_input, *line_input = &_line_input;
688 u32 if_id, gso_enabled = 0, gso_size = 0, coalesce_enabled = 0;
689 clib_error_t *error = NULL;
690 pg_interface_mode_t mode = PG_MODE_ETHERNET;
692 if (!unformat_user (input, unformat_line_input, line_input))
695 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
697 if (unformat (line_input, "interface pg%u", &if_id))
699 else if (unformat (line_input, "coalesce-enabled"))
700 coalesce_enabled = 1;
701 else if (unformat (line_input, "gso-enabled"))
704 if (unformat (line_input, "gso-size %u", &gso_size))
708 error = clib_error_create ("gso enabled but gso size missing");
712 else if (unformat (line_input, "mode ip4"))
714 else if (unformat (line_input, "mode ip6"))
718 error = clib_error_create ("unknown input `%U'",
719 format_unformat_error, line_input);
724 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size, coalesce_enabled,
728 unformat_free (line_input);
734 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
735 .path = "create packet-generator",
736 .short_help = "create packet-generator interface <interface name>"
737 " [gso-enabled gso-size <size> [coalesce-enabled]]"
738 " [mode <ethernet | ip4 | ip6>]",
739 .function = create_pg_if_cmd_fn,
743 /* Dummy init function so that we can be linked in. */
744 static clib_error_t *
745 pg_cli_init (vlib_main_t * vm)
750 VLIB_INIT_FUNCTION (pg_cli_init);
753 * fd.io coding-style-patch-verification: ON
756 * eval: (c-set-style "gnu")