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