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