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)
449 ASSERT (s.if_id != ~0);
452 n = vlib_get_node_by_name (vm, (u8 *) pg_interface_get_input_node (
453 &pg->interfaces[s.if_id]));
455 n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
456 s.node_index = n->index;
460 error = clib_error_create ("output interface or node not given");
468 if (s.node_index < vec_len (pg->nodes))
469 n = pg->nodes + s.node_index;
473 if (s.worker_index >= vlib_num_workers ())
476 if (pcap_file_name != 0)
478 error = pg_pcap_read (&s, pcap_file_name);
481 vec_free (pcap_file_name);
484 else if (n && n->unformat_edit
485 && unformat_user (&sub_input, n->unformat_edit, &s))
488 else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
490 error = clib_error_create
491 ("failed to parse packet data from `%U'",
492 format_unformat_error, &sub_input);
497 error = validate_stream (&s);
501 pg_stream_add (pg, &s);
506 unformat_free (&sub_input);
511 VLIB_CLI_COMMAND (new_stream_cli, static) = {
512 .path = "packet-generator new",
513 .function = new_stream,
514 .short_help = "Create packet generator stream",
516 "Create packet generator stream\n"
520 "name STRING sets stream name\n"
521 "interface STRING interface for stream output \n"
522 "node NODE-NAME node for stream output\n"
523 "data STRING specifies packet data\n"
524 "pcap FILENAME read packet data from pcap file\n"
525 "rate PPS rate to transfer packet data\n"
526 "maxframe NPKTS maximum number of packets per frame\n",
530 static clib_error_t *
531 del_stream (vlib_main_t * vm,
532 unformat_input_t * input, vlib_cli_command_t * cmd)
534 pg_main_t *pg = &pg_main;
537 if (!unformat (input, "%U",
538 &unformat_hash_vec_string, pg->stream_index_by_name, &i))
539 return clib_error_create ("expected stream name `%U'",
540 format_unformat_error, input);
542 pg_stream_del (pg, i);
547 VLIB_CLI_COMMAND (del_stream_cli, static) = {
548 .path = "packet-generator delete",
549 .function = del_stream,
550 .short_help = "Delete stream with given name",
554 static clib_error_t *
555 change_stream_parameters (vlib_main_t * vm,
556 unformat_input_t * input, vlib_cli_command_t * cmd)
558 pg_main_t *pg = &pg_main;
559 pg_stream_t *s, s_new;
560 u32 stream_index = ~0;
563 if (unformat (input, "%U", unformat_hash_vec_string,
564 pg->stream_index_by_name, &stream_index))
567 return clib_error_create ("expecting stream name; got `%U'",
568 format_unformat_error, input);
570 s = pool_elt_at_index (pg->streams, stream_index);
573 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
575 if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
579 return clib_error_create ("unknown input `%U'",
580 format_unformat_error, input);
583 error = validate_stream (&s_new);
587 pg_stream_change (pg, s);
594 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
595 .path = "packet-generator configure",
596 .short_help = "Change packet generator stream parameters",
597 .function = change_stream_parameters,
601 static clib_error_t *
602 pg_capture_cmd_fn (vlib_main_t * vm,
603 unformat_input_t * input, vlib_cli_command_t * cmd)
605 clib_error_t *error = 0;
606 vnet_main_t *vnm = vnet_get_main ();
607 unformat_input_t _line_input, *line_input = &_line_input;
608 vnet_hw_interface_t *hi = 0;
609 u8 *pcap_file_name = 0;
614 if (!unformat_user (input, unformat_line_input, line_input))
617 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
619 if (unformat (line_input, "%U",
620 unformat_vnet_hw_interface, vnm, &hw_if_index))
622 hi = vnet_get_hw_interface (vnm, hw_if_index);
625 else if (unformat (line_input, "pcap %s", &pcap_file_name))
627 else if (unformat (line_input, "count %u", &count))
629 else if (unformat (line_input, "disable"))
634 error = clib_error_create ("unknown input `%U'",
635 format_unformat_error, line_input);
642 error = clib_error_return (0, "Please specify interface name");
646 if (hi->dev_class_index != pg_dev_class.index)
649 clib_error_return (0, "Please specify packet-generator interface");
653 if (!pcap_file_name && is_disable == 0)
655 error = clib_error_return (0, "Please specify pcap file name");
660 pg_capture_args_t _a, *a = &_a;
662 a->hw_if_index = hw_if_index;
663 a->dev_instance = hi->dev_instance;
664 a->is_enabled = !is_disable;
665 a->pcap_file_name = (char *) pcap_file_name;
668 error = pg_capture (a);
671 unformat_free (line_input);
677 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
678 .path = "packet-generator capture",
679 .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
680 .function = pg_capture_cmd_fn,
684 static clib_error_t *
685 create_pg_if_cmd_fn (vlib_main_t * vm,
686 unformat_input_t * input, vlib_cli_command_t * cmd)
688 pg_main_t *pg = &pg_main;
689 unformat_input_t _line_input, *line_input = &_line_input;
690 u32 if_id, gso_enabled = 0, gso_size = 0, coalesce_enabled = 0;
691 clib_error_t *error = NULL;
693 if (!unformat_user (input, unformat_line_input, line_input))
696 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
698 if (unformat (line_input, "interface pg%u", &if_id))
700 else if (unformat (line_input, "coalesce-enabled"))
701 coalesce_enabled = 1;
702 else if (unformat (line_input, "gso-enabled"))
705 if (unformat (line_input, "gso-size %u", &gso_size))
709 error = clib_error_create ("gso enabled but gso size missing");
715 error = clib_error_create ("unknown input `%U'",
716 format_unformat_error, line_input);
721 pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size, coalesce_enabled,
725 unformat_free (line_input);
731 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
732 .path = "create packet-generator",
733 .short_help = "create packet-generator interface <interface name>"
734 " [gso-enabled gso-size <size> [coalesce-enabled]]",
735 .function = create_pg_if_cmd_fn,
739 /* Dummy init function so that we can be linked in. */
740 static clib_error_t *
741 pg_cli_init (vlib_main_t * vm)
746 VLIB_INIT_FUNCTION (pg_cli_init);
749 * fd.io coding-style-patch-verification: ON
752 * eval: (c-set-style "gnu")