]>
Commit | Line | Data |
---|---|---|
064af421 BP |
1 | /* |
2 | * Copyright (c) 2008, 2009 Nicira Networks. | |
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> | |
19 | #include <errno.h> | |
20 | #include <getopt.h> | |
21 | #include <inttypes.h> | |
22 | #include <net/if.h> | |
23 | #include <netinet/in.h> | |
24 | #include <signal.h> | |
25 | #include <stdarg.h> | |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <unistd.h> | |
29 | #include <sys/stat.h> | |
30 | #include <sys/time.h> | |
31 | ||
32 | #include "command-line.h" | |
33 | #include "compiler.h" | |
34 | #include "dirs.h" | |
35 | #include "dpif.h" | |
36 | #include "dynamic-string.h" | |
37 | #include "netdev.h" | |
38 | #include "odp-util.h" | |
b566902b | 39 | #include "svec.h" |
064af421 BP |
40 | #include "timeval.h" |
41 | #include "util.h" | |
42 | ||
43 | #include "vlog.h" | |
44 | #define THIS_MODULE VLM_dpctl | |
45 | ||
46 | struct command { | |
47 | const char *name; | |
48 | int min_args; | |
49 | int max_args; | |
50 | void (*handler)(int argc, char *argv[]); | |
51 | }; | |
52 | ||
53 | static struct command all_commands[]; | |
54 | ||
55 | static void usage(void) NO_RETURN; | |
56 | static void parse_options(int argc, char *argv[]); | |
57 | ||
58 | int main(int argc, char *argv[]) | |
59 | { | |
60 | struct command *p; | |
61 | ||
62 | set_program_name(argv[0]); | |
63 | time_init(); | |
64 | vlog_init(); | |
65 | parse_options(argc, argv); | |
66 | signal(SIGPIPE, SIG_IGN); | |
67 | ||
68 | argc -= optind; | |
69 | argv += optind; | |
70 | if (argc < 1) | |
71 | ovs_fatal(0, "missing command name; use --help for help"); | |
72 | ||
73 | for (p = all_commands; p->name != NULL; p++) { | |
74 | if (!strcmp(p->name, argv[0])) { | |
75 | int n_arg = argc - 1; | |
76 | if (n_arg < p->min_args) | |
77 | ovs_fatal(0, "'%s' command requires at least %d arguments", | |
78 | p->name, p->min_args); | |
79 | else if (n_arg > p->max_args) | |
80 | ovs_fatal(0, "'%s' command takes at most %d arguments", | |
81 | p->name, p->max_args); | |
82 | else { | |
83 | p->handler(argc, argv); | |
84 | if (ferror(stdout)) { | |
85 | ovs_fatal(0, "write to stdout failed"); | |
86 | } | |
87 | if (ferror(stderr)) { | |
88 | ovs_fatal(0, "write to stderr failed"); | |
89 | } | |
90 | exit(0); | |
91 | } | |
92 | } | |
93 | } | |
94 | ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]); | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
99 | static void | |
100 | parse_options(int argc, char *argv[]) | |
101 | { | |
102 | static struct option long_options[] = { | |
103 | {"timeout", required_argument, 0, 't'}, | |
104 | {"verbose", optional_argument, 0, 'v'}, | |
105 | {"help", no_argument, 0, 'h'}, | |
106 | {"version", no_argument, 0, 'V'}, | |
107 | {0, 0, 0, 0}, | |
108 | }; | |
109 | char *short_options = long_options_to_short_options(long_options); | |
110 | ||
111 | for (;;) { | |
112 | unsigned long int timeout; | |
113 | int c; | |
114 | ||
115 | c = getopt_long(argc, argv, short_options, long_options, NULL); | |
116 | if (c == -1) { | |
117 | break; | |
118 | } | |
119 | ||
120 | switch (c) { | |
121 | case 't': | |
122 | timeout = strtoul(optarg, NULL, 10); | |
123 | if (timeout <= 0) { | |
124 | ovs_fatal(0, "value %s on -t or --timeout is not at least 1", | |
125 | optarg); | |
126 | } else { | |
127 | time_alarm(timeout); | |
128 | } | |
129 | break; | |
130 | ||
131 | case 'h': | |
132 | usage(); | |
133 | ||
134 | case 'V': | |
135 | OVS_PRINT_VERSION(0, 0); | |
136 | exit(EXIT_SUCCESS); | |
137 | ||
138 | case 'v': | |
139 | vlog_set_verbosity(optarg); | |
140 | break; | |
141 | ||
142 | case '?': | |
143 | exit(EXIT_FAILURE); | |
144 | ||
145 | default: | |
146 | abort(); | |
147 | } | |
148 | } | |
149 | free(short_options); | |
150 | } | |
151 | ||
152 | static void | |
153 | usage(void) | |
154 | { | |
155 | printf("%s: Open vSwitch datapath management utility\n" | |
156 | "usage: %s [OPTIONS] COMMAND [ARG...]\n" | |
157 | " add-dp DP [IFACE...] add new datapath DP (with IFACEs)\n" | |
158 | " del-dp DP delete local datapath DP\n" | |
159 | " add-if DP IFACE... add each IFACE as a port on DP\n" | |
160 | " del-if DP IFACE... delete each IFACE from DP\n" | |
b566902b | 161 | " dump-dps display names of all datapaths\n" |
064af421 BP |
162 | " show show basic info on all datapaths\n" |
163 | " show DP... show basic info on each DP\n" | |
164 | " dump-flows DP display flows in DP\n" | |
165 | " del-flows DP delete all flows from DP\n" | |
166 | " dump-groups DP display port groups in DP\n", | |
167 | program_name, program_name); | |
168 | vlog_usage(); | |
169 | printf("\nOther options:\n" | |
170 | " -t, --timeout=SECS give up after SECS seconds\n" | |
171 | " -h, --help display this help message\n" | |
172 | " -V, --version display version information\n"); | |
173 | exit(EXIT_SUCCESS); | |
174 | } | |
175 | ||
176 | static void run(int retval, const char *message, ...) | |
177 | PRINTF_FORMAT(2, 3); | |
178 | ||
179 | static void run(int retval, const char *message, ...) | |
180 | { | |
181 | if (retval) { | |
182 | va_list args; | |
183 | ||
184 | fprintf(stderr, "%s: ", program_name); | |
185 | va_start(args, message); | |
186 | vfprintf(stderr, message, args); | |
187 | va_end(args); | |
188 | if (retval == EOF) { | |
189 | fputs(": unexpected end of file\n", stderr); | |
190 | } else { | |
191 | fprintf(stderr, ": %s\n", strerror(retval)); | |
192 | } | |
193 | ||
194 | exit(EXIT_FAILURE); | |
195 | } | |
196 | } | |
197 | \f | |
198 | static void do_add_if(int argc, char *argv[]); | |
199 | ||
200 | static int if_up(const char *netdev_name) | |
201 | { | |
202 | struct netdev *netdev; | |
203 | int retval; | |
204 | ||
205 | retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev); | |
206 | if (!retval) { | |
207 | retval = netdev_turn_flags_on(netdev, NETDEV_UP, true); | |
208 | netdev_close(netdev); | |
209 | } | |
210 | return retval; | |
211 | } | |
212 | ||
213 | static void | |
214 | do_add_dp(int argc UNUSED, char *argv[]) | |
215 | { | |
c228a364 | 216 | struct dpif *dpif; |
064af421 | 217 | run(dpif_create(argv[1], &dpif), "add_dp"); |
c228a364 | 218 | dpif_close(dpif); |
064af421 BP |
219 | if (argc > 2) { |
220 | do_add_if(argc, argv); | |
221 | } | |
222 | } | |
223 | ||
224 | static void | |
225 | do_del_dp(int argc UNUSED, char *argv[]) | |
226 | { | |
c228a364 | 227 | struct dpif *dpif; |
064af421 | 228 | run(dpif_open(argv[1], &dpif), "opening datapath"); |
c228a364 BP |
229 | run(dpif_delete(dpif), "del_dp"); |
230 | dpif_close(dpif); | |
064af421 BP |
231 | } |
232 | ||
233 | static int | |
234 | compare_ports(const void *a_, const void *b_) | |
235 | { | |
236 | const struct odp_port *a = a_; | |
237 | const struct odp_port *b = b_; | |
238 | return a->port < b->port ? -1 : a->port > b->port; | |
239 | } | |
240 | ||
241 | static void | |
242 | query_ports(struct dpif *dpif, struct odp_port **ports, size_t *n_ports) | |
243 | { | |
244 | run(dpif_port_list(dpif, ports, n_ports), "listing ports"); | |
245 | qsort(*ports, *n_ports, sizeof **ports, compare_ports); | |
246 | } | |
247 | ||
064af421 BP |
248 | static void |
249 | do_add_if(int argc UNUSED, char *argv[]) | |
250 | { | |
251 | bool failure = false; | |
c228a364 | 252 | struct dpif *dpif; |
064af421 BP |
253 | int i; |
254 | ||
255 | run(dpif_open(argv[1], &dpif), "opening datapath"); | |
256 | for (i = 2; i < argc; i++) { | |
257 | char *save_ptr = NULL; | |
258 | char *devname, *suboptions; | |
064af421 BP |
259 | int flags = 0; |
260 | int error; | |
261 | ||
e50097d2 | 262 | devname = strtok_r(argv[i], ",", &save_ptr); |
064af421 BP |
263 | if (!devname) { |
264 | ovs_error(0, "%s is not a valid network device name", argv[i]); | |
265 | continue; | |
266 | } | |
267 | ||
268 | suboptions = strtok_r(NULL, "", &save_ptr); | |
269 | if (suboptions) { | |
270 | enum { | |
064af421 BP |
271 | AP_INTERNAL |
272 | }; | |
273 | static char *options[] = { | |
064af421 BP |
274 | "internal" |
275 | }; | |
276 | ||
277 | while (*suboptions != '\0') { | |
278 | char *value; | |
279 | ||
280 | switch (getsubopt(&suboptions, options, &value)) { | |
064af421 BP |
281 | case AP_INTERNAL: |
282 | flags |= ODP_PORT_INTERNAL; | |
283 | break; | |
284 | ||
285 | default: | |
286 | ovs_error(0, "unknown suboption '%s'", value); | |
287 | break; | |
288 | } | |
289 | } | |
290 | } | |
064af421 | 291 | |
9ee3ae3e | 292 | error = dpif_port_add(dpif, devname, flags, NULL); |
064af421 | 293 | if (error) { |
9ee3ae3e | 294 | ovs_error(error, "adding %s to %s failed", devname, argv[1]); |
064af421 BP |
295 | failure = true; |
296 | } else if (if_up(devname)) { | |
297 | failure = true; | |
298 | } | |
299 | } | |
c228a364 | 300 | dpif_close(dpif); |
064af421 BP |
301 | if (failure) { |
302 | exit(EXIT_FAILURE); | |
303 | } | |
304 | } | |
305 | ||
306 | static bool | |
307 | get_port_number(struct dpif *dpif, const char *name, uint16_t *port) | |
308 | { | |
309 | struct odp_port *ports; | |
310 | size_t n_ports; | |
311 | size_t i; | |
312 | ||
313 | query_ports(dpif, &ports, &n_ports); | |
314 | for (i = 0; i < n_ports; i++) { | |
315 | if (!strcmp(name, ports[i].devname)) { | |
316 | *port = ports[i].port; | |
317 | free(ports); | |
318 | return true; | |
319 | } | |
320 | } | |
321 | free(ports); | |
322 | ovs_error(0, "no port named %s", name); | |
323 | return false; | |
324 | } | |
325 | ||
326 | static void | |
327 | do_del_if(int argc UNUSED, char *argv[]) | |
328 | { | |
329 | bool failure = false; | |
c228a364 | 330 | struct dpif *dpif; |
064af421 BP |
331 | int i; |
332 | ||
333 | run(dpif_open(argv[1], &dpif), "opening datapath"); | |
334 | for (i = 2; i < argc; i++) { | |
335 | const char *name = argv[i]; | |
336 | uint16_t port; | |
337 | int error; | |
338 | ||
339 | if (!name[strspn(name, "0123456789")]) { | |
340 | port = atoi(name); | |
c228a364 | 341 | } else if (!get_port_number(dpif, name, &port)) { |
064af421 BP |
342 | failure = true; |
343 | continue; | |
344 | } | |
345 | ||
c228a364 | 346 | error = dpif_port_del(dpif, port); |
064af421 BP |
347 | if (error) { |
348 | ovs_error(error, "deleting port %s from %s failed", name, argv[1]); | |
349 | failure = true; | |
350 | } | |
351 | } | |
c228a364 | 352 | dpif_close(dpif); |
064af421 BP |
353 | if (failure) { |
354 | exit(EXIT_FAILURE); | |
355 | } | |
356 | } | |
357 | ||
358 | static void | |
359 | show_dpif(struct dpif *dpif) | |
360 | { | |
361 | struct odp_port *ports; | |
362 | struct odp_stats stats; | |
363 | size_t n_ports; | |
364 | size_t i; | |
365 | ||
b29ba128 | 366 | printf("%s:\n", dpif_name(dpif)); |
064af421 BP |
367 | if (!dpif_get_dp_stats(dpif, &stats)) { |
368 | printf("\tflows: cur:%"PRIu32", soft-max:%"PRIu32", " | |
369 | "hard-max:%"PRIu32"\n", | |
370 | stats.n_flows, stats.cur_capacity, stats.max_capacity); | |
371 | printf("\tports: cur:%"PRIu32", max:%"PRIu32"\n", | |
372 | stats.n_ports, stats.max_ports); | |
373 | printf("\tgroups: max:%"PRIu16"\n", stats.max_groups); | |
2886875a BP |
374 | printf("\tlookups: frags:%llu, hit:%llu, missed:%llu, lost:%llu\n", |
375 | (unsigned long long int) stats.n_frags, | |
376 | (unsigned long long int) stats.n_hit, | |
377 | (unsigned long long int) stats.n_missed, | |
378 | (unsigned long long int) stats.n_lost); | |
064af421 BP |
379 | printf("\tqueues: max-miss:%"PRIu16", max-action:%"PRIu16"\n", |
380 | stats.max_miss_queue, stats.max_action_queue); | |
381 | } | |
382 | query_ports(dpif, &ports, &n_ports); | |
383 | for (i = 0; i < n_ports; i++) { | |
384 | printf("\tport %u: %s", ports[i].port, ports[i].devname); | |
385 | if (ports[i].flags & ODP_PORT_INTERNAL) { | |
386 | printf(" (internal)"); | |
387 | } | |
388 | printf("\n"); | |
389 | } | |
390 | free(ports); | |
391 | dpif_close(dpif); | |
392 | } | |
393 | ||
394 | static void | |
c4fca56a | 395 | do_show(int argc, char *argv[]) |
064af421 BP |
396 | { |
397 | bool failure = false; | |
398 | if (argc > 1) { | |
399 | int i; | |
400 | for (i = 1; i < argc; i++) { | |
401 | const char *name = argv[i]; | |
c228a364 | 402 | struct dpif *dpif; |
064af421 BP |
403 | int error; |
404 | ||
405 | error = dpif_open(name, &dpif); | |
406 | if (!error) { | |
c228a364 | 407 | show_dpif(dpif); |
064af421 BP |
408 | } else { |
409 | ovs_error(error, "opening datapath %s failed", name); | |
410 | failure = true; | |
411 | } | |
412 | } | |
413 | } else { | |
414 | unsigned int i; | |
415 | for (i = 0; i < ODP_MAX; i++) { | |
416 | char name[128]; | |
c228a364 | 417 | struct dpif *dpif; |
064af421 BP |
418 | int error; |
419 | ||
420 | sprintf(name, "dp%u", i); | |
421 | error = dpif_open(name, &dpif); | |
422 | if (!error) { | |
c228a364 | 423 | show_dpif(dpif); |
064af421 BP |
424 | } else if (error != ENODEV) { |
425 | ovs_error(error, "opening datapath %s failed", name); | |
426 | failure = true; | |
427 | } | |
428 | } | |
429 | } | |
430 | if (failure) { | |
431 | exit(EXIT_FAILURE); | |
432 | } | |
433 | } | |
434 | ||
b566902b JP |
435 | static void |
436 | do_dump_dps(int argc UNUSED, char *argv[] UNUSED) | |
437 | { | |
438 | struct svec all_dps; | |
439 | unsigned int i; | |
440 | int error; | |
441 | ||
442 | svec_init(&all_dps); | |
443 | error = dp_enumerate(&all_dps); | |
444 | ||
445 | for (i = 0; i < all_dps.n; i++) { | |
8fef8c71 BP |
446 | struct dpif *dpif; |
447 | if (!dpif_open(all_dps.names[i], &dpif)) { | |
448 | printf("%s\n", dpif_name(dpif)); | |
449 | dpif_close(dpif); | |
b566902b | 450 | } |
b566902b JP |
451 | } |
452 | ||
453 | svec_destroy(&all_dps); | |
454 | if (error) { | |
455 | exit(EXIT_FAILURE); | |
456 | } | |
457 | } | |
458 | ||
064af421 BP |
459 | static void |
460 | do_dump_flows(int argc UNUSED, char *argv[]) | |
461 | { | |
462 | struct odp_flow *flows; | |
c228a364 | 463 | struct dpif *dpif; |
064af421 BP |
464 | size_t n_flows; |
465 | struct ds ds; | |
466 | size_t i; | |
467 | ||
468 | run(dpif_open(argv[1], &dpif), "opening datapath"); | |
c228a364 | 469 | run(dpif_flow_list_all(dpif, &flows, &n_flows), "listing all flows"); |
064af421 BP |
470 | |
471 | ds_init(&ds); | |
472 | for (i = 0; i < n_flows; i++) { | |
473 | struct odp_flow *f = &flows[i]; | |
474 | enum { MAX_ACTIONS = 4096 / sizeof(union odp_action) }; | |
475 | union odp_action actions[MAX_ACTIONS]; | |
476 | ||
477 | f->actions = actions; | |
478 | f->n_actions = MAX_ACTIONS; | |
c228a364 | 479 | dpif_flow_get(dpif, f); |
064af421 BP |
480 | |
481 | ds_clear(&ds); | |
482 | format_odp_flow(&ds, f); | |
483 | printf("%s\n", ds_cstr(&ds)); | |
484 | } | |
485 | ds_destroy(&ds); | |
c228a364 | 486 | dpif_close(dpif); |
064af421 BP |
487 | } |
488 | ||
489 | static void | |
490 | do_del_flows(int argc UNUSED, char *argv[]) | |
491 | { | |
c228a364 | 492 | struct dpif *dpif; |
064af421 BP |
493 | |
494 | run(dpif_open(argv[1], &dpif), "opening datapath"); | |
c228a364 BP |
495 | run(dpif_flow_flush(dpif), "deleting all flows"); |
496 | dpif_close(dpif); | |
064af421 BP |
497 | } |
498 | ||
499 | static void | |
500 | do_dump_groups(int argc UNUSED, char *argv[]) | |
501 | { | |
502 | struct odp_stats stats; | |
c228a364 | 503 | struct dpif *dpif; |
064af421 BP |
504 | unsigned int i; |
505 | ||
506 | run(dpif_open(argv[1], &dpif), "opening datapath"); | |
c228a364 | 507 | run(dpif_get_dp_stats(dpif, &stats), "get datapath stats"); |
064af421 | 508 | for (i = 0; i < stats.max_groups; i++) { |
bb8a9a2b | 509 | uint16_t *ports; |
064af421 BP |
510 | size_t n_ports; |
511 | ||
bb8a9a2b | 512 | if (!dpif_port_group_get(dpif, i, &ports, &n_ports) && n_ports) { |
064af421 BP |
513 | size_t j; |
514 | ||
515 | printf("group %u:", i); | |
516 | for (j = 0; j < n_ports; j++) { | |
517 | printf(" %"PRIu16, ports[j]); | |
518 | } | |
519 | printf("\n"); | |
520 | } | |
bb8a9a2b | 521 | free(ports); |
064af421 | 522 | } |
c228a364 | 523 | dpif_close(dpif); |
064af421 BP |
524 | } |
525 | ||
526 | static void | |
527 | do_help(int argc UNUSED, char *argv[] UNUSED) | |
528 | { | |
529 | usage(); | |
530 | } | |
531 | ||
532 | static struct command all_commands[] = { | |
533 | { "add-dp", 1, INT_MAX, do_add_dp }, | |
534 | { "del-dp", 1, 1, do_del_dp }, | |
535 | { "add-if", 2, INT_MAX, do_add_if }, | |
536 | { "del-if", 2, INT_MAX, do_del_if }, | |
b566902b | 537 | { "dump-dps", 0, 0, do_dump_dps }, |
064af421 BP |
538 | { "show", 0, INT_MAX, do_show }, |
539 | { "dump-flows", 1, 1, do_dump_flows }, | |
540 | { "del-flows", 1, 1, do_del_flows }, | |
541 | { "dump-groups", 1, 1, do_dump_groups }, | |
542 | { "help", 0, INT_MAX, do_help }, | |
543 | { NULL, 0, 0, NULL }, | |
544 | }; |