]> git.proxmox.com Git - ovs.git/blob - utilities/ovs-dpctl.c
Rename static functions to increase uniqueness.
[ovs.git] / utilities / ovs-dpctl.c
1 /*
2 * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18 #include <arpa/inet.h>
19 #include <assert.h>
20 #include <errno.h>
21 #include <getopt.h>
22 #include <inttypes.h>
23 #include <sys/socket.h>
24 #include <net/if.h>
25 #include <netinet/in.h>
26 #include <signal.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33
34 #include "command-line.h"
35 #include "compiler.h"
36 #include "dirs.h"
37 #include "dpif.h"
38 #include "dynamic-string.h"
39 #include "flow.h"
40 #include "netdev.h"
41 #include "netlink.h"
42 #include "odp-util.h"
43 #include "ofpbuf.h"
44 #include "packets.h"
45 #include "shash.h"
46 #include "simap.h"
47 #include "smap.h"
48 #include "sset.h"
49 #include "timeval.h"
50 #include "util.h"
51 #include "vlog.h"
52
53 VLOG_DEFINE_THIS_MODULE(dpctl);
54
55 /* -s, --statistics: Print port statistics? */
56 static bool print_statistics;
57
58 /* -m, --more: Output verbosity.
59 *
60 * So far only undocumented commands honor this option, so we don't document
61 * the option itself. */
62 static int verbosity;
63
64 static const struct command all_commands[];
65
66 static void usage(void) NO_RETURN;
67 static void parse_options(int argc, char *argv[]);
68
69 int
70 main(int argc, char *argv[])
71 {
72 set_program_name(argv[0]);
73 parse_options(argc, argv);
74 signal(SIGPIPE, SIG_IGN);
75 run_command(argc - optind, argv + optind, all_commands);
76 return 0;
77 }
78
79 static void
80 parse_options(int argc, char *argv[])
81 {
82 enum {
83 OPT_DUMMY = UCHAR_MAX + 1,
84 VLOG_OPTION_ENUMS
85 };
86 static struct option long_options[] = {
87 {"statistics", no_argument, NULL, 's'},
88 {"more", no_argument, NULL, 'm'},
89 {"timeout", required_argument, NULL, 't'},
90 {"help", no_argument, NULL, 'h'},
91 {"version", no_argument, NULL, 'V'},
92 VLOG_LONG_OPTIONS,
93 {NULL, 0, NULL, 0},
94 };
95 char *short_options = long_options_to_short_options(long_options);
96
97 for (;;) {
98 unsigned long int timeout;
99 int c;
100
101 c = getopt_long(argc, argv, short_options, long_options, NULL);
102 if (c == -1) {
103 break;
104 }
105
106 switch (c) {
107 case 's':
108 print_statistics = true;
109 break;
110
111 case 'm':
112 verbosity++;
113 break;
114
115 case 't':
116 timeout = strtoul(optarg, NULL, 10);
117 if (timeout <= 0) {
118 ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
119 optarg);
120 } else {
121 time_alarm(timeout);
122 }
123 break;
124
125 case 'h':
126 usage();
127
128 case 'V':
129 ovs_print_version(0, 0);
130 exit(EXIT_SUCCESS);
131
132 VLOG_OPTION_HANDLERS
133
134 case '?':
135 exit(EXIT_FAILURE);
136
137 default:
138 abort();
139 }
140 }
141 free(short_options);
142 }
143
144 static void
145 usage(void)
146 {
147 printf("%s: Open vSwitch datapath management utility\n"
148 "usage: %s [OPTIONS] COMMAND [ARG...]\n"
149 " add-dp DP [IFACE...] add new datapath DP (with IFACEs)\n"
150 " del-dp DP delete local datapath DP\n"
151 " add-if DP IFACE... add each IFACE as a port on DP\n"
152 " set-if DP IFACE... reconfigure each IFACE within DP\n"
153 " del-if DP IFACE... delete each IFACE from DP\n"
154 " dump-dps display names of all datapaths\n"
155 " show show basic info on all datapaths\n"
156 " show DP... show basic info on each DP\n"
157 " dump-flows DP display flows in DP\n"
158 " del-flows DP delete all flows from DP\n"
159 "Each IFACE on add-dp, add-if, and set-if may be followed by\n"
160 "comma-separated options. See ovs-dpctl(8) for syntax, or the\n"
161 "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n",
162 program_name, program_name);
163 vlog_usage();
164 printf("\nOther options:\n"
165 " -t, --timeout=SECS give up after SECS seconds\n"
166 " -h, --help display this help message\n"
167 " -V, --version display version information\n");
168 exit(EXIT_SUCCESS);
169 }
170
171 static void run(int retval, const char *message, ...)
172 PRINTF_FORMAT(2, 3);
173
174 static void run(int retval, const char *message, ...)
175 {
176 if (retval) {
177 va_list args;
178
179 va_start(args, message);
180 ovs_fatal_valist(retval, message, args);
181 }
182 }
183 \f
184 static void dpctl_add_if(int argc, char *argv[]);
185
186 static int if_up(const char *netdev_name)
187 {
188 struct netdev *netdev;
189 int retval;
190
191 retval = netdev_open(netdev_name, "system", &netdev);
192 if (!retval) {
193 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
194 netdev_close(netdev);
195 }
196 return retval;
197 }
198
199 static int
200 parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
201 {
202 int result;
203 char *name, *type;
204
205 dp_parse_name(arg_, &name, &type);
206
207 if (create) {
208 result = dpif_create(name, type, dpifp);
209 } else {
210 result = dpif_open(name, type, dpifp);
211 }
212
213 free(name);
214 free(type);
215 return result;
216 }
217
218 static void
219 dpctl_add_dp(int argc OVS_UNUSED, char *argv[])
220 {
221 struct dpif *dpif;
222 run(parsed_dpif_open(argv[1], true, &dpif), "add_dp");
223 dpif_close(dpif);
224 if (argc > 2) {
225 dpctl_add_if(argc, argv);
226 }
227 }
228
229 static void
230 dpctl_del_dp(int argc OVS_UNUSED, char *argv[])
231 {
232 struct dpif *dpif;
233 run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
234 run(dpif_delete(dpif), "del_dp");
235 dpif_close(dpif);
236 }
237
238 static void
239 dpctl_add_if(int argc OVS_UNUSED, char *argv[])
240 {
241 bool failure = false;
242 struct dpif *dpif;
243 int i;
244
245 run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
246 for (i = 2; i < argc; i++) {
247 const char *name, *type;
248 char *save_ptr = NULL;
249 struct netdev *netdev = NULL;
250 struct smap args;
251 char *option;
252 int error;
253
254 name = strtok_r(argv[i], ",", &save_ptr);
255 type = "system";
256
257 if (!name) {
258 ovs_error(0, "%s is not a valid network device name", argv[i]);
259 failure = true;
260 continue;
261 }
262
263 smap_init(&args);
264 while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) {
265 char *save_ptr_2 = NULL;
266 char *key, *value;
267
268 key = strtok_r(option, "=", &save_ptr_2);
269 value = strtok_r(NULL, "", &save_ptr_2);
270 if (!value) {
271 value = "";
272 }
273
274 if (!strcmp(key, "type")) {
275 type = value;
276 } else if (!smap_add_once(&args, key, value)) {
277 ovs_error(0, "duplicate \"%s\" option", key);
278 }
279 }
280
281 error = netdev_open(name, type, &netdev);
282 if (error) {
283 ovs_error(error, "%s: failed to open network device", name);
284 goto next;
285 }
286
287 error = netdev_set_config(netdev, &args);
288 if (error) {
289 ovs_error(error, "%s: failed to configure network device", name);
290 goto next;
291 }
292
293 error = dpif_port_add(dpif, netdev, NULL);
294 if (error) {
295 ovs_error(error, "adding %s to %s failed", name, argv[1]);
296 goto next;
297 }
298
299 error = if_up(name);
300
301 next:
302 netdev_close(netdev);
303 if (error) {
304 failure = true;
305 }
306 }
307 dpif_close(dpif);
308 if (failure) {
309 exit(EXIT_FAILURE);
310 }
311 }
312
313 static void
314 dpctl_set_if(int argc, char *argv[])
315 {
316 bool failure = false;
317 struct dpif *dpif;
318 int i;
319
320 run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
321 for (i = 2; i < argc; i++) {
322 struct netdev *netdev = NULL;
323 struct dpif_port dpif_port;
324 char *save_ptr = NULL;
325 char *type = NULL;
326 const char *name;
327 struct smap args;
328 char *option;
329 int error;
330
331 name = strtok_r(argv[i], ",", &save_ptr);
332 if (!name) {
333 ovs_error(0, "%s is not a valid network device name", argv[i]);
334 failure = true;
335 continue;
336 }
337
338 /* Get the port's type from the datapath. */
339 error = dpif_port_query_by_name(dpif, name, &dpif_port);
340 if (error) {
341 ovs_error(error, "%s: failed to query port in %s", name, argv[1]);
342 goto next;
343 }
344 type = xstrdup(dpif_port.type);
345 dpif_port_destroy(&dpif_port);
346
347 /* Retrieve its existing configuration. */
348 error = netdev_open(name, type, &netdev);
349 if (error) {
350 ovs_error(error, "%s: failed to open network device", name);
351 goto next;
352 }
353
354 smap_init(&args);
355 error = netdev_get_config(netdev, &args);
356 if (error) {
357 ovs_error(error, "%s: failed to fetch configuration", name);
358 goto next;
359 }
360
361 /* Parse changes to configuration. */
362 while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) {
363 char *save_ptr_2 = NULL;
364 char *key, *value;
365
366 key = strtok_r(option, "=", &save_ptr_2);
367 value = strtok_r(NULL, "", &save_ptr_2);
368 if (!value) {
369 value = "";
370 }
371
372 if (!strcmp(key, "type")) {
373 if (strcmp(value, type)) {
374 ovs_error(0, "%s: can't change type from %s to %s",
375 name, type, value);
376 failure = true;
377 }
378 } else if (value[0] == '\0') {
379 smap_remove(&args, key);
380 } else {
381 smap_replace(&args, key, value);
382 }
383 }
384
385 /* Update configuration. */
386 error = netdev_set_config(netdev, &args);
387 smap_destroy(&args);
388 if (error) {
389 ovs_error(error, "%s: failed to configure network device", name);
390 goto next;
391 }
392
393 next:
394 free(type);
395 netdev_close(netdev);
396 if (error) {
397 failure = true;
398 }
399 }
400 dpif_close(dpif);
401 if (failure) {
402 exit(EXIT_FAILURE);
403 }
404 }
405
406 static bool
407 get_port_number(struct dpif *dpif, const char *name, uint16_t *port)
408 {
409 struct dpif_port dpif_port;
410
411 if (!dpif_port_query_by_name(dpif, name, &dpif_port)) {
412 *port = dpif_port.port_no;
413 dpif_port_destroy(&dpif_port);
414 return true;
415 } else {
416 ovs_error(0, "no port named %s", name);
417 return false;
418 }
419 }
420
421 static void
422 dpctl_del_if(int argc OVS_UNUSED, char *argv[])
423 {
424 bool failure = false;
425 struct dpif *dpif;
426 int i;
427
428 run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
429 for (i = 2; i < argc; i++) {
430 const char *name = argv[i];
431 uint16_t port;
432 int error;
433
434 if (!name[strspn(name, "0123456789")]) {
435 port = atoi(name);
436 } else if (!get_port_number(dpif, name, &port)) {
437 failure = true;
438 continue;
439 }
440
441 error = dpif_port_del(dpif, port);
442 if (error) {
443 ovs_error(error, "deleting port %s from %s failed", name, argv[1]);
444 failure = true;
445 }
446 }
447 dpif_close(dpif);
448 if (failure) {
449 exit(EXIT_FAILURE);
450 }
451 }
452
453 static void
454 print_stat(const char *leader, uint64_t value)
455 {
456 fputs(leader, stdout);
457 if (value != UINT64_MAX) {
458 printf("%"PRIu64, value);
459 } else {
460 putchar('?');
461 }
462 }
463
464 static void
465 print_human_size(uint64_t value)
466 {
467 if (value == UINT64_MAX) {
468 /* Nothing to do. */
469 } else if (value >= 1024ULL * 1024 * 1024 * 1024) {
470 printf(" (%.1f TiB)", value / (1024.0 * 1024 * 1024 * 1024));
471 } else if (value >= 1024ULL * 1024 * 1024) {
472 printf(" (%.1f GiB)", value / (1024.0 * 1024 * 1024));
473 } else if (value >= 1024ULL * 1024) {
474 printf(" (%.1f MiB)", value / (1024.0 * 1024));
475 } else if (value >= 1024) {
476 printf(" (%.1f KiB)", value / 1024.0);
477 }
478 }
479
480 static void
481 show_dpif(struct dpif *dpif)
482 {
483 struct dpif_port_dump dump;
484 struct dpif_port dpif_port;
485 struct dpif_dp_stats stats;
486 struct netdev *netdev;
487
488 printf("%s:\n", dpif_name(dpif));
489 if (!dpif_get_dp_stats(dpif, &stats)) {
490 printf("\tlookups: hit:%"PRIu64" missed:%"PRIu64" lost:%"PRIu64"\n"
491 "\tflows: %"PRIu64"\n",
492 stats.n_hit, stats.n_missed, stats.n_lost, stats.n_flows);
493 }
494 DPIF_PORT_FOR_EACH (&dpif_port, &dump, dpif) {
495 printf("\tport %u: %s", dpif_port.port_no, dpif_port.name);
496
497 if (strcmp(dpif_port.type, "system")) {
498 int error;
499
500 printf (" (%s", dpif_port.type);
501
502 error = netdev_open(dpif_port.name, dpif_port.type, &netdev);
503 if (!error) {
504 struct smap config;
505
506 smap_init(&config);
507 error = netdev_get_config(netdev, &config);
508 if (!error) {
509 const struct smap_node **nodes;
510 size_t i;
511
512 nodes = smap_sort(&config);
513 for (i = 0; i < smap_count(&config); i++) {
514 const struct smap_node *node = nodes[i];
515 printf("%c %s=%s", i ? ',' : ':', node->key,
516 node->value);
517 }
518 free(nodes);
519 } else {
520 printf(", could not retrieve configuration (%s)",
521 strerror(error));
522 }
523 smap_destroy(&config);
524
525 netdev_close(netdev);
526 } else {
527 printf(": open failed (%s)", strerror(error));
528 }
529 putchar(')');
530 }
531 putchar('\n');
532
533 if (print_statistics) {
534 struct netdev_stats s;
535 int error;
536
537 error = netdev_open(dpif_port.name, dpif_port.type, &netdev);
538 if (error) {
539 printf(", open failed (%s)", strerror(error));
540 continue;
541 }
542 error = netdev_get_stats(netdev, &s);
543 if (error) {
544 printf(", could not retrieve stats (%s)", strerror(error));
545 continue;
546 }
547
548 netdev_close(netdev);
549 print_stat("\t\tRX packets:", s.rx_packets);
550 print_stat(" errors:", s.rx_errors);
551 print_stat(" dropped:", s.rx_dropped);
552 print_stat(" overruns:", s.rx_over_errors);
553 print_stat(" frame:", s.rx_frame_errors);
554 printf("\n");
555
556 print_stat("\t\tTX packets:", s.tx_packets);
557 print_stat(" errors:", s.tx_errors);
558 print_stat(" dropped:", s.tx_dropped);
559 print_stat(" aborted:", s.tx_aborted_errors);
560 print_stat(" carrier:", s.tx_carrier_errors);
561 printf("\n");
562
563 print_stat("\t\tcollisions:", s.collisions);
564 printf("\n");
565
566 print_stat("\t\tRX bytes:", s.rx_bytes);
567 print_human_size(s.rx_bytes);
568 print_stat(" TX bytes:", s.tx_bytes);
569 print_human_size(s.tx_bytes);
570 printf("\n");
571 }
572 }
573 dpif_close(dpif);
574 }
575
576 static void
577 dpctl_show(int argc, char *argv[])
578 {
579 bool failure = false;
580 if (argc > 1) {
581 int i;
582 for (i = 1; i < argc; i++) {
583 const char *name = argv[i];
584 struct dpif *dpif;
585 int error;
586
587 error = parsed_dpif_open(name, false, &dpif);
588 if (!error) {
589 show_dpif(dpif);
590 } else {
591 ovs_error(error, "opening datapath %s failed", name);
592 failure = true;
593 }
594 }
595 } else {
596 struct sset types;
597 const char *type;
598
599 sset_init(&types);
600 dp_enumerate_types(&types);
601 SSET_FOR_EACH (type, &types) {
602 struct sset names;
603 const char *name;
604
605 sset_init(&names);
606 if (dp_enumerate_names(type, &names)) {
607 failure = true;
608 continue;
609 }
610 SSET_FOR_EACH (name, &names) {
611 struct dpif *dpif;
612 int error;
613
614 error = dpif_open(name, type, &dpif);
615 if (!error) {
616 show_dpif(dpif);
617 } else {
618 ovs_error(error, "opening datapath %s failed", name);
619 failure = true;
620 }
621 }
622 sset_destroy(&names);
623 }
624 sset_destroy(&types);
625 }
626 if (failure) {
627 exit(EXIT_FAILURE);
628 }
629 }
630
631 static void
632 dpctl_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
633 {
634 struct sset dpif_names, dpif_types;
635 const char *type;
636 int error = 0;
637
638 sset_init(&dpif_names);
639 sset_init(&dpif_types);
640 dp_enumerate_types(&dpif_types);
641
642 SSET_FOR_EACH (type, &dpif_types) {
643 const char *name;
644 int retval;
645
646 retval = dp_enumerate_names(type, &dpif_names);
647 if (retval) {
648 error = retval;
649 }
650
651 SSET_FOR_EACH (name, &dpif_names) {
652 struct dpif *dpif;
653 if (!dpif_open(name, type, &dpif)) {
654 printf("%s\n", dpif_name(dpif));
655 dpif_close(dpif);
656 }
657 }
658 }
659
660 sset_destroy(&dpif_names);
661 sset_destroy(&dpif_types);
662 if (error) {
663 exit(EXIT_FAILURE);
664 }
665 }
666
667 static void
668 dpctl_dump_flows(int argc OVS_UNUSED, char *argv[])
669 {
670 const struct dpif_flow_stats *stats;
671 const struct nlattr *actions;
672 struct dpif_flow_dump dump;
673 const struct nlattr *key;
674 size_t actions_len;
675 struct dpif *dpif;
676 size_t key_len;
677 struct ds ds;
678
679 run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
680
681 ds_init(&ds);
682 dpif_flow_dump_start(&dump, dpif);
683 while (dpif_flow_dump_next(&dump, &key, &key_len,
684 &actions, &actions_len, &stats)) {
685 ds_clear(&ds);
686 odp_flow_key_format(key, key_len, &ds);
687 ds_put_cstr(&ds, ", ");
688 dpif_flow_stats_format(stats, &ds);
689 ds_put_cstr(&ds, ", actions:");
690 format_odp_actions(&ds, actions, actions_len);
691 printf("%s\n", ds_cstr(&ds));
692 }
693 dpif_flow_dump_done(&dump);
694 ds_destroy(&ds);
695 dpif_close(dpif);
696 }
697
698 static void
699 dpctl_del_flows(int argc OVS_UNUSED, char *argv[])
700 {
701 struct dpif *dpif;
702
703 run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
704 run(dpif_flow_flush(dpif), "deleting all flows");
705 dpif_close(dpif);
706 }
707
708 static void
709 dpctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
710 {
711 usage();
712 }
713 \f
714 /* Undocumented commands for unit testing. */
715
716 static void
717 dpctl_parse_actions(int argc, char *argv[])
718 {
719 int i;
720
721 for (i = 1; i < argc; i++) {
722 struct ofpbuf actions;
723 struct ds s;
724
725 ofpbuf_init(&actions, 0);
726 run(odp_actions_from_string(argv[i], NULL, &actions),
727 "odp_actions_from_string");
728
729 ds_init(&s);
730 format_odp_actions(&s, actions.data, actions.size);
731 puts(ds_cstr(&s));
732 ds_destroy(&s);
733
734 ofpbuf_uninit(&actions);
735 }
736 }
737
738 struct actions_for_flow {
739 struct hmap_node hmap_node;
740 struct flow flow;
741 struct ofpbuf actions;
742 };
743
744 static struct actions_for_flow *
745 get_actions_for_flow(struct hmap *actions_per_flow, const struct flow *flow)
746 {
747 uint32_t hash = flow_hash(flow, 0);
748 struct actions_for_flow *af;
749
750 HMAP_FOR_EACH_WITH_HASH (af, hmap_node, hash, actions_per_flow) {
751 if (flow_equal(&af->flow, flow)) {
752 return af;
753 }
754 }
755
756 af = xmalloc(sizeof *af);
757 af->flow = *flow;
758 ofpbuf_init(&af->actions, 0);
759 hmap_insert(actions_per_flow, &af->hmap_node, hash);
760 return af;
761 }
762
763 static int
764 compare_actions_for_flow(const void *a_, const void *b_)
765 {
766 struct actions_for_flow *const *a = a_;
767 struct actions_for_flow *const *b = b_;
768
769 return flow_compare_3way(&(*a)->flow, &(*b)->flow);
770 }
771
772 static int
773 compare_output_actions(const void *a_, const void *b_)
774 {
775 const struct nlattr *a = a_;
776 const struct nlattr *b = b_;
777 uint32_t a_port = nl_attr_get_u32(a);
778 uint32_t b_port = nl_attr_get_u32(b);
779
780 return a_port < b_port ? -1 : a_port > b_port;
781 }
782
783 static void
784 sort_output_actions__(struct nlattr *first, struct nlattr *end)
785 {
786 size_t bytes = (uint8_t *) end - (uint8_t *) first;
787 size_t n = bytes / NL_A_U32_SIZE;
788
789 assert(bytes % NL_A_U32_SIZE == 0);
790 qsort(first, n, NL_A_U32_SIZE, compare_output_actions);
791 }
792
793 static void
794 sort_output_actions(struct nlattr *actions, size_t length)
795 {
796 struct nlattr *first_output = NULL;
797 struct nlattr *a;
798 int left;
799
800 NL_ATTR_FOR_EACH (a, left, actions, length) {
801 if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT) {
802 if (!first_output) {
803 first_output = a;
804 }
805 } else {
806 if (first_output) {
807 sort_output_actions__(first_output, a);
808 first_output = NULL;
809 }
810 }
811 }
812 if (first_output) {
813 uint8_t *end = (uint8_t *) actions + length;
814 sort_output_actions__(first_output, (struct nlattr *) end);
815 }
816 }
817
818 /* usage: "ovs-dpctl normalize-actions FLOW ACTIONS" where FLOW and ACTIONS
819 * have the syntax used by "ovs-dpctl dump-flows".
820 *
821 * This command prints ACTIONS in a format that shows what happens for each
822 * VLAN, independent of the order of the ACTIONS. For example, there is more
823 * than one way to output a packet on VLANs 9 and 11, but this command will
824 * print the same output for any form.
825 *
826 * The idea here generalizes beyond VLANs (e.g. to setting other fields) but
827 * so far the implementation only covers VLANs. */
828 static void
829 dpctl_normalize_actions(int argc, char *argv[])
830 {
831 struct simap port_names;
832 struct ofpbuf keybuf;
833 struct flow flow;
834 struct ofpbuf odp_actions;
835 struct hmap actions_per_flow;
836 struct actions_for_flow **afs;
837 struct actions_for_flow *af;
838 struct nlattr *a;
839 size_t n_afs;
840 struct ds s;
841 int left;
842 int i;
843
844 ds_init(&s);
845
846 simap_init(&port_names);
847 for (i = 3; i < argc; i++) {
848 char name[16];
849 int number;
850 int n = -1;
851
852 if (sscanf(argv[i], "%15[^=]=%d%n", name, &number, &n) > 0 && n > 0) {
853 uintptr_t n = number;
854 simap_put(&port_names, name, n);
855 } else {
856 ovs_fatal(0, "%s: expected NAME=NUMBER", argv[i]);
857 }
858 }
859
860 /* Parse flow key. */
861 ofpbuf_init(&keybuf, 0);
862 run(odp_flow_key_from_string(argv[1], &port_names, &keybuf),
863 "odp_flow_key_from_string");
864
865 ds_clear(&s);
866 odp_flow_key_format(keybuf.data, keybuf.size, &s);
867 printf("input flow: %s\n", ds_cstr(&s));
868
869 run(odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow),
870 "odp_flow_key_to_flow");
871 ofpbuf_uninit(&keybuf);
872
873 /* Parse actions. */
874 ofpbuf_init(&odp_actions, 0);
875 run(odp_actions_from_string(argv[2], &port_names, &odp_actions),
876 "odp_actions_from_string");
877
878 if (verbosity) {
879 ds_clear(&s);
880 format_odp_actions(&s, odp_actions.data, odp_actions.size);
881 printf("input actions: %s\n", ds_cstr(&s));
882 }
883
884 hmap_init(&actions_per_flow);
885 NL_ATTR_FOR_EACH (a, left, odp_actions.data, odp_actions.size) {
886 if (nl_attr_type(a) == OVS_ACTION_ATTR_POP_VLAN) {
887 flow.vlan_tci = htons(0);
888 continue;
889 }
890
891 if (nl_attr_type(a) == OVS_ACTION_ATTR_PUSH_VLAN) {
892 const struct ovs_action_push_vlan *push;
893
894 push = nl_attr_get_unspec(a, sizeof *push);
895 flow.vlan_tci = push->vlan_tci;
896 continue;
897 }
898
899 af = get_actions_for_flow(&actions_per_flow, &flow);
900 nl_msg_put_unspec(&af->actions, nl_attr_type(a),
901 nl_attr_get(a), nl_attr_get_size(a));
902 }
903
904 n_afs = hmap_count(&actions_per_flow);
905 afs = xmalloc(n_afs * sizeof *afs);
906 i = 0;
907 HMAP_FOR_EACH (af, hmap_node, &actions_per_flow) {
908 afs[i++] = af;
909 }
910 assert(i == n_afs);
911
912 qsort(afs, n_afs, sizeof *afs, compare_actions_for_flow);
913
914 for (i = 0; i < n_afs; i++) {
915 const struct actions_for_flow *af = afs[i];
916
917 sort_output_actions(af->actions.data, af->actions.size);
918
919 if (af->flow.vlan_tci != htons(0)) {
920 printf("vlan(vid=%"PRIu16",pcp=%d): ",
921 vlan_tci_to_vid(af->flow.vlan_tci),
922 vlan_tci_to_pcp(af->flow.vlan_tci));
923 } else {
924 printf("no vlan: ");
925 }
926
927 ds_clear(&s);
928 format_odp_actions(&s, af->actions.data, af->actions.size);
929 puts(ds_cstr(&s));
930 }
931 ds_destroy(&s);
932 }
933
934 static const struct command all_commands[] = {
935 { "add-dp", 1, INT_MAX, dpctl_add_dp },
936 { "del-dp", 1, 1, dpctl_del_dp },
937 { "add-if", 2, INT_MAX, dpctl_add_if },
938 { "del-if", 2, INT_MAX, dpctl_del_if },
939 { "set-if", 2, INT_MAX, dpctl_set_if },
940 { "dump-dps", 0, 0, dpctl_dump_dps },
941 { "show", 0, INT_MAX, dpctl_show },
942 { "dump-flows", 1, 1, dpctl_dump_flows },
943 { "del-flows", 1, 1, dpctl_del_flows },
944 { "help", 0, INT_MAX, dpctl_help },
945
946 /* Undocumented commands for testing. */
947 { "parse-actions", 1, INT_MAX, dpctl_parse_actions },
948 { "normalize-actions", 2, INT_MAX, dpctl_normalize_actions },
949
950 { NULL, 0, 0, NULL },
951 };