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