ethernet: check destination mac for L3 in ethernet-input node
[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 VLIB_CLI_COMMAND (vlib_cli_pg_command, static) = {
51   .path = "packet-generator",
52   .short_help = "Packet generator commands",
53 };
54
55 void
56 pg_enable_disable (u32 stream_index, int is_enable)
57 {
58   pg_main_t *pg = &pg_main;
59   pg_stream_t *s;
60
61   if (stream_index == ~0)
62     {
63       /* No stream specified: enable/disable all streams. */
64         pool_foreach (s, pg->streams)  {
65             pg_stream_enable_disable (pg, s, is_enable);
66         }
67     }
68   else
69     {
70       /* enable/disable specified stream. */
71       s = pool_elt_at_index (pg->streams, stream_index);
72       pg_stream_enable_disable (pg, s, is_enable);
73     }
74 }
75
76 clib_error_t *
77 pg_capture (pg_capture_args_t * a)
78 {
79   pg_main_t *pg = &pg_main;
80   pg_interface_t *pi;
81
82   if (a->is_enabled == 1)
83     {
84       struct stat sb;
85       if (stat (a->pcap_file_name, &sb) != -1)
86         return clib_error_return (0, "pcap file '%s' already exists.",
87                                   a->pcap_file_name);
88     }
89
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;
96
97   if (a->is_enabled == 0)
98     return 0;
99
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;
104
105   return 0;
106 }
107
108 static clib_error_t *
109 enable_disable_stream (vlib_main_t * vm,
110                        unformat_input_t * input, vlib_cli_command_t * cmd)
111 {
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;
116
117   if (!unformat_user (input, unformat_line_input, line_input))
118     goto doit;
119
120   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
121     {
122       if (unformat (line_input, "%U", unformat_hash_vec_string,
123                     pg->stream_index_by_name, &stream_index))
124         ;
125       else
126         return clib_error_create ("unknown input `%U'",
127                                   format_unformat_error, line_input);
128     }
129   unformat_free (line_input);
130
131 doit:
132   pg_enable_disable (stream_index, is_enable);
133
134   return 0;
135 }
136
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 */
142 };
143
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 */
149 };
150
151 static u8 *
152 format_pg_edit_group (u8 * s, va_list * va)
153 {
154   pg_edit_group_t *g = va_arg (*va, pg_edit_group_t *);
155
156   s =
157     format (s, "hdr-size %d, offset %d, ", g->n_packet_bytes,
158             g->start_byte_offset);
159   if (g->edit_function)
160     {
161       u8 *function_name;
162       u8 *junk_after_name;
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 != ' ')
167         junk_after_name++;
168       *junk_after_name = 0;
169       s = format (s, "edit-function %s, ", function_name);
170       vec_free (function_name);
171     }
172
173   return s;
174 }
175
176 static u8 *
177 format_pg_stream (u8 * s, va_list * va)
178 {
179   pg_stream_t *t = va_arg (*va, pg_stream_t *);
180   int verbose = va_arg (*va, int);
181
182   if (!t)
183     return format (s, "%-16s%=12s%=16s%s",
184                    "Name", "Enabled", "Count", "Parameters");
185
186   s = format (s, "%-16v%=12s%=16Ld",
187               t->name,
188               pg_stream_is_enabled (t) ? "Yes" : "No",
189               t->n_packets_generated);
190
191   int indent = format_get_indent (s);
192
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, ",
196               t->min_packet_bytes,
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);
201
202   if (verbose)
203     {
204       pg_edit_group_t *g;
205   vec_foreach (g, t->edit_groups)
206     {
207       s = format (s, "\n%U%U", format_white_space, indent, format_pg_edit_group, g);
208     }
209     }
210
211   return s;
212 }
213
214 static clib_error_t *
215 show_streams (vlib_main_t * vm,
216               unformat_input_t * input, vlib_cli_command_t * cmd)
217 {
218   pg_main_t *pg = &pg_main;
219   pg_stream_t *s;
220   int verbose = 0;
221
222   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
223     {
224       if (unformat (input, "verbose"))
225         verbose = 1;
226       else
227         break;
228     }
229
230   if (pool_elts (pg->streams) == 0)
231     {
232       vlib_cli_output (vm, "no streams currently defined");
233       goto done;
234     }
235
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);
239     }
240
241 done:
242   return 0;
243 }
244
245 VLIB_CLI_COMMAND (show_streams_cli, static) = {
246   .path = "show packet-generator ",
247   .short_help = "show packet-generator [verbose]",
248   .function = show_streams,
249 };
250
251 static clib_error_t *
252 pg_pcap_read (pg_stream_t * s, char *file_name)
253 {
254 #ifndef CLIB_UNIX
255   return clib_error_return (0, "no pcap support");
256 #else
257   pcap_main_t pm;
258   clib_error_t *error;
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;
267
268   if (s->n_packets_limit == 0)
269     s->n_packets_limit = vec_len (pm.packets_read);
270
271   return error;
272 #endif /* CLIB_UNIX */
273 }
274
275 static uword
276 unformat_pg_stream_parameter (unformat_input_t * input, va_list * args)
277 {
278   pg_stream_t *s = va_arg (*args, pg_stream_t *);
279   f64 x;
280
281   if (unformat (input, "limit %f", &x))
282     s->n_packets_limit = x;
283
284   else if (unformat (input, "rate %f", &x))
285     s->rate_packets_per_second = x;
286
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;
290
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;
294
295   else if (unformat (input, "buffer-size %d", &s->buffer_bytes))
296     ;
297
298   else
299     return 0;
300
301   return 1;
302 }
303
304 static clib_error_t *
305 validate_stream (pg_stream_t * s)
306 {
307   if (s->max_packet_bytes < s->min_packet_bytes)
308     return clib_error_create ("max-size < min-size");
309
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");
315
316   if (s->rate_packets_per_second < 0)
317     return clib_error_create ("negative rate");
318
319   return 0;
320 }
321
322 const char *
323 pg_interface_get_input_node (pg_interface_t *pi)
324 {
325   switch (pi->mode)
326     {
327     case PG_MODE_ETHERNET:
328       return ("ethernet-input");
329     case PG_MODE_IP4:
330       return ("ip4-input");
331     case PG_MODE_IP6:
332       return ("ip6-input");
333     }
334
335   ASSERT (0);
336   return ("ethernet-input");
337 }
338
339 static clib_error_t *
340 new_stream (vlib_main_t * vm,
341             unformat_input_t * input, vlib_cli_command_t * cmd)
342 {
343   clib_error_t *error = 0;
344   u8 *tmp = 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;
352
353   s.sw_if_index[VLIB_RX] = s.sw_if_index[VLIB_TX] = ~0;
354   s.node_index = ~0;
355   s.max_packet_bytes = s.min_packet_bytes = 64;
356   s.buffer_bytes = vlib_buffer_get_default_data_size (vm);
357   s.if_id = ~0;
358   s.n_max_frame = VLIB_FRAME_SIZE;
359   pcap_file_name = 0;
360
361   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
362     {
363       if (unformat (input, "name %v", &tmp))
364         {
365           if (s.name)
366             vec_free (s.name);
367           s.name = tmp;
368         }
369
370       else if (unformat (input, "node %U",
371                          unformat_vnet_hw_interface, vnm, &hw_if_index))
372         {
373           vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
374
375           s.node_index = hi->output_node_index;
376           s.sw_if_index[VLIB_TX] = hi->sw_if_index;
377         }
378
379       else if (unformat (input, "source pg%u", &s.if_id))
380         ;
381
382       else if (unformat (input, "buffer-flags %U",
383                          unformat_vnet_buffer_flags, &s.buffer_flags))
384         ;
385       else if (unformat (input, "buffer-offload-flags %U",
386                          unformat_vnet_buffer_offload_flags, &s.buffer_oflags))
387         ;
388       else if (unformat (input, "node %U",
389                          unformat_vlib_node, vm, &s.node_index))
390         ;
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))
394         ;
395
396       else if (unformat (input, "interface %U",
397                          unformat_vnet_sw_interface, vnm,
398                          &s.sw_if_index[VLIB_RX]))
399         ;
400       else if (unformat (input, "tx-interface %U",
401                          unformat_vnet_sw_interface, vnm,
402                          &s.sw_if_index[VLIB_TX]))
403         ;
404
405       else if (unformat (input, "pcap %s", &pcap_file_name))
406         ;
407
408       else if (!sub_input_given
409                && unformat (input, "data %U", unformat_input, &sub_input))
410         sub_input_given++;
411
412       else if (unformat_user (input, unformat_pg_stream_parameter, &s))
413         ;
414
415       else
416         {
417           error = clib_error_create ("unknown input `%U'",
418                                      format_unformat_error, input);
419           goto done;
420         }
421     }
422
423   if (!sub_input_given && !pcap_file_name)
424     {
425       error = clib_error_create ("no packet data given");
426       goto done;
427     }
428
429   if (s.node_index == ~0)
430     {
431       if (pcap_file_name != 0)
432         {
433           vlib_node_t *n;
434
435           if (s.if_id != ~0)
436             n = vlib_get_node_by_name (vm, (u8 *) pg_interface_get_input_node (
437                                              &pg->interfaces[s.if_id]));
438           else
439             n = vlib_get_node_by_name (vm, (u8 *) "ethernet-input");
440           s.node_index = n->index;
441         }
442       else
443         {
444           error = clib_error_create ("output interface or node not given");
445           goto done;
446         }
447     }
448
449   {
450     pg_node_t *n;
451
452     if (s.node_index < vec_len (pg->nodes))
453       n = pg->nodes + s.node_index;
454     else
455       n = 0;
456
457     if (s.worker_index >= vlib_num_workers ())
458       s.worker_index = 0;
459
460     if (pcap_file_name != 0)
461       {
462         error = pg_pcap_read (&s, pcap_file_name);
463         if (error)
464           goto done;
465         vec_free (pcap_file_name);
466       }
467
468     else if (n && n->unformat_edit
469              && unformat_user (&sub_input, n->unformat_edit, &s))
470       ;
471
472     else if (!unformat_user (&sub_input, unformat_pg_payload, &s))
473       {
474         error = clib_error_create
475           ("failed to parse packet data from `%U'",
476            format_unformat_error, &sub_input);
477         goto done;
478       }
479   }
480
481   error = validate_stream (&s);
482   if (error)
483     return error;
484
485   pg_stream_add (pg, &s);
486   return 0;
487
488 done:
489   pg_stream_free (&s);
490   unformat_free (&sub_input);
491   return error;
492 }
493
494 VLIB_CLI_COMMAND (new_stream_cli, static) = {
495   .path = "packet-generator new",
496   .function = new_stream,
497   .short_help = "Create packet generator stream",
498   .long_help =
499   "Create packet generator stream\n"
500   "\n"
501   "Arguments:\n"
502   "\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",
510 };
511
512 static clib_error_t *
513 del_stream (vlib_main_t * vm,
514             unformat_input_t * input, vlib_cli_command_t * cmd)
515 {
516   pg_main_t *pg = &pg_main;
517   u32 i;
518
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);
523
524   pg_stream_del (pg, i);
525   return 0;
526 }
527
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",
532 };
533
534 static clib_error_t *
535 change_stream_parameters (vlib_main_t * vm,
536                           unformat_input_t * input, vlib_cli_command_t * cmd)
537 {
538   pg_main_t *pg = &pg_main;
539   pg_stream_t *s, s_new;
540   u32 stream_index = ~0;
541   clib_error_t *error;
542
543   if (unformat (input, "%U", unformat_hash_vec_string,
544                 pg->stream_index_by_name, &stream_index))
545     ;
546   else
547     return clib_error_create ("expecting stream name; got `%U'",
548                               format_unformat_error, input);
549
550   s = pool_elt_at_index (pg->streams, stream_index);
551   s_new = s[0];
552
553   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
554     {
555       if (unformat_user (input, unformat_pg_stream_parameter, &s_new))
556         ;
557
558       else
559         return clib_error_create ("unknown input `%U'",
560                                   format_unformat_error, input);
561     }
562
563   error = validate_stream (&s_new);
564   if (!error)
565     {
566       s[0] = s_new;
567       pg_stream_change (pg, s);
568     }
569
570   return error;
571 }
572
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,
577 };
578
579 static clib_error_t *
580 pg_capture_cmd_fn (vlib_main_t * vm,
581                    unformat_input_t * input, vlib_cli_command_t * cmd)
582 {
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;
588   u32 hw_if_index;
589   u32 is_disable = 0;
590   u32 count = ~0;
591
592   if (!unformat_user (input, unformat_line_input, line_input))
593     return 0;
594
595   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
596     {
597       if (unformat (line_input, "%U",
598                     unformat_vnet_hw_interface, vnm, &hw_if_index))
599         {
600           hi = vnet_get_hw_interface (vnm, hw_if_index);
601         }
602
603       else if (unformat (line_input, "pcap %s", &pcap_file_name))
604         ;
605       else if (unformat (line_input, "count %u", &count))
606         ;
607       else if (unformat (line_input, "disable"))
608         is_disable = 1;
609
610       else
611         {
612           error = clib_error_create ("unknown input `%U'",
613                                      format_unformat_error, line_input);
614           goto done;
615         }
616     }
617
618   if (!hi)
619     {
620       error = clib_error_return (0, "Please specify interface name");
621       goto done;
622     }
623
624   if (hi->dev_class_index != pg_dev_class.index)
625     {
626       error =
627         clib_error_return (0, "Please specify packet-generator interface");
628       goto done;
629     }
630
631   if (!pcap_file_name && is_disable == 0)
632     {
633       error = clib_error_return (0, "Please specify pcap file name");
634       goto done;
635     }
636
637
638   pg_capture_args_t _a, *a = &_a;
639
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;
644   a->count = count;
645
646   error = pg_capture (a);
647
648 done:
649   unformat_free (line_input);
650
651   return error;
652 }
653
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,
658 };
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 = ~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;
669
670   if (!unformat_user (input, unformat_line_input, line_input))
671     return 0;
672
673   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
674     {
675       if (unformat (line_input, "interface pg%u", &if_id))
676         ;
677       else if (unformat (line_input, "coalesce-enabled"))
678         coalesce_enabled = 1;
679       else if (unformat (line_input, "gso-enabled"))
680         {
681           gso_enabled = 1;
682           if (unformat (line_input, "gso-size %u", &gso_size))
683             ;
684           else
685             {
686               error = clib_error_create ("gso enabled but gso size missing");
687               goto done;
688             }
689         }
690       else if (unformat (line_input, "mode ip4"))
691         mode = PG_MODE_IP4;
692       else if (unformat (line_input, "mode ip6"))
693         mode = PG_MODE_IP6;
694       else
695         {
696           error = clib_error_create ("unknown input `%U'",
697                                      format_unformat_error, line_input);
698           goto done;
699         }
700     }
701
702   pg_interface_add_or_get (pg, if_id, gso_enabled, gso_size, coalesce_enabled,
703                            mode);
704
705 done:
706   unformat_free (line_input);
707
708   return error;
709 }
710
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,
717 };
718
719 /* Dummy init function so that we can be linked in. */
720 static clib_error_t *
721 pg_cli_init (vlib_main_t * vm)
722 {
723   return 0;
724 }
725
726 VLIB_INIT_FUNCTION (pg_cli_init);
727
728 /*
729  * fd.io coding-style-patch-verification: ON
730  *
731  * Local Variables:
732  * eval: (c-set-style "gnu")
733  * End:
734  */