pg: set vnet buffer flags in pg streams
[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 (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   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;
100
101   if (a->is_enabled == 0)
102     return 0;
103
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;
108
109   return 0;
110 }
111
112 static clib_error_t *
113 enable_disable_stream (vlib_main_t * vm,
114                        unformat_input_t * input, vlib_cli_command_t * cmd)
115 {
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;
120
121   if (!unformat_user (input, unformat_line_input, line_input))
122     goto doit;
123
124   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
125     {
126       if (unformat (line_input, "%U", unformat_hash_vec_string,
127                     pg->stream_index_by_name, &stream_index))
128         ;
129       else
130         return clib_error_create ("unknown input `%U'",
131                                   format_unformat_error, line_input);
132     }
133   unformat_free (line_input);
134
135 doit:
136   pg_enable_disable (stream_index, is_enable);
137
138   return 0;
139 }
140
141 /* *INDENT-OFF* */
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 */
147 };
148 /* *INDENT-ON* */
149
150 /* *INDENT-OFF* */
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 */
156 };
157 /* *INDENT-ON* */
158
159 static u8 *
160 format_pg_edit_group (u8 * s, va_list * va)
161 {
162   pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
163
164   s =
165     format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
166             g->start_byte_offset);
167   if (g->edit_function)
168     {
169       u8 *function_name;
170       u8 *junk_after_name;
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 != ' ')
175         junk_after_name++;
176       *junk_after_name = 0;
177       s = format (s, "edit-function %s, ", function_name);
178       vec_free (function_name);
179     }
180
181   return s;
182 }
183
184 static u8 *
185 format_pg_stream (u8 * s, va_list * va)
186 {
187   pg_stream_t *t = va_arg (*va, pg_stream_t *);
188   int verbose = va_arg (*va, int);
189
190   if (!t)
191     return format (s, "%-16s%=12s%=16s%s",
192                    "Name", "Enabled", "Count", "Parameters");
193
194   s = format (s, "%-16v%=12s%=16Ld",
195               t->name,
196               pg_stream_is_enabled (t) ? "Yes" : "No",
197               t->n_packets_generated);
198
199   int indent = format_get_indent (s);
200
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, ",
204               t->min_packet_bytes,
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);
209
210   if (verbose)
211     {
212       pg_edit_group_t *g;
213   /* *INDENT-OFF* */
214   vec_foreach (g, t->edit_groups)
215     {
216       s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
217     }
218   /* *INDENT-ON* */
219     }
220
221   return s;
222 }
223
224 static clib_error_t *
225 show_streams (vlib_main_t * vm,
226               unformat_input_t * input, vlib_cli_command_t * cmd)
227 {
228   pg_main_t *pg = &pg_main;
229   pg_stream_t *s;
230   int verbose = 0;
231
232   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
233     {
234       if (unformat (input, "verbose"))
235         verbose = 1;
236       else
237         break;
238     }
239
240   if (pool_elts (pg->streams) == 0)
241     {
242       vlib_cli_output (vm, "no streams currently defined");
243       goto done;
244     }
245
246   vlib_cli_output (vm, "%U", format_pg_stream, 0, 0);
247   /* *INDENT-OFF* */
248   pool_foreach (s, pg->streams, ({
249       vlib_cli_output (vm, "%U", format_pg_stream, s, verbose);
250     }));
251   /* *INDENT-ON* */
252
253 done:
254   return 0;
255 }
256
257 /* *INDENT-OFF* */
258 VLIB_CLI_COMMAND (show_streams_cli, static) = {
259   .path = "show packet-generator ",
260   .short_help = "show packet-generator [verbose]",
261   .function = show_streams,
262 };
263 /* *INDENT-ON* */
264
265 static clib_error_t *
266 pg_pcap_read (pg_stream_t * s, char *file_name)
267 {
268 #ifndef CLIB_UNIX
269   return clib_error_return (0, "no pcap support");
270 #else
271   pcap_main_t pm;
272   clib_error_t *error;
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;
281
282   if (s->n_packets_limit == 0)
283     s->n_packets_limit = vec_len (pm.packets_read);
284
285   return error;
286 #endif /* CLIB_UNIX */
287 }
288
289 static uword
290 unformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
291 {
292   pg_stream_t *s = va_arg (*args, pg_stream_t *);
293   f64 x;
294
295   if (unformat (input, "limit %f", &x))
296     s->n_packets_limit = x;
297
298   else if (unformat (input, "rate %f", &x))
299     s->rate_packets_per_second = x;
300
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;
304
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;
308
309   else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
310     ;
311
312   else
313     return 0;
314
315   return 1;
316 }
317
318 static clib_error_t *
319 validate_stream (pg_stream_t * s)
320 {
321   if (s->max_packet_bytes < s->min_packet_bytes)
322     return clib_error_create ("max-size < min-size");
323
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");
329
330   if (s->rate_packets_per_second < 0)
331     return clib_error_create ("negative rate");
332
333   return 0;
334 }
335
336 static clib_error_t *
337 new_stream (vlib_main_t * vm,
338             unformat_input_t * input, vlib_cli_command_t * cmd)
339 {
340   clib_error_t *error = 0;
341   u8 *tmp = 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;
349
350   s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
351   s.node_index = ~0;
352   s.max_packet_bytes = s.min_packet_bytes = 64;
353   s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
354   s.if_id = 0;
355   s.n_max_frame = VLIB_FRAME_SIZE;
356   pcap_file_name = 0;
357
358   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
359     {
360       if (unformat (input, "name %v", &tmp))
361         {
362           if (s.name)
363             vec_free (s.name);
364           s.name = tmp;
365         }
366
367       else if (unformat (input, "node %U",
368                          unformat_vnet_hw_interface, vnm, &hw_if_index))
369         {
370           vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
371
372           s.node_index = hi->output_node_index;
373           s.sw_if_index[VLIB_TX] = hi->sw_if_index;
374         }
375
376       else if (unformat (input, "source pg%u", &s.if_id))
377         ;
378
379       else if (unformat (input, "buffer-flags %U",
380                          unformat_vnet_buffer_flags, &s.buffer_flags))
381         ;
382
383       else if (unformat (input, "node %U",
384                          unformat_vlib_node, vm, &s.node_index))
385         ;
386       else if (unformat (input, "maxframe %u", &maxframe))
387         s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
388       else if (unformat (input, "worker %u", &s.worker_index))
389         ;
390
391       else if (unformat (input, "interface %U",
392                          unformat_vnet_sw_interface, vnm,
393                          &s.sw_if_index[VLIB_RX]))
394         ;
395       else if (unformat (input, "tx-interface %U",
396                          unformat_vnet_sw_interface, vnm,
397                          &s.sw_if_index[VLIB_TX]))
398         ;
399
400       else if (unformat (input, "pcap %s", &pcap_file_name))
401         ;
402
403       else if (!sub_input_given
404                && unformat (input, "data %U", unformat_input, &sub_input))
405         sub_input_given++;
406
407       else if (unformat_user (input, unformat_pg_stream_parameter, &s))
408         ;
409
410       else
411         {
412           error = clib_error_create ("unknown input `%U'",
413                                      format_unformat_error, input);
414           goto done;
415         }
416     }
417
418   if (!sub_input_given && !pcap_file_name)
419     {
420       error = clib_error_create ("no packet data given");
421       goto done;
422     }
423
424   if (s.node_index == ~0)
425     {
426       if (pcap_file_name != 0)
427         {
428           vlib_node_t *n =
429             vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
430           s.node_index = n->index;
431         }
432       else
433         {
434           error = clib_error_create ("output interface or node not given");
435           goto done;
436         }
437     }
438
439   {
440     pg_node_t *n;
441
442     if (s.node_index < vec_len (pg->nodes))
443       n = pg->nodes + s.node_index;
444     else
445       n = 0;
446
447     if (s.worker_index >= vlib_num_workers ())
448       s.worker_index = 0;
449
450     if (pcap_file_name != 0)
451       {
452         error = pg_pcap_read (&s, pcap_file_name);
453         if (error)
454           goto done;
455         vec_free (pcap_file_name);
456       }
457
458     else if (n && n->unformat_edit
459              && unformat_user (&sub_input, n->unformat_edit, &s))
460       ;
461
462     else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
463       {
464         error = clib_error_create
465           ("failed to parse packet data from `%U'",
466            format_unformat_error, &sub_input);
467         goto done;
468       }
469   }
470
471   error = validate_stream (&s);
472   if (error)
473     return error;
474
475   pg_stream_add (pg, &s);
476   return 0;
477
478 done:
479   pg_stream_free (&s);
480   unformat_free (&sub_input);
481   return error;
482 }
483
484 /* *INDENT-OFF* */
485 VLIB_CLI_COMMAND (new_stream_cli, static) = {
486   .path = "packet-generator new",
487   .function = new_stream,
488   .short_help = "Create packet generator stream",
489   .long_help =
490   "Create packet generator stream\n"
491   "\n"
492   "Arguments:\n"
493   "\n"
494   "name STRING          sets stream name\n"
495   "interface STRING     interface for stream output \n"
496   "node NODE-NAME       node for stream output\n"
497   "data STRING          specifies packet data\n"
498   "pcap FILENAME        read packet data from pcap file\n"
499   "rate PPS             rate to transfer packet data\n"
500   "maxframe NPKTS       maximum number of packets per frame\n",
501 };
502 /* *INDENT-ON* */
503
504 static clib_error_t *
505 del_stream (vlib_main_t * vm,
506             unformat_input_t * input, vlib_cli_command_t * cmd)
507 {
508   pg_main_t *pg = &pg_main;
509   u32 i;
510
511   if (!unformat (input, "%U",
512                  &unformat_hash_vec_string, pg->stream_index_by_name, &i))
513     return clib_error_create ("expected stream name `%U'",
514                               format_unformat_error, input);
515
516   pg_stream_del (pg, i);
517   return 0;
518 }
519
520 /* *INDENT-OFF* */
521 VLIB_CLI_COMMAND (del_stream_cli, static) = {
522   .path = "packet-generator delete",
523   .function = del_stream,
524   .short_help = "Delete stream with given name",
525 };
526 /* *INDENT-ON* */
527
528 static clib_error_t *
529 change_stream_parameters (vlib_main_t * vm,
530                           unformat_input_t * input, vlib_cli_command_t * cmd)
531 {
532   pg_main_t *pg = &pg_main;
533   pg_stream_t *s, s_new;
534   u32 stream_index = ~0;
535   clib_error_t *error;
536
537   if (unformat (input, "%U", unformat_hash_vec_string,
538                 pg->stream_index_by_name, &stream_index))
539     ;
540   else
541     return clib_error_create ("expecting stream name; got `%U'",
542                               format_unformat_error, input);
543
544   s = pool_elt_at_index (pg->streams, stream_index);
545   s_new = s[0];
546
547   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
548     {
549       if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
550         ;
551
552       else
553         return clib_error_create ("unknown input `%U'",
554                                   format_unformat_error, input);
555     }
556
557   error = validate_stream (&s_new);
558   if (!error)
559     {
560       s[0] = s_new;
561       pg_stream_change (pg, s);
562     }
563
564   return error;
565 }
566
567 /* *INDENT-OFF* */
568 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
569   .path = "packet-generator configure",
570   .short_help = "Change packet generator stream parameters",
571   .function = change_stream_parameters,
572 };
573 /* *INDENT-ON* */
574
575 static clib_error_t *
576 pg_capture_cmd_fn (vlib_main_t * vm,
577                    unformat_input_t * input, vlib_cli_command_t * cmd)
578 {
579   clib_error_t *error = 0;
580   vnet_main_t *vnm = vnet_get_main ();
581   unformat_input_t _line_input, *line_input = &_line_input;
582   vnet_hw_interface_t *hi = 0;
583   u8 *pcap_file_name = 0;
584   u32 hw_if_index;
585   u32 is_disable = 0;
586   u32 count = ~0;
587
588   if (!unformat_user (input, unformat_line_input, line_input))
589     return 0;
590
591   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
592     {
593       if (unformat (line_input, "%U",
594                     unformat_vnet_hw_interface, vnm, &hw_if_index))
595         {
596           hi = vnet_get_hw_interface (vnm, hw_if_index);
597         }
598
599       else if (unformat (line_input, "pcap %s", &pcap_file_name))
600         ;
601       else if (unformat (line_input, "count %u", &count))
602         ;
603       else if (unformat (line_input, "disable"))
604         is_disable = 1;
605
606       else
607         {
608           error = clib_error_create ("unknown input `%U'",
609                                      format_unformat_error, line_input);
610           goto done;
611         }
612     }
613
614   if (!hi)
615     {
616       error = clib_error_return (0, "Please specify interface name");
617       goto done;
618     }
619
620   if (hi->dev_class_index != pg_dev_class.index)
621     {
622       error =
623         clib_error_return (0, "Please specify packet-generator interface");
624       goto done;
625     }
626
627   if (!pcap_file_name && is_disable == 0)
628     {
629       error = clib_error_return (0, "Please specify pcap file name");
630       goto done;
631     }
632
633
634   pg_capture_args_t _a, *a = &_a;
635
636   a->hw_if_index = hw_if_index;
637   a->dev_instance = hi->dev_instance;
638   a->is_enabled = !is_disable;
639   a->pcap_file_name = (char *) pcap_file_name;
640   a->count = count;
641
642   error = pg_capture (a);
643
644 done:
645   unformat_free (line_input);
646
647   return error;
648 }
649
650 /* *INDENT-OFF* */
651 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
652   .path = "packet-generator capture",
653   .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
654   .function = pg_capture_cmd_fn,
655 };
656 /* *INDENT-ON* */
657
658 static clib_error_t *
659 create_pg_if_cmd_fn (vlib_main_t * vm,
660                      unformat_input_t * input, vlib_cli_command_t * cmd)
661 {
662   pg_main_t *pg = &pg_main;
663   unformat_input_t _line_input, *line_input = &_line_input;
664   u32 if_id, gso_enabled = 0, gso_size = 0;
665   clib_error_t *error = NULL;
666
667   if (!unformat_user (input, unformat_line_input, line_input))
668     return 0;
669
670   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
671     {
672       if (unformat (line_input, "interface pg%u", &if_id))
673         ;
674       else if (unformat (line_input, "gso-enabled"))
675         {
676           gso_enabled = 1;
677           if (unformat (line_input, "gso-size %u", &gso_size))
678             ;
679           else
680             {
681               error = clib_error_create ("gso enabled but gso size missing");
682               goto done;
683             }
684         }
685       else
686         {
687           error = clib_error_create ("unknown input `%U'",
688                                      format_unformat_error, line_input);
689           goto done;
690         }
691     }
692
693   pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size);
694
695 done:
696   unformat_free (line_input);
697
698   return error;
699 }
700
701 /* *INDENT-OFF* */
702 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
703   .path = "create packet-generator",
704   .short_help = "create packet-generator interface <interface name> [gso-enabled gso-size <size>]",
705   .function = create_pg_if_cmd_fn,
706 };
707 /* *INDENT-ON* */
708
709 /* Dummy init function so that we can be linked in. */
710 static clib_error_t *
711 pg_cli_init (vlib_main_t * vm)
712 {
713   return 0;
714 }
715
716 VLIB_INIT_FUNCTION (pg_cli_init);
717
718 /*
719  * fd.io coding-style-patch-verification: ON
720  *
721  * Local Variables:
722  * eval: (c-set-style "gnu")
723  * End:
724  */