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. */
50 VLIB_CLI_COMMAND (vlib_cli_pg_command, static) = {
51 .path = "packet-generator",
52 .short_help = "Packet generator commands",
56 pg_enable_disable (u32 stream_index, int is_enable)
58 pg_main_t *pg = &pg_main;
61 if (stream_index == ~0)
63 /* No stream specified: enable/disable all streams. */
64 pool_foreach (s, pg->streams) {
65 pg_stream_enable_disable (pg, s, is_enable);
70 /* enable/disable specified stream. */
71 s = pool_elt_at_index (pg->streams, stream_index);
72 pg_stream_enable_disable (pg, s, is_enable);
77 pg_capture (pg_capture_args_t * a)
79 pg_main_t *pg = &pg_main;
82 if (a->is_enabled == 1)
85 if (stat (a->pcap_file_name, &sb) != -1)
86 return clib_error_return (0, "pcap file '%s' already exists.",
90 pi = pool_elt_at_index (pg->interfaces, a->dev_instance);
91 vec_free (pi->pcap_file_name);
92 if ((pi->pcap_main.flags & PCAP_MAIN_INIT_DONE))
93 pcap_close (&pi->pcap_main);
94 clib_memset (&pi->pcap_main, 0, sizeof (pi->pcap_main));
95 pi->pcap_main.file_descriptor = -1;
97 if (a->is_enabled == 0)
100 pi->pcap_file_name = a->pcap_file_name;
101 pi->pcap_main.file_name = (char *) pi->pcap_file_name;
102 pi->pcap_main.n_packets_to_capture = a->count;
103 pi->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
108 static clib_error_t *
109 enable_disable_stream (vlib_main_t * vm,
110 unformat_input_t * input, vlib_cli_command_t * cmd)
112 unformat_input_t _line_input, *line_input = &_line_input;
113 pg_main_t *pg = &pg_main;
114 int is_enable = cmd->function_arg != 0;
115 u32 stream_index = ~0;
117 if (!unformat_user (input, unformat_line_input, line_input))
120 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
122 if (unformat (line_input, "%U", unformat_hash_vec_string,
123 pg->stream_index_by_name, &stream_index))
126 return clib_error_create ("unknown input `%U'",
127 format_unformat_error, line_input);
129 unformat_free (line_input);
132 pg_enable_disable (stream_index, is_enable);
137 VLIB_CLI_COMMAND (enable_streams_cli, static) = {
138 .path = "packet-generator enable-stream",
139 .short_help = "Enable packet generator streams",
140 .function = enable_disable_stream,
141 .function_arg = 1, /* is_enable */
144 VLIB_CLI_COMMAND (disable_streams_cli, static) = {
145 .path = "packet-generator disable-stream",
146 .short_help = "Disable packet generator streams",
147 .function = enable_disable_stream,
148 .function_arg = 0, /* is_enable */
152 format_pg_edit_group (u8 * s, va_list * va)
154 pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
157 format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
158 g->start_byte_offset);
159 if (g->edit_function)
163 function_name = format (0, "%U%c", format_clib_elf_symbol_with_address,
164 g->edit_function, 0);
165 junk_after_name = function_name;
166 while (*junk_after_name && *junk_after_name != ' ')
168 *junk_after_name = 0;
169 s = format (s, "edit-function %s, ", function_name);
170 vec_free (function_name);
177 format_pg_stream (u8 * s, va_list * va)
179 pg_stream_t *t = va_arg (*va, pg_stream_t *);
180 int verbose = va_arg (*va, int);
183 return format (s, "%-16s%=12s%=16s%s",
184 "Name", "Enabled", "Count", "Parameters");
186 s = format (s, "%-16v%=12s%=16Ld",
188 pg_stream_is_enabled (t) ? "Yes" : "No",
189 t->n_packets_generated);
191 int indent = format_get_indent (s);
193 s = format (s, "limit %Ld, ", t->n_packets_limit);
194 s = format (s, "rate %.2e pps, ", t->rate_packets_per_second);
195 s = format (s, "size %d%c%d, ",
197 t->packet_size_edit_type == PG_EDIT_RANDOM ? '+' : '-',
198 t->max_packet_bytes);
199 s = format (s, "buffer-size %d, ", t->buffer_bytes);
200 s = format (s, "worker %d, ", t->worker_index);
205 vec_foreach (g, t->edit_groups)
207 s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
214 static clib_error_t *
215 show_streams (vlib_main_t * vm,
216 unformat_input_t * input, vlib_cli_command_t * cmd)
218 pg_main_t *pg = &pg_main;
222 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
224 if (unformat (input, "verbose"))
230 if (pool_elts (pg->streams) == 0)
232 vlib_cli_output (vm, "no streams currently defined");
236 vlib_cli_output (vm, "%U", format_pg_stream, 0, 0);
237 pool_foreach (s, pg->streams) {
238 vlib_cli_output (vm, "%U", format_pg_stream, s, verbose);
245 VLIB_CLI_COMMAND (show_streams_cli, static) = {
246 .path = "show packet-generator ",
247 .short_help = "show packet-generator [verbose]",
248 .function = show_streams,
251 static clib_error_t *
252 pg_pcap_read (pg_stream_t * s, char *file_name)
255 return clib_error_return (0, "no pcap support");
259 clib_memset (&pm, 0, sizeof (pm));
260 pm.file_name = file_name;
261 error = pcap_read (&pm);
262 s->replay_packet_templates = pm.packets_read;
263 s->replay_packet_timestamps = pm.timestamps;
264 s->min_packet_bytes = pm.min_packet_bytes;
265 s->max_packet_bytes = pm.max_packet_bytes;
266 s->buffer_bytes = pm.max_packet_bytes;
268 if (s->n_packets_limit == 0)
269 s->n_packets_limit = vec_len (pm.packets_read);
272 #endif /* CLIB_UNIX */
276 unformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
278 pg_stream_t *s = va_arg (*args, pg_stream_t *);
281 if (unformat (input, "limit %f", &x))
282 s->n_packets_limit = x;
284 else if (unformat (input, "rate %f", &x))
285 s->rate_packets_per_second = x;
287 else if (unformat (input, "size %d-%d", &s->min_packet_bytes,
288 &s->max_packet_bytes))
289 s->packet_size_edit_type = PG_EDIT_INCREMENT;
291 else if (unformat (input, "size %d+%d", &s->min_packet_bytes,
292 &s->max_packet_bytes))
293 s->packet_size_edit_type = PG_EDIT_RANDOM;
295 else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
304 static clib_error_t *
305 validate_stream (pg_stream_t * s)
307 if (s->max_packet_bytes < s->min_packet_bytes)
308 return clib_error_create ("max-size < min-size");
310 u32 hdr_size = pg_edit_group_n_bytes (s, 0);
311 if (s->min_packet_bytes < hdr_size)
312 return clib_error_create ("min-size < total header size %d", hdr_size);
313 if (s->buffer_bytes == 0)
314 return clib_error_create ("buffer-size must be positive");
316 if (s->rate_packets_per_second < 0)
317 return clib_error_create ("negative rate");
323 pg_interface_get_input_node (pg_interface_t *pi)
327 case PG_MODE_ETHERNET:
328 return ("ethernet-input");
330 return ("ip4-input");
332 return ("ip6-input");
336 return ("ethernet-input");
339 static clib_error_t *
340 new_stream (vlib_main_t * vm,
341 unformat_input_t * input, vlib_cli_command_t * cmd)
343 clib_error_t *error = 0;
345 u32 maxframe, hw_if_index;
346 unformat_input_t sub_input = { 0 };
347 int sub_input_given = 0;
348 vnet_main_t *vnm = vnet_get_main ();
349 pg_main_t *pg = &pg_main;
350 pg_stream_t s = { 0 };
351 char *pcap_file_name;
353 s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
355 s.max_packet_bytes = s.min_packet_bytes = 64;
356 s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
358 s.n_max_frame = VLIB_FRAME_SIZE;
361 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
363 if (unformat (input, "name %v", &tmp))
370 else if (unformat (input, "node %U",
371 unformat_vnet_hw_interface, vnm, &hw_if_index))
373 vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
375 s.node_index = hi->output_node_index;
376 s.sw_if_index[VLIB_TX] = hi->sw_if_index;
379 else if (unformat (input, "source pg%u", &s.if_id))
382 else if (unformat (input, "buffer-flags %U",
383 unformat_vnet_buffer_flags, &s.buffer_flags))
385 else if (unformat (input, "buffer-offload-flags %U",
386 unformat_vnet_buffer_offload_flags, &s.buffer_oflags))
388 else if (unformat (input, "node %U",
389 unformat_vlib_node, vm, &s.node_index))
391 else if (unformat (input, "maxframe %u", &maxframe))
392 s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
393 else if (unformat (input, "worker %u", &s.worker_index))
396 else if (unformat (input, "interface %U",
397 unformat_vnet_sw_interface, vnm,
398 &s.sw_if_index[VLIB_RX]))
400 else if (unformat (input, "tx-interface %U",
401 unformat_vnet_sw_interface, vnm,
402 &s.sw_if_index[VLIB_TX]))
405 else if (unformat (input, "pcap %s", &pcap_file_name))
408 else if (!sub_input_given
409 && unformat (input, "data %U", unformat_input, &sub_input))
412 else if (unformat_user (input, unformat_pg_stream_parameter, &s))
417 error = clib_error_create ("unknown input `%U'",
418 format_unformat_error, input);
423 if (!sub_input_given && !pcap_file_name)
425 error = clib_error_create ("no packet data given");
429 if (s.node_index == ~0)
431 if (pcap_file_name != 0)
436 n = vlib_get_node_by_name (vm, (u8 *) pg_interface_get_input_node (
437 &pg->interfaces[s.if_id]));
439 n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
440 s.node_index = n->index;
444 error = clib_error_create ("output interface or node not given");
452 if (s.node_index < vec_len (pg->nodes))
453 n = pg->nodes + s.node_index;
457 if (s.worker_index >= vlib_num_workers ())
460 if (pcap_file_name != 0)
462 error = pg_pcap_read (&s, pcap_file_name);
465 vec_free (pcap_file_name);
468 else if (n && n->unformat_edit
469 && unformat_user (&sub_input, n->unformat_edit, &s))
472 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
474 error = clib_error_create
475 ("failed to parse packet data from `%U'",
476 format_unformat_error, &sub_input);
481 error = validate_stream (&s);
485 pg_stream_add (pg, &s);
490 unformat_free (&sub_input);
494 VLIB_CLI_COMMAND (new_stream_cli, static) = {
495 .path = "packet-generator new",
496 .function = new_stream,
497 .short_help = "Create packet generator stream",
499 "Create packet generator stream\n"
503 "name STRING sets stream name\n"
504 "interface STRING interface for stream output \n"
505 "node NODE-NAME node for stream output\n"
506 "data STRING specifies packet data\n"
507 "pcap FILENAME read packet data from pcap file\n"
508 "rate PPS rate to transfer packet data\n"
509 "maxframe NPKTS maximum number of packets per frame\n",
512 static clib_error_t *
513 del_stream (vlib_main_t * vm,
514 unformat_input_t * input, vlib_cli_command_t * cmd)
516 pg_main_t *pg = &pg_main;
519 if (!unformat (input, "%U",
520 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
521 return clib_error_create ("expected stream name `%U'",
522 format_unformat_error, input);
524 pg_stream_del (pg, i);
528 VLIB_CLI_COMMAND (del_stream_cli, static) = {
529 .path = "packet-generator delete",
530 .function = del_stream,
531 .short_help = "Delete stream with given name",
534 static clib_error_t *
535 change_stream_parameters (vlib_main_t * vm,
536 unformat_input_t * input, vlib_cli_command_t * cmd)
538 pg_main_t *pg = &pg_main;
539 pg_stream_t *s, s_new;
540 u32 stream_index = ~0;
543 if (unformat (input, "%U", unformat_hash_vec_string,
544 pg->stream_index_by_name, &stream_index))
547 return clib_error_create ("expecting stream name; got `%U'",
548 format_unformat_error, input);
550 s = pool_elt_at_index (pg->streams, stream_index);
553 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
555 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
559 return clib_error_create ("unknown input `%U'",
560 format_unformat_error, input);
563 error = validate_stream (&s_new);
567 pg_stream_change (pg, s);
573 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
574 .path = "packet-generator configure",
575 .short_help = "Change packet generator stream parameters",
576 .function = change_stream_parameters,
579 static clib_error_t *
580 pg_capture_cmd_fn (vlib_main_t * vm,
581 unformat_input_t * input, vlib_cli_command_t * cmd)
583 clib_error_t *error = 0;
584 vnet_main_t *vnm = vnet_get_main ();
585 unformat_input_t _line_input, *line_input = &_line_input;
586 vnet_hw_interface_t *hi = 0;
587 u8 *pcap_file_name = 0;
592 if (!unformat_user (input, unformat_line_input, line_input))
595 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
597 if (unformat (line_input, "%U",
598 unformat_vnet_hw_interface, vnm, &hw_if_index))
600 hi = vnet_get_hw_interface (vnm, hw_if_index);
603 else if (unformat (line_input, "pcap %s", &pcap_file_name))
605 else if (unformat (line_input, "count %u", &count))
607 else if (unformat (line_input, "disable"))
612 error = clib_error_create ("unknown input `%U'",
613 format_unformat_error, line_input);
620 error = clib_error_return (0, "Please specify interface name");
624 if (hi->dev_class_index != pg_dev_class.index)
627 clib_error_return (0, "Please specify packet-generator interface");
631 if (!pcap_file_name && is_disable == 0)
633 error = clib_error_return (0, "Please specify pcap file name");
638 pg_capture_args_t _a, *a = &_a;
640 a->hw_if_index = hw_if_index;
641 a->dev_instance = hi->dev_instance;
642 a->is_enabled = !is_disable;
643 a->pcap_file_name = (char *) pcap_file_name;
646 error = pg_capture (a);
649 unformat_free (line_input);
654 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
655 .path = "packet-generator capture",
656 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
657 .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 = ~0, gso_enabled = 0, gso_size = 0, coalesce_enabled = 0;
667 clib_error_t *error = NULL;
668 pg_interface_mode_t mode = PG_MODE_ETHERNET;
670 if (!unformat_user (input, unformat_line_input, line_input))
673 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
675 if (unformat (line_input, "interface pg%u", &if_id))
677 else if (unformat (line_input, "coalesce-enabled"))
678 coalesce_enabled = 1;
679 else if (unformat (line_input, "gso-enabled"))
682 if (unformat (line_input, "gso-size %u", &gso_size))
686 error = clib_error_create ("gso enabled but gso size missing");
690 else if (unformat (line_input, "mode ip4"))
692 else if (unformat (line_input, "mode ip6"))
696 error = clib_error_create ("unknown input `%U'",
697 format_unformat_error, line_input);
702 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size, coalesce_enabled,
706 unformat_free (line_input);
711 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
712 .path = "create packet-generator",
713 .short_help = "create packet-generator interface <interface name>"
714 " [gso-enabled gso-size <size> [coalesce-enabled]]"
715 " [mode <ethernet | ip4 | ip6>]",
716 .function = create_pg_if_cmd_fn,
719 /* Dummy init function so that we can be linked in. */
720 static clib_error_t *
721 pg_cli_init (vlib_main_t * vm)
726 VLIB_INIT_FUNCTION (pg_cli_init);
729 * fd.io coding-style-patch-verification: ON
732 * eval: (c-set-style "gnu")