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