c43ff903e756fe6997eb4ecc85ef0f0e8ea26944
[vpp.git] / src / vnet / pg / cli.c
1 /*
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15 /*
16  * pg_cli.c: packet generator cli
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
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:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
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.
38  */
39
40 #include <sys/stat.h>
41
42 #include <vnet/vnet.h>
43 #include <vnet/pg/pg.h>
44
45 #include <strings.h>
46 #include <vppinfra/pcap.h>
47
48
49 /* Root of all packet generator cli commands. */
50 /* *INDENT-OFF* */
51 VLIB_CLI_COMMAND (vlib_cli_pg_command, static) = {
52   .path = "packet-generator",
53   .short_help = "Packet generator commands",
54 };
55 /* *INDENT-ON* */
56
57 void
58 pg_enable_disable (u32 stream_index, int is_enable)
59 {
60   pg_main_t *pg = &pg_main;
61   pg_stream_t *s;
62
63   if (stream_index == ~0)
64     {
65       /* No stream specified: enable/disable all streams. */
66       /* *INDENT-OFF* */
67         pool_foreach (s, pg->streams, ({
68             pg_stream_enable_disable (pg, s, is_enable);
69         }));
70         /* *INDENT-ON* */
71     }
72   else
73     {
74       /* enable/disable specified stream. */
75       s = pool_elt_at_index (pg->streams, stream_index);
76       pg_stream_enable_disable (pg, s, is_enable);
77     }
78 }
79
80 clib_error_t *
81 pg_capture (pg_capture_args_t * a)
82 {
83   pg_main_t *pg = &pg_main;
84   pg_interface_t *pi;
85
86   if (a->is_enabled == 1)
87     {
88       struct stat sb;
89       if (stat ((char *) a->pcap_file_name, &sb) != -1)
90         return clib_error_return (0, "pcap file '%s' already exists.",
91                                   a->pcap_file_name);
92     }
93
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));
97
98   if (a->is_enabled == 0)
99     return 0;
100
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;
105
106   return 0;
107 }
108
109 static clib_error_t *
110 enable_disable_stream (vlib_main_t * vm,
111                        unformat_input_t * input, vlib_cli_command_t * cmd)
112 {
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;
117
118   if (!unformat_user (input, unformat_line_input, line_input))
119     goto doit;
120
121   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
122     {
123       if (unformat (line_input, "%U", unformat_hash_vec_string,
124                     pg->stream_index_by_name, &stream_index))
125         ;
126       else
127         return clib_error_create ("unknown input `%U'",
128                                   format_unformat_error, line_input);
129     }
130   unformat_free (line_input);
131
132 doit:
133   pg_enable_disable (stream_index, is_enable);
134
135   return 0;
136 }
137
138 /* *INDENT-OFF* */
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 */
144 };
145 /* *INDENT-ON* */
146
147 /* *INDENT-OFF* */
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 */
153 };
154 /* *INDENT-ON* */
155
156 static u8 *
157 format_pg_edit_group (u8 * s, va_list * va)
158 {
159   pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
160
161   s =
162     format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
163             g->start_byte_offset);
164   if (g->edit_function)
165     {
166       u8 *function_name;
167       u8 *junk_after_name;
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 != ' ')
172         junk_after_name++;
173       *junk_after_name = 0;
174       s = format (s, "edit-function %s, ", function_name);
175       vec_free (function_name);
176     }
177
178   return s;
179 }
180
181 static u8 *
182 format_pg_stream (u8 * s, va_list * va)
183 {
184   pg_stream_t *t = va_arg (*va, pg_stream_t *);
185   int verbose = va_arg (*va, int);
186
187   if (!t)
188     return format (s, "%-16s%=12s%=16s%s",
189                    "Name", "Enabled", "Count", "Parameters");
190
191   s = format (s, "%-16v%=12s%=16Ld",
192               t->name,
193               pg_stream_is_enabled (t) ? "Yes" : "No",
194               t->n_packets_generated);
195
196   int indent = format_get_indent (s);
197
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, ",
201               t->min_packet_bytes,
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);
206
207   if (verbose)
208     {
209       pg_edit_group_t *g;
210   /* *INDENT-OFF* */
211   vec_foreach (g, t->edit_groups)
212     {
213       s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
214     }
215   /* *INDENT-ON* */
216     }
217
218   return s;
219 }
220
221 static clib_error_t *
222 show_streams (vlib_main_t * vm,
223               unformat_input_t * input, vlib_cli_command_t * cmd)
224 {
225   pg_main_t *pg = &pg_main;
226   pg_stream_t *s;
227   int verbose = 0;
228
229   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
230     {
231       if (unformat (input, "verbose"))
232         verbose = 1;
233       else
234         break;
235     }
236
237   if (pool_elts (pg->streams) == 0)
238     {
239       vlib_cli_output (vm, "no streams currently defined");
240       goto done;
241     }
242
243   vlib_cli_output (vm, "%U", format_pg_stream, 0, 0);
244   /* *INDENT-OFF* */
245   pool_foreach (s, pg->streams, ({
246       vlib_cli_output (vm, "%U", format_pg_stream, s, verbose);
247     }));
248   /* *INDENT-ON* */
249
250 done:
251   return 0;
252 }
253
254 /* *INDENT-OFF* */
255 VLIB_CLI_COMMAND (show_streams_cli, static) = {
256   .path = "show packet-generator ",
257   .short_help = "show packet-generator [verbose]",
258   .function = show_streams,
259 };
260 /* *INDENT-ON* */
261
262 static clib_error_t *
263 pg_pcap_read (pg_stream_t * s, char *file_name)
264 {
265 #ifndef CLIB_UNIX
266   return clib_error_return (0, "no pcap support");
267 #else
268   pcap_main_t pm;
269   clib_error_t *error;
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;
278
279   if (s->n_packets_limit == 0)
280     s->n_packets_limit = vec_len (pm.packets_read);
281
282   return error;
283 #endif /* CLIB_UNIX */
284 }
285
286 static uword
287 unformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
288 {
289   pg_stream_t *s = va_arg (*args, pg_stream_t *);
290   f64 x;
291
292   if (unformat (input, "limit %f", &x))
293     s->n_packets_limit = x;
294
295   else if (unformat (input, "rate %f", &x))
296     s->rate_packets_per_second = x;
297
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;
301
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;
305
306   else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
307     ;
308
309   else
310     return 0;
311
312   return 1;
313 }
314
315 static clib_error_t *
316 validate_stream (pg_stream_t * s)
317 {
318   if (s->max_packet_bytes < s->min_packet_bytes)
319     return clib_error_create ("max-size < min-size");
320
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");
326
327   if (s->rate_packets_per_second < 0)
328     return clib_error_create ("negative rate");
329
330   return 0;
331 }
332
333 static clib_error_t *
334 new_stream (vlib_main_t * vm,
335             unformat_input_t * input, vlib_cli_command_t * cmd)
336 {
337   clib_error_t *error = 0;
338   u8 *tmp = 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;
346
347   s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
348   s.node_index = ~0;
349   s.max_packet_bytes = s.min_packet_bytes = 64;
350   s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
351   s.if_id = 0;
352   s.n_max_frame = VLIB_FRAME_SIZE;
353   pcap_file_name = 0;
354
355   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
356     {
357       if (unformat (input, "name %v", &tmp))
358         {
359           if (s.name)
360             vec_free (s.name);
361           s.name = tmp;
362         }
363
364       else if (unformat (input, "node %U",
365                          unformat_vnet_hw_interface, vnm, &hw_if_index))
366         {
367           vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
368
369           s.node_index = hi->output_node_index;
370           s.sw_if_index[VLIB_TX] = hi->sw_if_index;
371         }
372
373       else if (unformat (input, "source pg%u", &s.if_id))
374         ;
375
376       else if (unformat (input, "node %U",
377                          unformat_vlib_node, vm, &s.node_index))
378         ;
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))
382         ;
383
384       else if (unformat (input, "interface %U",
385                          unformat_vnet_sw_interface, vnm,
386                          &s.sw_if_index[VLIB_RX]))
387         ;
388       else if (unformat (input, "tx-interface %U",
389                          unformat_vnet_sw_interface, vnm,
390                          &s.sw_if_index[VLIB_TX]))
391         ;
392
393       else if (unformat (input, "pcap %s", &pcap_file_name))
394         ;
395
396       else if (!sub_input_given
397                && unformat (input, "data %U", unformat_input, &sub_input))
398         sub_input_given++;
399
400       else if (unformat_user (input, unformat_pg_stream_parameter, &s))
401         ;
402
403       else
404         {
405           error = clib_error_create ("unknown input `%U'",
406                                      format_unformat_error, input);
407           goto done;
408         }
409     }
410
411   if (!sub_input_given && !pcap_file_name)
412     {
413       error = clib_error_create ("no packet data given");
414       goto done;
415     }
416
417   if (s.node_index == ~0)
418     {
419       if (pcap_file_name != 0)
420         {
421           vlib_node_t *n =
422             vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
423           s.node_index = n->index;
424         }
425       else
426         {
427           error = clib_error_create ("output interface or node not given");
428           goto done;
429         }
430     }
431
432   {
433     pg_node_t *n;
434
435     if (s.node_index < vec_len (pg->nodes))
436       n = pg->nodes + s.node_index;
437     else
438       n = 0;
439
440     if (s.worker_index >= vlib_num_workers ())
441       s.worker_index = 0;
442
443     if (pcap_file_name != 0)
444       {
445         error = pg_pcap_read (&s, pcap_file_name);
446         if (error)
447           goto done;
448         vec_free (pcap_file_name);
449       }
450
451     else if (n && n->unformat_edit
452              && unformat_user (&sub_input, n->unformat_edit, &s))
453       ;
454
455     else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
456       {
457         error = clib_error_create
458           ("failed to parse packet data from `%U'",
459            format_unformat_error, &sub_input);
460         goto done;
461       }
462   }
463
464   error = validate_stream (&s);
465   if (error)
466     return error;
467
468   pg_stream_add (pg, &s);
469   return 0;
470
471 done:
472   pg_stream_free (&s);
473   unformat_free (&sub_input);
474   return error;
475 }
476
477 /* *INDENT-OFF* */
478 VLIB_CLI_COMMAND (new_stream_cli, static) = {
479   .path = "packet-generator new",
480   .function = new_stream,
481   .short_help = "Create packet generator stream",
482   .long_help =
483   "Create packet generator stream\n"
484   "\n"
485   "Arguments:\n"
486   "\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",
494 };
495 /* *INDENT-ON* */
496
497 static clib_error_t *
498 del_stream (vlib_main_t * vm,
499             unformat_input_t * input, vlib_cli_command_t * cmd)
500 {
501   pg_main_t *pg = &pg_main;
502   u32 i;
503
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);
508
509   pg_stream_del (pg, i);
510   return 0;
511 }
512
513 /* *INDENT-OFF* */
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",
518 };
519 /* *INDENT-ON* */
520
521 static clib_error_t *
522 change_stream_parameters (vlib_main_t * vm,
523                           unformat_input_t * input, vlib_cli_command_t * cmd)
524 {
525   pg_main_t *pg = &pg_main;
526   pg_stream_t *s, s_new;
527   u32 stream_index = ~0;
528   clib_error_t *error;
529
530   if (unformat (input, "%U", unformat_hash_vec_string,
531                 pg->stream_index_by_name, &stream_index))
532     ;
533   else
534     return clib_error_create ("expecting stream name; got `%U'",
535                               format_unformat_error, input);
536
537   s = pool_elt_at_index (pg->streams, stream_index);
538   s_new = s[0];
539
540   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
541     {
542       if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
543         ;
544
545       else
546         return clib_error_create ("unknown input `%U'",
547                                   format_unformat_error, input);
548     }
549
550   error = validate_stream (&s_new);
551   if (!error)
552     {
553       s[0] = s_new;
554       pg_stream_change (pg, s);
555     }
556
557   return error;
558 }
559
560 /* *INDENT-OFF* */
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,
565 };
566 /* *INDENT-ON* */
567
568 static clib_error_t *
569 pg_capture_cmd_fn (vlib_main_t * vm,
570                    unformat_input_t * input, vlib_cli_command_t * cmd)
571 {
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;
577   u32 hw_if_index;
578   u32 is_disable = 0;
579   u32 count = ~0;
580
581   if (!unformat_user (input, unformat_line_input, line_input))
582     return 0;
583
584   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
585     {
586       if (unformat (line_input, "%U",
587                     unformat_vnet_hw_interface, vnm, &hw_if_index))
588         {
589           hi = vnet_get_hw_interface (vnm, hw_if_index);
590         }
591
592       else if (unformat (line_input, "pcap %s", &pcap_file_name))
593         ;
594       else if (unformat (line_input, "count %u", &count))
595         ;
596       else if (unformat (line_input, "disable"))
597         is_disable = 1;
598
599       else
600         {
601           error = clib_error_create ("unknown input `%U'",
602                                      format_unformat_error, line_input);
603           goto done;
604         }
605     }
606
607   if (!hi)
608     {
609       error = clib_error_return (0, "Please specify interface name");
610       goto done;
611     }
612
613   if (hi->dev_class_index != pg_dev_class.index)
614     {
615       error =
616         clib_error_return (0, "Please specify packet-generator interface");
617       goto done;
618     }
619
620   if (!pcap_file_name && is_disable == 0)
621     {
622       error = clib_error_return (0, "Please specify pcap file name");
623       goto done;
624     }
625
626
627   pg_capture_args_t _a, *a = &_a;
628
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;
633   a->count = count;
634
635   error = pg_capture (a);
636
637 done:
638   unformat_free (line_input);
639
640   return error;
641 }
642
643 /* *INDENT-OFF* */
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,
648 };
649 /* *INDENT-ON* */
650
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)
654 {
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;
659
660   if (!unformat_user (input, unformat_line_input, line_input))
661     return 0;
662
663   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
664     {
665       if (unformat (line_input, "interface pg%u", &if_id))
666         ;
667       else if (unformat (line_input, "gso-enabled"))
668         {
669           gso_enabled = 1;
670           if (unformat (line_input, "gso-size %u", &gso_size))
671             ;
672           else
673             {
674               error = clib_error_create ("gso enabled but gso size missing");
675               goto done;
676             }
677         }
678       else
679         {
680           error = clib_error_create ("unknown input `%U'",
681                                      format_unformat_error, line_input);
682           goto done;
683         }
684     }
685
686   pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size);
687
688 done:
689   unformat_free (line_input);
690
691   return error;
692 }
693
694 /* *INDENT-OFF* */
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,
699 };
700 /* *INDENT-ON* */
701
702 /* Dummy init function so that we can be linked in. */
703 static clib_error_t *
704 pg_cli_init (vlib_main_t * vm)
705 {
706   return 0;
707 }
708
709 VLIB_INIT_FUNCTION (pg_cli_init);
710
711 /*
712  * fd.io coding-style-patch-verification: ON
713  *
714  * Local Variables:
715  * eval: (c-set-style "gnu")
716  * End:
717  */