vlib: refactor checksum offload support
[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       else if (unformat (input, "buffer-offload-flags %U",
383                          unformat_vnet_buffer_offload_flags, &s.buffer_oflags))
384         ;
385       else if (unformat (input, "node %U",
386                          unformat_vlib_node, vm, &s.node_index))
387         ;
388       else if (unformat (input, "maxframe %u", &maxframe))
389         s.n_max_frame = s.n_max_frame < maxframe ? s.n_max_frame : maxframe;
390       else if (unformat (input, "worker %u", &s.worker_index))
391         ;
392
393       else if (unformat (input, "interface %U",
394                          unformat_vnet_sw_interface, vnm,
395                          &s.sw_if_index[VLIB_RX]))
396         ;
397       else if (unformat (input, "tx-interface %U",
398                          unformat_vnet_sw_interface, vnm,
399                          &s.sw_if_index[VLIB_TX]))
400         ;
401
402       else if (unformat (input, "pcap %s", &pcap_file_name))
403         ;
404
405       else if (!sub_input_given
406                && unformat (input, "data %U", unformat_input, &sub_input))
407         sub_input_given++;
408
409       else if (unformat_user (input, unformat_pg_stream_parameter, &s))
410         ;
411
412       else
413         {
414           error = clib_error_create ("unknown input `%U'",
415                                      format_unformat_error, input);
416           goto done;
417         }
418     }
419
420   if (!sub_input_given && !pcap_file_name)
421     {
422       error = clib_error_create ("no packet data given");
423       goto done;
424     }
425
426   if (s.node_index == ~0)
427     {
428       if (pcap_file_name != 0)
429         {
430           vlib_node_t *n =
431             vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
432           s.node_index = n->index;
433         }
434       else
435         {
436           error = clib_error_create ("output interface or node not given");
437           goto done;
438         }
439     }
440
441   {
442     pg_node_t *n;
443
444     if (s.node_index < vec_len (pg->nodes))
445       n = pg->nodes + s.node_index;
446     else
447       n = 0;
448
449     if (s.worker_index >= vlib_num_workers ())
450       s.worker_index = 0;
451
452     if (pcap_file_name != 0)
453       {
454         error = pg_pcap_read (&s, pcap_file_name);
455         if (error)
456           goto done;
457         vec_free (pcap_file_name);
458       }
459
460     else if (n && n->unformat_edit
461              && unformat_user (&sub_input, n->unformat_edit, &s))
462       ;
463
464     else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
465       {
466         error = clib_error_create
467           ("failed to parse packet data from `%U'",
468            format_unformat_error, &sub_input);
469         goto done;
470       }
471   }
472
473   error = validate_stream (&s);
474   if (error)
475     return error;
476
477   pg_stream_add (pg, &s);
478   return 0;
479
480 done:
481   pg_stream_free (&s);
482   unformat_free (&sub_input);
483   return error;
484 }
485
486 /* *INDENT-OFF* */
487 VLIB_CLI_COMMAND (new_stream_cli, static) = {
488   .path = "packet-generator new",
489   .function = new_stream,
490   .short_help = "Create packet generator stream",
491   .long_help =
492   "Create packet generator stream\n"
493   "\n"
494   "Arguments:\n"
495   "\n"
496   "name STRING          sets stream name\n"
497   "interface STRING     interface for stream output \n"
498   "node NODE-NAME       node for stream output\n"
499   "data STRING          specifies packet data\n"
500   "pcap FILENAME        read packet data from pcap file\n"
501   "rate PPS             rate to transfer packet data\n"
502   "maxframe NPKTS       maximum number of packets per frame\n",
503 };
504 /* *INDENT-ON* */
505
506 static clib_error_t *
507 del_stream (vlib_main_t * vm,
508             unformat_input_t * input, vlib_cli_command_t * cmd)
509 {
510   pg_main_t *pg = &pg_main;
511   u32 i;
512
513   if (!unformat (input, "%U",
514                  &unformat_hash_vec_string, pg->stream_index_by_name, &i))
515     return clib_error_create ("expected stream name `%U'",
516                               format_unformat_error, input);
517
518   pg_stream_del (pg, i);
519   return 0;
520 }
521
522 /* *INDENT-OFF* */
523 VLIB_CLI_COMMAND (del_stream_cli, static) = {
524   .path = "packet-generator delete",
525   .function = del_stream,
526   .short_help = "Delete stream with given name",
527 };
528 /* *INDENT-ON* */
529
530 static clib_error_t *
531 change_stream_parameters (vlib_main_t * vm,
532                           unformat_input_t * input, vlib_cli_command_t * cmd)
533 {
534   pg_main_t *pg = &pg_main;
535   pg_stream_t *s, s_new;
536   u32 stream_index = ~0;
537   clib_error_t *error;
538
539   if (unformat (input, "%U", unformat_hash_vec_string,
540                 pg->stream_index_by_name, &stream_index))
541     ;
542   else
543     return clib_error_create ("expecting stream name; got `%U'",
544                               format_unformat_error, input);
545
546   s = pool_elt_at_index (pg->streams, stream_index);
547   s_new = s[0];
548
549   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
550     {
551       if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
552         ;
553
554       else
555         return clib_error_create ("unknown input `%U'",
556                                   format_unformat_error, input);
557     }
558
559   error = validate_stream (&s_new);
560   if (!error)
561     {
562       s[0] = s_new;
563       pg_stream_change (pg, s);
564     }
565
566   return error;
567 }
568
569 /* *INDENT-OFF* */
570 VLIB_CLI_COMMAND (change_stream_parameters_cli, static) = {
571   .path = "packet-generator configure",
572   .short_help = "Change packet generator stream parameters",
573   .function = change_stream_parameters,
574 };
575 /* *INDENT-ON* */
576
577 static clib_error_t *
578 pg_capture_cmd_fn (vlib_main_t * vm,
579                    unformat_input_t * input, vlib_cli_command_t * cmd)
580 {
581   clib_error_t *error = 0;
582   vnet_main_t *vnm = vnet_get_main ();
583   unformat_input_t _line_input, *line_input = &_line_input;
584   vnet_hw_interface_t *hi = 0;
585   u8 *pcap_file_name = 0;
586   u32 hw_if_index;
587   u32 is_disable = 0;
588   u32 count = ~0;
589
590   if (!unformat_user (input, unformat_line_input, line_input))
591     return 0;
592
593   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
594     {
595       if (unformat (line_input, "%U",
596                     unformat_vnet_hw_interface, vnm, &hw_if_index))
597         {
598           hi = vnet_get_hw_interface (vnm, hw_if_index);
599         }
600
601       else if (unformat (line_input, "pcap %s", &pcap_file_name))
602         ;
603       else if (unformat (line_input, "count %u", &count))
604         ;
605       else if (unformat (line_input, "disable"))
606         is_disable = 1;
607
608       else
609         {
610           error = clib_error_create ("unknown input `%U'",
611                                      format_unformat_error, line_input);
612           goto done;
613         }
614     }
615
616   if (!hi)
617     {
618       error = clib_error_return (0, "Please specify interface name");
619       goto done;
620     }
621
622   if (hi->dev_class_index != pg_dev_class.index)
623     {
624       error =
625         clib_error_return (0, "Please specify packet-generator interface");
626       goto done;
627     }
628
629   if (!pcap_file_name && is_disable == 0)
630     {
631       error = clib_error_return (0, "Please specify pcap file name");
632       goto done;
633     }
634
635
636   pg_capture_args_t _a, *a = &_a;
637
638   a->hw_if_index = hw_if_index;
639   a->dev_instance = hi->dev_instance;
640   a->is_enabled = !is_disable;
641   a->pcap_file_name = (char *) pcap_file_name;
642   a->count = count;
643
644   error = pg_capture (a);
645
646 done:
647   unformat_free (line_input);
648
649   return error;
650 }
651
652 /* *INDENT-OFF* */
653 VLIB_CLI_COMMAND (pg_capture_cmd, static) = {
654   .path = "packet-generator capture",
655   .short_help = "packet-generator capture <interface name> pcap <filename> [count <n>]",
656   .function = pg_capture_cmd_fn,
657 };
658 /* *INDENT-ON* */
659
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)
663 {
664   pg_main_t *pg = &pg_main;
665   unformat_input_t _line_input, *line_input = &_line_input;
666   u32 if_id, gso_enabled = 0, gso_size = 0, coalesce_enabled = 0;
667   clib_error_t *error = NULL;
668
669   if (!unformat_user (input, unformat_line_input, line_input))
670     return 0;
671
672   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
673     {
674       if (unformat (line_input, "interface pg%u", &if_id))
675         ;
676       else if (unformat (line_input, "gso-enabled"))
677         {
678           gso_enabled = 1;
679           if (unformat (line_input, "gso-size %u", &gso_size))
680             ;
681           else
682             {
683               error = clib_error_create ("gso enabled but gso size missing");
684               goto done;
685             }
686           if (unformat (line_input, "coalesce-enabled"))
687             coalesce_enabled = 1;
688         }
689       else
690         {
691           error = clib_error_create ("unknown input `%U'",
692                                      format_unformat_error, line_input);
693           goto done;
694         }
695     }
696
697   pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size,
698                            coalesce_enabled);
699
700 done:
701   unformat_free (line_input);
702
703   return error;
704 }
705
706 /* *INDENT-OFF* */
707 VLIB_CLI_COMMAND (create_pg_if_cmd, static) = {
708   .path = "create packet-generator",
709   .short_help = "create packet-generator interface <interface name>"
710                 " [gso-enabled gso-size <size> [coalesce-enabled]]",
711   .function = create_pg_if_cmd_fn,
712 };
713 /* *INDENT-ON* */
714
715 /* Dummy init function so that we can be linked in. */
716 static clib_error_t *
717 pg_cli_init (vlib_main_t * vm)
718 {
719   return 0;
720 }
721
722 VLIB_INIT_FUNCTION (pg_cli_init);
723
724 /*
725  * fd.io coding-style-patch-verification: ON
726  *
727  * Local Variables:
728  * eval: (c-set-style "gnu")
729  * End:
730  */