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