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 ((char *) 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 clib_memset (&pi->pcap_main, 0, sizeof (pi->pcap_main));
98 if (a->is_enabled == 0)
101 pi->pcap_file_name = a->pcap_file_name;
102 pi->pcap_main.file_name = (char *) pi->pcap_file_name;
103 pi->pcap_main.n_packets_to_capture = a->count;
104 pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
109 static clib_error_t *
110 enable_disable_stream (vlib_main_t * vm,
111 unformat_input_t * input, vlib_cli_command_t * cmd)
113 unformat_input_t _line_input, *line_input = &_line_input;
114 pg_main_t *pg = &pg_main;
115 int is_enable = cmd->function_arg != 0;
116 u32 stream_index = ~0;
118 if (!unformat_user (input, unformat_line_input, line_input))
121 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
123 if (unformat (line_input, "%U", unformat_hash_vec_string,
124 pg->stream_index_by_name, &stream_index))
127 return clib_error_create ("unknown input `%U'",
128 format_unformat_error, line_input);
130 unformat_free (line_input);
133 pg_enable_disable (stream_index, is_enable);
139 VLIB_CLI_COMMAND (enable_streams_cli, static) = {
140 .path = "packet-generator enable-stream",
141 .short_help = "Enable packet generator streams",
142 .function = enable_disable_stream,
143 .function_arg = 1, /* is_enable */
148 VLIB_CLI_COMMAND (disable_streams_cli, static) = {
149 .path = "packet-generator disable-stream",
150 .short_help = "Disable packet generator streams",
151 .function = enable_disable_stream,
152 .function_arg = 0, /* is_enable */
157 format_pg_edit_group (u8 * s, va_list * va)
159 pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
162 format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
163 g->start_byte_offset);
164 if (g->edit_function)
168 function_name = format (0, "%U%c", format_clib_elf_symbol_with_address,
169 g->edit_function, 0);
170 junk_after_name = function_name;
171 while (*junk_after_name && *junk_after_name != ' ')
173 *junk_after_name = 0;
174 s = format (s, "edit-function %s, ", function_name);
175 vec_free (function_name);
182 format_pg_stream (u8 * s, va_list * va)
184 pg_stream_t *t = va_arg (*va, pg_stream_t *);
185 int verbose = va_arg (*va, int);
188 return format (s, "%-16s%=12s%=16s%s",
189 "Name", "Enabled", "Count", "Parameters");
191 s = format (s, "%-16v%=12s%=16Ld",
193 pg_stream_is_enabled (t) ? "Yes" : "No",
194 t->n_packets_generated);
196 int indent = format_get_indent (s);
198 s = format (s, "limit %Ld, ", t->n_packets_limit);
199 s = format (s, "rate %.2e pps, ", t->rate_packets_per_second);
200 s = format (s, "size %d%c%d, ",
202 t->packet_size_edit_type == PG_EDIT_RANDOM ? '+' : '-',
203 t->max_packet_bytes);
204 s = format (s, "buffer-size %d, ", t->buffer_bytes);
205 s = format (s, "worker %d, ", t->worker_index);
211 vec_foreach (g, t->edit_groups)
213 s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
221 static clib_error_t *
222 show_streams (vlib_main_t * vm,
223 unformat_input_t * input, vlib_cli_command_t * cmd)
225 pg_main_t *pg = &pg_main;
229 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
231 if (unformat (input, "verbose"))
237 if (pool_elts (pg->streams) == 0)
239 vlib_cli_output (vm, "no streams currently defined");
243 vlib_cli_output (vm, "%U", format_pg_stream, 0, 0);
245 pool_foreach (s, pg->streams, ({
246 vlib_cli_output (vm, "%U", format_pg_stream, s, verbose);
255 VLIB_CLI_COMMAND (show_streams_cli, static) = {
256 .path = "show packet-generator ",
257 .short_help = "show packet-generator [verbose]",
258 .function = show_streams,
262 static clib_error_t *
263 pg_pcap_read (pg_stream_t * s, char *file_name)
266 return clib_error_return (0, "no pcap support");
270 clib_memset (&pm, 0, sizeof (pm));
271 pm.file_name = file_name;
272 error = pcap_read (&pm);
273 s->replay_packet_templates = pm.packets_read;
274 s->replay_packet_timestamps = pm.timestamps;
275 s->min_packet_bytes = pm.min_packet_bytes;
276 s->max_packet_bytes = pm.max_packet_bytes;
277 s->buffer_bytes = pm.max_packet_bytes;
279 if (s->n_packets_limit == 0)
280 s->n_packets_limit = vec_len (pm.packets_read);
283 #endif /* CLIB_UNIX */
287 unformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
289 pg_stream_t *s = va_arg (*args, pg_stream_t *);
292 if (unformat (input, "limit %f", &x))
293 s->n_packets_limit = x;
295 else if (unformat (input, "rate %f", &x))
296 s->rate_packets_per_second = x;
298 else if (unformat (input, "size %d-%d", &s->min_packet_bytes,
299 &s->max_packet_bytes))
300 s->packet_size_edit_type = PG_EDIT_INCREMENT;
302 else if (unformat (input, "size %d+%d", &s->min_packet_bytes,
303 &s->max_packet_bytes))
304 s->packet_size_edit_type = PG_EDIT_RANDOM;
306 else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
315 static clib_error_t *
316 validate_stream (pg_stream_t * s)
318 if (s->max_packet_bytes < s->min_packet_bytes)
319 return clib_error_create ("max-size < min-size");
321 u32 hdr_size = pg_edit_group_n_bytes (s, 0);
322 if (s->min_packet_bytes < hdr_size)
323 return clib_error_create ("min-size < total header size %d", hdr_size);
324 if (s->buffer_bytes == 0)
325 return clib_error_create ("buffer-size must be positive");
327 if (s->rate_packets_per_second < 0)
328 return clib_error_create ("negative rate");
333 static clib_error_t *
334 new_stream (vlib_main_t * vm,
335 unformat_input_t * input, vlib_cli_command_t * cmd)
337 clib_error_t *error = 0;
339 u32 maxframe, hw_if_index;
340 unformat_input_t sub_input = { 0 };
341 int sub_input_given = 0;
342 vnet_main_t *vnm = vnet_get_main ();
343 pg_main_t *pg = &pg_main;
344 pg_stream_t s = { 0 };
345 char *pcap_file_name;
347 s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
349 s.max_packet_bytes = s.min_packet_bytes = 64;
350 s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
352 s.n_max_frame = VLIB_FRAME_SIZE;
355 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
357 if (unformat (input, "name %v", &tmp))
364 else if (unformat (input, "node %U",
365 unformat_vnet_hw_interface, vnm, &hw_if_index))
367 vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
369 s.node_index = hi->output_node_index;
370 s.sw_if_index[VLIB_TX] = hi->sw_if_index;
373 else if (unformat (input, "source pg%u", &s.if_id))
376 else if (unformat (input, "node %U",
377 unformat_vlib_node, vm, &s.node_index))
379 else if (unformat (input, "maxframe %u", &maxframe))
380 s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
381 else if (unformat (input, "worker %u", &s.worker_index))
384 else if (unformat (input, "interface %U",
385 unformat_vnet_sw_interface, vnm,
386 &s.sw_if_index[VLIB_RX]))
388 else if (unformat (input, "tx-interface %U",
389 unformat_vnet_sw_interface, vnm,
390 &s.sw_if_index[VLIB_TX]))
393 else if (unformat (input, "pcap %s", &pcap_file_name))
396 else if (!sub_input_given
397 && unformat (input, "data %U", unformat_input, &sub_input))
400 else if (unformat_user (input, unformat_pg_stream_parameter, &s))
405 error = clib_error_create ("unknown input `%U'",
406 format_unformat_error, input);
411 if (!sub_input_given && !pcap_file_name)
413 error = clib_error_create ("no packet data given");
417 if (s.node_index == ~0)
419 if (pcap_file_name != 0)
422 vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
423 s.node_index = n->index;
427 error = clib_error_create ("output interface or node not given");
435 if (s.node_index < vec_len (pg->nodes))
436 n = pg->nodes + s.node_index;
440 if (s.worker_index >= vlib_num_workers ())
443 if (pcap_file_name != 0)
445 error = pg_pcap_read (&s, pcap_file_name);
448 vec_free (pcap_file_name);
451 else if (n && n->unformat_edit
452 && unformat_user (&sub_input, n->unformat_edit, &s))
455 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
457 error = clib_error_create
458 ("failed to parse packet data from `%U'",
459 format_unformat_error, &sub_input);
464 error = validate_stream (&s);
468 pg_stream_add (pg, &s);
473 unformat_free (&sub_input);
478 VLIB_CLI_COMMAND (new_stream_cli, static) = {
479 .path = "packet-generator new",
480 .function = new_stream,
481 .short_help = "Create packet generator stream",
483 "Create packet generator stream\n"
487 "name STRING sets stream name\n"
488 "interface STRING interface for stream output \n"
489 "node NODE-NAME node for stream output\n"
490 "data STRING specifies packet data\n"
491 "pcap FILENAME read packet data from pcap file\n"
492 "rate PPS rate to transfer packet data\n"
493 "maxframe NPKTS maximum number of packets per frame\n",
497 static clib_error_t *
498 del_stream (vlib_main_t * vm,
499 unformat_input_t * input, vlib_cli_command_t * cmd)
501 pg_main_t *pg = &pg_main;
504 if (!unformat (input, "%U",
505 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
506 return clib_error_create ("expected stream name `%U'",
507 format_unformat_error, input);
509 pg_stream_del (pg, i);
514 VLIB_CLI_COMMAND (del_stream_cli, static) = {
515 .path = "packet-generator delete",
516 .function = del_stream,
517 .short_help = "Delete stream with given name",
521 static clib_error_t *
522 change_stream_parameters (vlib_main_t * vm,
523 unformat_input_t * input, vlib_cli_command_t * cmd)
525 pg_main_t *pg = &pg_main;
526 pg_stream_t *s, s_new;
527 u32 stream_index = ~0;
530 if (unformat (input, "%U", unformat_hash_vec_string,
531 pg->stream_index_by_name, &stream_index))
534 return clib_error_create ("expecting stream name; got `%U'",
535 format_unformat_error, input);
537 s = pool_elt_at_index (pg->streams, stream_index);
540 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
542 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
546 return clib_error_create ("unknown input `%U'",
547 format_unformat_error, input);
550 error = validate_stream (&s_new);
554 pg_stream_change (pg, s);
561 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
562 .path = "packet-generator configure",
563 .short_help = "Change packet generator stream parameters",
564 .function = change_stream_parameters,
568 static clib_error_t *
569 pg_capture_cmd_fn (vlib_main_t * vm,
570 unformat_input_t * input, vlib_cli_command_t * cmd)
572 clib_error_t *error = 0;
573 vnet_main_t *vnm = vnet_get_main ();
574 unformat_input_t _line_input, *line_input = &_line_input;
575 vnet_hw_interface_t *hi = 0;
576 u8 *pcap_file_name = 0;
581 if (!unformat_user (input, unformat_line_input, line_input))
584 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
586 if (unformat (line_input, "%U",
587 unformat_vnet_hw_interface, vnm, &hw_if_index))
589 hi = vnet_get_hw_interface (vnm, hw_if_index);
592 else if (unformat (line_input, "pcap %s", &pcap_file_name))
594 else if (unformat (line_input, "count %u", &count))
596 else if (unformat (line_input, "disable"))
601 error = clib_error_create ("unknown input `%U'",
602 format_unformat_error, line_input);
609 error = clib_error_return (0, "Please specify interface name");
613 if (hi->dev_class_index != pg_dev_class.index)
616 clib_error_return (0, "Please specify packet-generator interface");
620 if (!pcap_file_name && is_disable == 0)
622 error = clib_error_return (0, "Please specify pcap file name");
627 pg_capture_args_t _a, *a = &_a;
629 a->hw_if_index = hw_if_index;
630 a->dev_instance = hi->dev_instance;
631 a->is_enabled = !is_disable;
632 a->pcap_file_name = pcap_file_name;
635 error = pg_capture (a);
638 unformat_free (line_input);
644 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
645 .path = "packet-generator capture",
646 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
647 .function = pg_capture_cmd_fn,
651 static clib_error_t *
652 create_pg_if_cmd_fn (vlib_main_t * vm,
653 unformat_input_t * input, vlib_cli_command_t * cmd)
655 pg_main_t *pg = &pg_main;
656 unformat_input_t _line_input, *line_input = &_line_input;
657 u32 if_id, gso_enabled = 0, gso_size = 0;
658 clib_error_t *error = NULL;
660 if (!unformat_user (input, unformat_line_input, line_input))
663 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
665 if (unformat (line_input, "interface pg%u", &if_id))
667 else if (unformat (line_input, "gso-enabled"))
670 if (unformat (line_input, "gso-size %u", &gso_size))
674 error = clib_error_create ("gso enabled but gso size missing");
680 error = clib_error_create ("unknown input `%U'",
681 format_unformat_error, line_input);
686 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size);
689 unformat_free (line_input);
695 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
696 .path = "create packet-generator",
697 .short_help = "create packet-generator interface <interface name> [gso-enabled gso-size <size>]",
698 .function = create_pg_if_cmd_fn,
702 /* Dummy init function so that we can be linked in. */
703 static clib_error_t *
704 pg_cli_init (vlib_main_t * vm)
709 VLIB_INIT_FUNCTION (pg_cli_init);
712 * fd.io coding-style-patch-verification: ON
715 * eval: (c-set-style "gnu")