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, "node %U",
380 unformat_vlib_node, vm, &s.node_index))
382 else if (unformat (input, "maxframe %u", &maxframe))
383 s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
384 else if (unformat (input, "worker %u", &s.worker_index))
387 else if (unformat (input, "interface %U",
388 unformat_vnet_sw_interface, vnm,
389 &s.sw_if_index[VLIB_RX]))
391 else if (unformat (input, "tx-interface %U",
392 unformat_vnet_sw_interface, vnm,
393 &s.sw_if_index[VLIB_TX]))
396 else if (unformat (input, "pcap %s", &pcap_file_name))
399 else if (!sub_input_given
400 && unformat (input, "data %U", unformat_input, &sub_input))
403 else if (unformat_user (input, unformat_pg_stream_parameter, &s))
408 error = clib_error_create ("unknown input `%U'",
409 format_unformat_error, input);
414 if (!sub_input_given && !pcap_file_name)
416 error = clib_error_create ("no packet data given");
420 if (s.node_index == ~0)
422 if (pcap_file_name != 0)
425 vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
426 s.node_index = n->index;
430 error = clib_error_create ("output interface or node not given");
438 if (s.node_index < vec_len (pg->nodes))
439 n = pg->nodes + s.node_index;
443 if (s.worker_index >= vlib_num_workers ())
446 if (pcap_file_name != 0)
448 error = pg_pcap_read (&s, pcap_file_name);
451 vec_free (pcap_file_name);
454 else if (n && n->unformat_edit
455 && unformat_user (&sub_input, n->unformat_edit, &s))
458 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
460 error = clib_error_create
461 ("failed to parse packet data from `%U'",
462 format_unformat_error, &sub_input);
467 error = validate_stream (&s);
471 pg_stream_add (pg, &s);
476 unformat_free (&sub_input);
481 VLIB_CLI_COMMAND (new_stream_cli, static) = {
482 .path = "packet-generator new",
483 .function = new_stream,
484 .short_help = "Create packet generator stream",
486 "Create packet generator stream\n"
490 "name STRING sets stream name\n"
491 "interface STRING interface for stream output \n"
492 "node NODE-NAME node for stream output\n"
493 "data STRING specifies packet data\n"
494 "pcap FILENAME read packet data from pcap file\n"
495 "rate PPS rate to transfer packet data\n"
496 "maxframe NPKTS maximum number of packets per frame\n",
500 static clib_error_t *
501 del_stream (vlib_main_t * vm,
502 unformat_input_t * input, vlib_cli_command_t * cmd)
504 pg_main_t *pg = &pg_main;
507 if (!unformat (input, "%U",
508 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
509 return clib_error_create ("expected stream name `%U'",
510 format_unformat_error, input);
512 pg_stream_del (pg, i);
517 VLIB_CLI_COMMAND (del_stream_cli, static) = {
518 .path = "packet-generator delete",
519 .function = del_stream,
520 .short_help = "Delete stream with given name",
524 static clib_error_t *
525 change_stream_parameters (vlib_main_t * vm,
526 unformat_input_t * input, vlib_cli_command_t * cmd)
528 pg_main_t *pg = &pg_main;
529 pg_stream_t *s, s_new;
530 u32 stream_index = ~0;
533 if (unformat (input, "%U", unformat_hash_vec_string,
534 pg->stream_index_by_name, &stream_index))
537 return clib_error_create ("expecting stream name; got `%U'",
538 format_unformat_error, input);
540 s = pool_elt_at_index (pg->streams, stream_index);
543 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
545 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
549 return clib_error_create ("unknown input `%U'",
550 format_unformat_error, input);
553 error = validate_stream (&s_new);
557 pg_stream_change (pg, s);
564 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
565 .path = "packet-generator configure",
566 .short_help = "Change packet generator stream parameters",
567 .function = change_stream_parameters,
571 static clib_error_t *
572 pg_capture_cmd_fn (vlib_main_t * vm,
573 unformat_input_t * input, vlib_cli_command_t * cmd)
575 clib_error_t *error = 0;
576 vnet_main_t *vnm = vnet_get_main ();
577 unformat_input_t _line_input, *line_input = &_line_input;
578 vnet_hw_interface_t *hi = 0;
579 u8 *pcap_file_name = 0;
584 if (!unformat_user (input, unformat_line_input, line_input))
587 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
589 if (unformat (line_input, "%U",
590 unformat_vnet_hw_interface, vnm, &hw_if_index))
592 hi = vnet_get_hw_interface (vnm, hw_if_index);
595 else if (unformat (line_input, "pcap %s", &pcap_file_name))
597 else if (unformat (line_input, "count %u", &count))
599 else if (unformat (line_input, "disable"))
604 error = clib_error_create ("unknown input `%U'",
605 format_unformat_error, line_input);
612 error = clib_error_return (0, "Please specify interface name");
616 if (hi->dev_class_index != pg_dev_class.index)
619 clib_error_return (0, "Please specify packet-generator interface");
623 if (!pcap_file_name && is_disable == 0)
625 error = clib_error_return (0, "Please specify pcap file name");
630 pg_capture_args_t _a, *a = &_a;
632 a->hw_if_index = hw_if_index;
633 a->dev_instance = hi->dev_instance;
634 a->is_enabled = !is_disable;
635 a->pcap_file_name = (char *) pcap_file_name;
638 error = pg_capture (a);
641 unformat_free (line_input);
647 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
648 .path = "packet-generator capture",
649 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
650 .function = pg_capture_cmd_fn,
654 static clib_error_t *
655 create_pg_if_cmd_fn (vlib_main_t * vm,
656 unformat_input_t * input, vlib_cli_command_t * cmd)
658 pg_main_t *pg = &pg_main;
659 unformat_input_t _line_input, *line_input = &_line_input;
660 u32 if_id, gso_enabled = 0, gso_size = 0;
661 clib_error_t *error = NULL;
663 if (!unformat_user (input, unformat_line_input, line_input))
666 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
668 if (unformat (line_input, "interface pg%u", &if_id))
670 else if (unformat (line_input, "gso-enabled"))
673 if (unformat (line_input, "gso-size %u", &gso_size))
677 error = clib_error_create ("gso enabled but gso size missing");
683 error = clib_error_create ("unknown input `%U'",
684 format_unformat_error, line_input);
689 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size);
692 unformat_free (line_input);
698 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
699 .path = "create packet-generator",
700 .short_help = "create packet-generator interface <interface name> [gso-enabled gso-size <size>]",
701 .function = create_pg_if_cmd_fn,
705 /* Dummy init function so that we can be linked in. */
706 static clib_error_t *
707 pg_cli_init (vlib_main_t * vm)
712 VLIB_INIT_FUNCTION (pg_cli_init);
715 * fd.io coding-style-patch-verification: ON
718 * eval: (c-set-style "gnu")