]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
67a4917b | 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 "netlink.h" | |
39 | #include "odp-util.h" | |
40 | #include "ofp-print.h" | |
41 | #include "ofpbuf.h" | |
42 | #include "openflow/nicira-ext.h" | |
43 | #include "openflow/openflow.h" | |
44 | #include "packets.h" | |
45 | #include "random.h" | |
46 | #include "socket-util.h" | |
fe55ad15 | 47 | #include "stream-ssl.h" |
064af421 BP |
48 | #include "timeval.h" |
49 | #include "util.h" | |
064af421 | 50 | #include "vconn.h" |
39997502 | 51 | #include "xtoxll.h" |
064af421 BP |
52 | |
53 | #include "vlog.h" | |
54 | #define THIS_MODULE VLM_ofctl | |
55 | ||
56 | #define DEFAULT_IDLE_TIMEOUT 60 | |
57 | ||
58 | #define MOD_PORT_CMD_UP "up" | |
59 | #define MOD_PORT_CMD_DOWN "down" | |
60 | #define MOD_PORT_CMD_FLOOD "flood" | |
61 | #define MOD_PORT_CMD_NOFLOOD "noflood" | |
62 | ||
675febfa BP |
63 | /* Use strict matching for flow mod commands? */ |
64 | static bool strict; | |
064af421 | 65 | |
675febfa | 66 | static const struct command all_commands[]; |
064af421 BP |
67 | |
68 | static void usage(void) NO_RETURN; | |
675febfa | 69 | static void parse_options(int argc, char *argv[]); |
064af421 | 70 | |
675febfa BP |
71 | int |
72 | main(int argc, char *argv[]) | |
064af421 | 73 | { |
064af421 BP |
74 | set_program_name(argv[0]); |
75 | time_init(); | |
76 | vlog_init(); | |
675febfa | 77 | parse_options(argc, argv); |
064af421 | 78 | signal(SIGPIPE, SIG_IGN); |
675febfa | 79 | run_command(argc - optind, argv + optind, all_commands); |
064af421 BP |
80 | return 0; |
81 | } | |
82 | ||
83 | static void | |
675febfa | 84 | parse_options(int argc, char *argv[]) |
064af421 BP |
85 | { |
86 | enum { | |
87c84891 JP |
87 | OPT_STRICT = UCHAR_MAX + 1, |
88 | VLOG_OPTION_ENUMS | |
064af421 BP |
89 | }; |
90 | static struct option long_options[] = { | |
91 | {"timeout", required_argument, 0, 't'}, | |
064af421 BP |
92 | {"strict", no_argument, 0, OPT_STRICT}, |
93 | {"help", no_argument, 0, 'h'}, | |
94 | {"version", no_argument, 0, 'V'}, | |
87c84891 | 95 | VLOG_LONG_OPTIONS, |
fe55ad15 | 96 | STREAM_SSL_LONG_OPTIONS |
064af421 BP |
97 | {0, 0, 0, 0}, |
98 | }; | |
99 | char *short_options = long_options_to_short_options(long_options); | |
100 | ||
064af421 BP |
101 | for (;;) { |
102 | unsigned long int timeout; | |
103 | int c; | |
104 | ||
105 | c = getopt_long(argc, argv, short_options, long_options, NULL); | |
106 | if (c == -1) { | |
107 | break; | |
108 | } | |
109 | ||
110 | switch (c) { | |
111 | case 't': | |
112 | timeout = strtoul(optarg, NULL, 10); | |
113 | if (timeout <= 0) { | |
114 | ovs_fatal(0, "value %s on -t or --timeout is not at least 1", | |
115 | optarg); | |
116 | } else { | |
117 | time_alarm(timeout); | |
118 | } | |
119 | break; | |
120 | ||
121 | case 'h': | |
122 | usage(); | |
123 | ||
124 | case 'V': | |
125 | OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION); | |
126 | exit(EXIT_SUCCESS); | |
127 | ||
064af421 | 128 | case OPT_STRICT: |
675febfa | 129 | strict = true; |
064af421 BP |
130 | break; |
131 | ||
87c84891 | 132 | VLOG_OPTION_HANDLERS |
fe55ad15 | 133 | STREAM_SSL_OPTION_HANDLERS |
064af421 BP |
134 | |
135 | case '?': | |
136 | exit(EXIT_FAILURE); | |
137 | ||
138 | default: | |
139 | abort(); | |
140 | } | |
141 | } | |
142 | free(short_options); | |
143 | } | |
144 | ||
145 | static void | |
146 | usage(void) | |
147 | { | |
148 | printf("%s: OpenFlow switch management utility\n" | |
149 | "usage: %s [OPTIONS] COMMAND [ARG...]\n" | |
150 | "\nFor OpenFlow switches:\n" | |
151 | " show SWITCH show OpenFlow information\n" | |
152 | " status SWITCH [KEY] report statistics (about KEY)\n" | |
153 | " dump-desc SWITCH print switch description\n" | |
154 | " dump-tables SWITCH print table stats\n" | |
155 | " mod-port SWITCH IFACE ACT modify port behavior\n" | |
abaad8cf | 156 | " dump-ports SWITCH [PORT] print port statistics\n" |
064af421 BP |
157 | " dump-flows SWITCH print all flow entries\n" |
158 | " dump-flows SWITCH FLOW print matching FLOWs\n" | |
159 | " dump-aggregate SWITCH print aggregate flow statistics\n" | |
160 | " dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n" | |
161 | " add-flow SWITCH FLOW add flow described by FLOW\n" | |
162 | " add-flows SWITCH FILE add flows from FILE\n" | |
163 | " mod-flows SWITCH FLOW modify actions of matching FLOWs\n" | |
164 | " del-flows SWITCH [FLOW] delete matching FLOWs\n" | |
165 | " monitor SWITCH MISSLEN EXP print packets received from SWITCH\n" | |
064af421 BP |
166 | "\nFor OpenFlow switches and controllers:\n" |
167 | " probe VCONN probe whether VCONN is up\n" | |
168 | " ping VCONN [N] latency of N-byte echos\n" | |
169 | " benchmark VCONN N COUNT bandwidth of COUNT N-byte echos\n" | |
170 | "where each SWITCH is an active OpenFlow connection method.\n", | |
171 | program_name, program_name); | |
172 | vconn_usage(true, false, false); | |
173 | vlog_usage(); | |
174 | printf("\nOther options:\n" | |
175 | " --strict use strict match for flow commands\n" | |
176 | " -t, --timeout=SECS give up after SECS seconds\n" | |
177 | " -h, --help display this help message\n" | |
178 | " -V, --version display version information\n"); | |
179 | exit(EXIT_SUCCESS); | |
180 | } | |
181 | ||
182 | static void run(int retval, const char *message, ...) | |
183 | PRINTF_FORMAT(2, 3); | |
184 | ||
185 | static void run(int retval, const char *message, ...) | |
186 | { | |
187 | if (retval) { | |
188 | va_list args; | |
189 | ||
190 | fprintf(stderr, "%s: ", program_name); | |
191 | va_start(args, message); | |
192 | vfprintf(stderr, message, args); | |
193 | va_end(args); | |
194 | if (retval == EOF) { | |
195 | fputs(": unexpected end of file\n", stderr); | |
196 | } else { | |
197 | fprintf(stderr, ": %s\n", strerror(retval)); | |
198 | } | |
199 | ||
200 | exit(EXIT_FAILURE); | |
201 | } | |
202 | } | |
203 | \f | |
204 | /* Generic commands. */ | |
205 | ||
1a6f1e2a JG |
206 | static void |
207 | open_vconn_socket(const char *name, struct vconn **vconnp) | |
208 | { | |
209 | char *vconn_name = xasprintf("unix:%s", name); | |
210 | VLOG_INFO("connecting to %s", vconn_name); | |
211 | run(vconn_open_block(vconn_name, OFP_VERSION, vconnp), | |
212 | "connecting to %s", vconn_name); | |
213 | free(vconn_name); | |
214 | } | |
215 | ||
064af421 BP |
216 | static void |
217 | open_vconn(const char *name, struct vconn **vconnp) | |
218 | { | |
c228a364 | 219 | struct dpif *dpif; |
064af421 | 220 | struct stat s; |
1a6f1e2a JG |
221 | char *bridge_path, *datapath_name, *datapath_type; |
222 | ||
223 | bridge_path = xasprintf("%s/%s.mgmt", ovs_rundir, name); | |
224 | dp_parse_name(name, &datapath_name, &datapath_type); | |
064af421 BP |
225 | |
226 | if (strstr(name, ":")) { | |
227 | run(vconn_open_block(name, OFP_VERSION, vconnp), | |
228 | "connecting to %s", name); | |
229 | } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) { | |
1a6f1e2a JG |
230 | open_vconn_socket(name, vconnp); |
231 | } else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) { | |
232 | open_vconn_socket(bridge_path, vconnp); | |
233 | } else if (!dpif_open(datapath_name, datapath_type, &dpif)) { | |
064af421 BP |
234 | char dpif_name[IF_NAMESIZE + 1]; |
235 | char *socket_name; | |
064af421 | 236 | |
c228a364 | 237 | run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name), |
064af421 | 238 | "obtaining name of %s", dpif_name); |
c228a364 | 239 | dpif_close(dpif); |
064af421 BP |
240 | if (strcmp(dpif_name, name)) { |
241 | VLOG_INFO("datapath %s is named %s", name, dpif_name); | |
242 | } | |
243 | ||
244 | socket_name = xasprintf("%s/%s.mgmt", ovs_rundir, dpif_name); | |
245 | if (stat(socket_name, &s)) { | |
246 | ovs_fatal(errno, "cannot connect to %s: stat failed on %s", | |
247 | name, socket_name); | |
248 | } else if (!S_ISSOCK(s.st_mode)) { | |
249 | ovs_fatal(0, "cannot connect to %s: %s is not a socket", | |
250 | name, socket_name); | |
251 | } | |
252 | ||
1a6f1e2a | 253 | open_vconn_socket(socket_name, vconnp); |
064af421 | 254 | free(socket_name); |
064af421 BP |
255 | } else { |
256 | ovs_fatal(0, "%s is not a valid connection method", name); | |
257 | } | |
1a6f1e2a JG |
258 | |
259 | free(datapath_name); | |
260 | free(datapath_type); | |
261 | free(bridge_path); | |
064af421 BP |
262 | } |
263 | ||
264 | static void * | |
265 | alloc_stats_request(size_t body_len, uint16_t type, struct ofpbuf **bufferp) | |
266 | { | |
267 | struct ofp_stats_request *rq; | |
268 | rq = make_openflow((offsetof(struct ofp_stats_request, body) | |
269 | + body_len), OFPT_STATS_REQUEST, bufferp); | |
270 | rq->type = htons(type); | |
271 | rq->flags = htons(0); | |
272 | return rq->body; | |
273 | } | |
274 | ||
275 | static void | |
276 | send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer) | |
277 | { | |
278 | update_openflow_length(buffer); | |
279 | run(vconn_send_block(vconn, buffer), "failed to send packet to switch"); | |
280 | } | |
281 | ||
282 | static void | |
283 | dump_transaction(const char *vconn_name, struct ofpbuf *request) | |
284 | { | |
285 | struct vconn *vconn; | |
286 | struct ofpbuf *reply; | |
287 | ||
288 | update_openflow_length(request); | |
289 | open_vconn(vconn_name, &vconn); | |
290 | run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); | |
291 | ofp_print(stdout, reply->data, reply->size, 1); | |
292 | vconn_close(vconn); | |
293 | } | |
294 | ||
295 | static void | |
296 | dump_trivial_transaction(const char *vconn_name, uint8_t request_type) | |
297 | { | |
298 | struct ofpbuf *request; | |
299 | make_openflow(sizeof(struct ofp_header), request_type, &request); | |
300 | dump_transaction(vconn_name, request); | |
301 | } | |
302 | ||
303 | static void | |
304 | dump_stats_transaction(const char *vconn_name, struct ofpbuf *request) | |
305 | { | |
306 | uint32_t send_xid = ((struct ofp_header *) request->data)->xid; | |
307 | struct vconn *vconn; | |
308 | bool done = false; | |
309 | ||
310 | open_vconn(vconn_name, &vconn); | |
311 | send_openflow_buffer(vconn, request); | |
312 | while (!done) { | |
313 | uint32_t recv_xid; | |
314 | struct ofpbuf *reply; | |
315 | ||
316 | run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); | |
317 | recv_xid = ((struct ofp_header *) reply->data)->xid; | |
318 | if (send_xid == recv_xid) { | |
319 | struct ofp_stats_reply *osr; | |
320 | ||
321 | ofp_print(stdout, reply->data, reply->size, 1); | |
322 | ||
323 | osr = ofpbuf_at(reply, 0, sizeof *osr); | |
324 | done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE); | |
325 | } else { | |
326 | VLOG_DBG("received reply with xid %08"PRIx32" " | |
327 | "!= expected %08"PRIx32, recv_xid, send_xid); | |
328 | } | |
329 | ofpbuf_delete(reply); | |
330 | } | |
331 | vconn_close(vconn); | |
332 | } | |
333 | ||
334 | static void | |
335 | dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type) | |
336 | { | |
337 | struct ofpbuf *request; | |
338 | alloc_stats_request(0, stats_type, &request); | |
339 | dump_stats_transaction(vconn_name, request); | |
340 | } | |
341 | ||
342 | static void | |
c69ee87c | 343 | do_show(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
344 | { |
345 | dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST); | |
346 | dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST); | |
347 | } | |
348 | ||
349 | static void | |
675febfa | 350 | do_status(int argc, char *argv[]) |
064af421 BP |
351 | { |
352 | struct nicira_header *request, *reply; | |
353 | struct vconn *vconn; | |
354 | struct ofpbuf *b; | |
355 | ||
356 | request = make_openflow(sizeof *request, OFPT_VENDOR, &b); | |
357 | request->vendor = htonl(NX_VENDOR_ID); | |
358 | request->subtype = htonl(NXT_STATUS_REQUEST); | |
359 | if (argc > 2) { | |
360 | ofpbuf_put(b, argv[2], strlen(argv[2])); | |
361 | update_openflow_length(b); | |
362 | } | |
363 | open_vconn(argv[1], &vconn); | |
364 | run(vconn_transact(vconn, b, &b), "talking to %s", argv[1]); | |
365 | vconn_close(vconn); | |
366 | ||
367 | if (b->size < sizeof *reply) { | |
368 | ovs_fatal(0, "short reply (%zu bytes)", b->size); | |
369 | } | |
370 | reply = b->data; | |
371 | if (reply->header.type != OFPT_VENDOR | |
372 | || reply->vendor != ntohl(NX_VENDOR_ID) | |
373 | || reply->subtype != ntohl(NXT_STATUS_REPLY)) { | |
374 | ofp_print(stderr, b->data, b->size, 2); | |
375 | ovs_fatal(0, "bad reply"); | |
376 | } | |
377 | ||
378 | fwrite(reply + 1, b->size - sizeof *reply, 1, stdout); | |
379 | } | |
380 | ||
381 | static void | |
c69ee87c | 382 | do_dump_desc(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
383 | { |
384 | dump_trivial_stats_transaction(argv[1], OFPST_DESC); | |
385 | } | |
386 | ||
387 | static void | |
c69ee87c | 388 | do_dump_tables(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
389 | { |
390 | dump_trivial_stats_transaction(argv[1], OFPST_TABLE); | |
391 | } | |
392 | ||
393 | ||
394 | static uint32_t | |
395 | str_to_u32(const char *str) | |
396 | { | |
397 | char *tail; | |
398 | uint32_t value; | |
399 | ||
400 | errno = 0; | |
401 | value = strtoul(str, &tail, 0); | |
402 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
403 | ovs_fatal(0, "invalid numeric format %s", str); | |
404 | } | |
405 | return value; | |
406 | } | |
407 | ||
408 | static void | |
409 | str_to_mac(const char *str, uint8_t mac[6]) | |
410 | { | |
eaa71334 BP |
411 | if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) |
412 | != ETH_ADDR_SCAN_COUNT) { | |
064af421 BP |
413 | ovs_fatal(0, "invalid mac address %s", str); |
414 | } | |
415 | } | |
416 | ||
417 | static uint32_t | |
418 | str_to_ip(const char *str_, uint32_t *ip) | |
419 | { | |
420 | char *str = xstrdup(str_); | |
421 | char *save_ptr = NULL; | |
422 | const char *name, *netmask; | |
423 | struct in_addr in_addr; | |
424 | int n_wild, retval; | |
425 | ||
e50097d2 | 426 | name = strtok_r(str, "/", &save_ptr); |
064af421 BP |
427 | retval = name ? lookup_ip(name, &in_addr) : EINVAL; |
428 | if (retval) { | |
429 | ovs_fatal(0, "%s: could not convert to IP address", str); | |
430 | } | |
431 | *ip = in_addr.s_addr; | |
432 | ||
e50097d2 | 433 | netmask = strtok_r(NULL, "/", &save_ptr); |
064af421 BP |
434 | if (netmask) { |
435 | uint8_t o[4]; | |
436 | if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8, | |
437 | &o[0], &o[1], &o[2], &o[3]) == 4) { | |
438 | uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3]; | |
439 | int i; | |
440 | ||
441 | /* Find first 1-bit. */ | |
442 | for (i = 0; i < 32; i++) { | |
443 | if (nm & (1u << i)) { | |
444 | break; | |
445 | } | |
446 | } | |
447 | n_wild = i; | |
448 | ||
449 | /* Verify that the rest of the bits are 1-bits. */ | |
450 | for (; i < 32; i++) { | |
451 | if (!(nm & (1u << i))) { | |
452 | ovs_fatal(0, "%s: %s is not a valid netmask", | |
453 | str, netmask); | |
454 | } | |
455 | } | |
456 | } else { | |
457 | int prefix = atoi(netmask); | |
458 | if (prefix <= 0 || prefix > 32) { | |
459 | ovs_fatal(0, "%s: network prefix bits not between 1 and 32", | |
460 | str); | |
461 | } | |
462 | n_wild = 32 - prefix; | |
463 | } | |
464 | } else { | |
465 | n_wild = 0; | |
466 | } | |
467 | ||
468 | free(str); | |
469 | return n_wild; | |
470 | } | |
471 | ||
abaad8cf JP |
472 | static uint16_t |
473 | str_to_port_no(const char *vconn_name, const char *str) | |
474 | { | |
475 | struct ofpbuf *request, *reply; | |
476 | struct ofp_switch_features *osf; | |
477 | struct vconn *vconn; | |
478 | int n_ports; | |
479 | int port_idx; | |
480 | unsigned int port_no; | |
481 | ||
482 | ||
483 | /* Check if the argument is a port index. Otherwise, treat it as | |
484 | * the port name. */ | |
485 | if (str_to_uint(str, 10, &port_no)) { | |
486 | return port_no; | |
487 | } | |
488 | ||
489 | /* Send a "Features Request" to resolve the name into a number. */ | |
490 | make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request); | |
491 | open_vconn(vconn_name, &vconn); | |
492 | run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); | |
493 | ||
494 | osf = reply->data; | |
495 | n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports; | |
496 | ||
497 | for (port_idx = 0; port_idx < n_ports; port_idx++) { | |
498 | /* Check argument as an interface name */ | |
499 | if (!strncmp((char *)osf->ports[port_idx].name, str, | |
500 | sizeof osf->ports[0].name)) { | |
501 | break; | |
502 | } | |
503 | } | |
504 | if (port_idx == n_ports) { | |
505 | ovs_fatal(0, "couldn't find monitored port: %s", str); | |
506 | } | |
507 | ||
508 | ofpbuf_delete(reply); | |
509 | vconn_close(vconn); | |
510 | ||
511 | return port_idx; | |
512 | } | |
513 | ||
064af421 BP |
514 | static void * |
515 | put_action(struct ofpbuf *b, size_t size, uint16_t type) | |
516 | { | |
517 | struct ofp_action_header *ah = ofpbuf_put_zeros(b, size); | |
518 | ah->type = htons(type); | |
519 | ah->len = htons(size); | |
520 | return ah; | |
521 | } | |
522 | ||
523 | static struct ofp_action_output * | |
524 | put_output_action(struct ofpbuf *b, uint16_t port) | |
525 | { | |
526 | struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT); | |
527 | oao->port = htons(port); | |
528 | return oao; | |
529 | } | |
530 | ||
531 | static void | |
532 | put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr) | |
533 | { | |
534 | struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type); | |
535 | str_to_mac(addr, oada->dl_addr); | |
536 | } | |
537 | ||
538 | ||
539 | static bool | |
540 | parse_port_name(const char *name, uint16_t *port) | |
541 | { | |
542 | struct pair { | |
543 | const char *name; | |
544 | uint16_t value; | |
545 | }; | |
546 | static const struct pair pairs[] = { | |
547 | #define DEF_PAIR(NAME) {#NAME, OFPP_##NAME} | |
548 | DEF_PAIR(IN_PORT), | |
549 | DEF_PAIR(TABLE), | |
550 | DEF_PAIR(NORMAL), | |
551 | DEF_PAIR(FLOOD), | |
552 | DEF_PAIR(ALL), | |
553 | DEF_PAIR(CONTROLLER), | |
554 | DEF_PAIR(LOCAL), | |
555 | DEF_PAIR(NONE), | |
556 | #undef DEF_PAIR | |
557 | }; | |
558 | static const int n_pairs = ARRAY_SIZE(pairs); | |
559 | size_t i; | |
560 | ||
561 | for (i = 0; i < n_pairs; i++) { | |
562 | if (!strcasecmp(name, pairs[i].name)) { | |
563 | *port = pairs[i].value; | |
564 | return true; | |
565 | } | |
566 | } | |
567 | return false; | |
568 | } | |
569 | ||
570 | static void | |
571 | str_to_action(char *str, struct ofpbuf *b) | |
572 | { | |
573 | char *act, *arg; | |
574 | char *saveptr = NULL; | |
575 | bool drop = false; | |
576 | int n_actions; | |
577 | ||
578 | for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act; | |
579 | act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++) | |
580 | { | |
581 | uint16_t port; | |
582 | ||
583 | if (drop) { | |
584 | ovs_fatal(0, "Drop actions must not be followed by other actions"); | |
585 | } | |
586 | ||
587 | /* Arguments are separated by colons */ | |
588 | arg = strchr(act, ':'); | |
589 | if (arg) { | |
590 | *arg = '\0'; | |
591 | arg++; | |
592 | } | |
593 | ||
594 | if (!strcasecmp(act, "mod_vlan_vid")) { | |
595 | struct ofp_action_vlan_vid *va; | |
596 | va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID); | |
597 | va->vlan_vid = htons(str_to_u32(arg)); | |
598 | } else if (!strcasecmp(act, "mod_vlan_pcp")) { | |
599 | struct ofp_action_vlan_pcp *va; | |
600 | va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP); | |
601 | va->vlan_pcp = str_to_u32(arg); | |
602 | } else if (!strcasecmp(act, "strip_vlan")) { | |
603 | struct ofp_action_header *ah; | |
604 | ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN); | |
605 | ah->type = htons(OFPAT_STRIP_VLAN); | |
606 | } else if (!strcasecmp(act, "mod_dl_src")) { | |
607 | put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg); | |
608 | } else if (!strcasecmp(act, "mod_dl_dst")) { | |
609 | put_dl_addr_action(b, OFPAT_SET_DL_DST, arg); | |
e423eca6 JP |
610 | } else if (!strcasecmp(act, "mod_nw_src")) { |
611 | struct ofp_action_nw_addr *na; | |
612 | na = put_action(b, sizeof *na, OFPAT_SET_NW_SRC); | |
613 | str_to_ip(arg, &na->nw_addr); | |
614 | } else if (!strcasecmp(act, "mod_nw_dst")) { | |
615 | struct ofp_action_nw_addr *na; | |
616 | na = put_action(b, sizeof *na, OFPAT_SET_NW_DST); | |
617 | str_to_ip(arg, &na->nw_addr); | |
618 | } else if (!strcasecmp(act, "mod_tp_src")) { | |
619 | struct ofp_action_tp_port *ta; | |
620 | ta = put_action(b, sizeof *ta, OFPAT_SET_TP_SRC); | |
621 | ta->tp_port = htons(str_to_u32(arg)); | |
622 | } else if (!strcasecmp(act, "mod_tp_dst")) { | |
623 | struct ofp_action_tp_port *ta; | |
624 | ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST); | |
625 | ta->tp_port = htons(str_to_u32(arg)); | |
959a2ecd JP |
626 | } else if (!strcasecmp(act, "mod_nw_tos")) { |
627 | struct ofp_action_nw_tos *nt; | |
628 | nt = put_action(b, sizeof *nt, OFPAT_SET_NW_TOS); | |
629 | nt->nw_tos = str_to_u32(arg); | |
064af421 BP |
630 | } else if (!strcasecmp(act, "output")) { |
631 | put_output_action(b, str_to_u32(arg)); | |
632 | } else if (!strcasecmp(act, "drop")) { | |
633 | /* A drop action in OpenFlow occurs by just not setting | |
634 | * an action. */ | |
635 | drop = true; | |
636 | if (n_actions) { | |
637 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
638 | "actions"); | |
639 | } | |
640 | } else if (!strcasecmp(act, "CONTROLLER")) { | |
641 | struct ofp_action_output *oao; | |
642 | oao = put_output_action(b, OFPP_CONTROLLER); | |
643 | ||
644 | /* Unless a numeric argument is specified, we send the whole | |
645 | * packet to the controller. */ | |
5c9a0b82 | 646 | if (arg && (strspn(arg, "0123456789") == strlen(arg))) { |
064af421 | 647 | oao->max_len = htons(str_to_u32(arg)); |
080ec396 BP |
648 | } else { |
649 | oao->max_len = htons(UINT16_MAX); | |
064af421 BP |
650 | } |
651 | } else if (parse_port_name(act, &port)) { | |
652 | put_output_action(b, port); | |
653 | } else if (strspn(act, "0123456789") == strlen(act)) { | |
654 | put_output_action(b, str_to_u32(act)); | |
655 | } else { | |
656 | ovs_fatal(0, "Unknown action: %s", act); | |
657 | } | |
658 | } | |
659 | } | |
660 | ||
661 | struct protocol { | |
662 | const char *name; | |
663 | uint16_t dl_type; | |
664 | uint8_t nw_proto; | |
665 | }; | |
666 | ||
667 | static bool | |
668 | parse_protocol(const char *name, const struct protocol **p_out) | |
669 | { | |
670 | static const struct protocol protocols[] = { | |
671 | { "ip", ETH_TYPE_IP, 0 }, | |
672 | { "arp", ETH_TYPE_ARP, 0 }, | |
673 | { "icmp", ETH_TYPE_IP, IP_TYPE_ICMP }, | |
674 | { "tcp", ETH_TYPE_IP, IP_TYPE_TCP }, | |
675 | { "udp", ETH_TYPE_IP, IP_TYPE_UDP }, | |
676 | }; | |
677 | const struct protocol *p; | |
678 | ||
679 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
680 | if (!strcmp(p->name, name)) { | |
681 | *p_out = p; | |
682 | return true; | |
683 | } | |
684 | } | |
685 | *p_out = NULL; | |
686 | return false; | |
687 | } | |
688 | ||
689 | struct field { | |
690 | const char *name; | |
691 | uint32_t wildcard; | |
692 | enum { F_U8, F_U16, F_MAC, F_IP } type; | |
693 | size_t offset, shift; | |
694 | }; | |
695 | ||
696 | static bool | |
697 | parse_field(const char *name, const struct field **f_out) | |
698 | { | |
699 | #define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER) | |
700 | static const struct field fields[] = { | |
701 | { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 }, | |
702 | { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 }, | |
959a2ecd | 703 | { "dl_vlan_pcp", OFPFW_DL_VLAN_PCP, F_U8, F_OFS(dl_vlan_pcp), 0 }, |
064af421 BP |
704 | { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 }, |
705 | { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 }, | |
706 | { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 }, | |
707 | { "nw_src", OFPFW_NW_SRC_MASK, F_IP, | |
708 | F_OFS(nw_src), OFPFW_NW_SRC_SHIFT }, | |
709 | { "nw_dst", OFPFW_NW_DST_MASK, F_IP, | |
710 | F_OFS(nw_dst), OFPFW_NW_DST_SHIFT }, | |
711 | { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 }, | |
834377ea | 712 | { "nw_tos", OFPFW_NW_TOS, F_U8, F_OFS(nw_tos), 0 }, |
064af421 BP |
713 | { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 }, |
714 | { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 }, | |
715 | { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 }, | |
716 | { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code), 0 } | |
717 | }; | |
718 | const struct field *f; | |
719 | ||
720 | for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) { | |
721 | if (!strcmp(f->name, name)) { | |
722 | *f_out = f; | |
723 | return true; | |
724 | } | |
725 | } | |
726 | *f_out = NULL; | |
727 | return false; | |
728 | } | |
729 | ||
730 | static void | |
731 | str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions, | |
732 | uint8_t *table_idx, uint16_t *out_port, uint16_t *priority, | |
39997502 JP |
733 | uint16_t *idle_timeout, uint16_t *hard_timeout, |
734 | uint64_t *cookie) | |
064af421 BP |
735 | { |
736 | char *save_ptr = NULL; | |
737 | char *name; | |
738 | uint32_t wildcards; | |
739 | ||
740 | if (table_idx) { | |
741 | *table_idx = 0xff; | |
742 | } | |
743 | if (out_port) { | |
744 | *out_port = OFPP_NONE; | |
745 | } | |
746 | if (priority) { | |
747 | *priority = OFP_DEFAULT_PRIORITY; | |
748 | } | |
749 | if (idle_timeout) { | |
750 | *idle_timeout = DEFAULT_IDLE_TIMEOUT; | |
751 | } | |
752 | if (hard_timeout) { | |
753 | *hard_timeout = OFP_FLOW_PERMANENT; | |
754 | } | |
39997502 JP |
755 | if (cookie) { |
756 | *cookie = 0; | |
757 | } | |
064af421 BP |
758 | if (actions) { |
759 | char *act_str = strstr(string, "action"); | |
760 | if (!act_str) { | |
761 | ovs_fatal(0, "must specify an action"); | |
762 | } | |
763 | *(act_str-1) = '\0'; | |
764 | ||
765 | act_str = strchr(act_str, '='); | |
766 | if (!act_str) { | |
767 | ovs_fatal(0, "must specify an action"); | |
768 | } | |
769 | ||
770 | act_str++; | |
771 | ||
772 | str_to_action(act_str, actions); | |
773 | } | |
774 | memset(match, 0, sizeof *match); | |
775 | wildcards = OFPFW_ALL; | |
776 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
777 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
778 | const struct protocol *p; | |
779 | ||
780 | if (parse_protocol(name, &p)) { | |
781 | wildcards &= ~OFPFW_DL_TYPE; | |
782 | match->dl_type = htons(p->dl_type); | |
783 | if (p->nw_proto) { | |
784 | wildcards &= ~OFPFW_NW_PROTO; | |
785 | match->nw_proto = p->nw_proto; | |
786 | } | |
787 | } else { | |
788 | const struct field *f; | |
789 | char *value; | |
790 | ||
791 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
792 | if (!value) { | |
793 | ovs_fatal(0, "field %s missing value", name); | |
794 | } | |
795 | ||
796 | if (table_idx && !strcmp(name, "table")) { | |
797 | *table_idx = atoi(value); | |
798 | } else if (out_port && !strcmp(name, "out_port")) { | |
799 | *out_port = atoi(value); | |
800 | } else if (priority && !strcmp(name, "priority")) { | |
801 | *priority = atoi(value); | |
802 | } else if (idle_timeout && !strcmp(name, "idle_timeout")) { | |
803 | *idle_timeout = atoi(value); | |
804 | } else if (hard_timeout && !strcmp(name, "hard_timeout")) { | |
805 | *hard_timeout = atoi(value); | |
39997502 JP |
806 | } else if (cookie && !strcmp(name, "cookie")) { |
807 | *cookie = atoi(value); | |
064af421 BP |
808 | } else if (parse_field(name, &f)) { |
809 | void *data = (char *) match + f->offset; | |
810 | if (!strcmp(value, "*") || !strcmp(value, "ANY")) { | |
811 | wildcards |= f->wildcard; | |
812 | } else { | |
813 | wildcards &= ~f->wildcard; | |
814 | if (f->wildcard == OFPFW_IN_PORT | |
815 | && parse_port_name(value, (uint16_t *) data)) { | |
816 | /* Nothing to do. */ | |
817 | } else if (f->type == F_U8) { | |
818 | *(uint8_t *) data = str_to_u32(value); | |
819 | } else if (f->type == F_U16) { | |
820 | *(uint16_t *) data = htons(str_to_u32(value)); | |
821 | } else if (f->type == F_MAC) { | |
822 | str_to_mac(value, data); | |
823 | } else if (f->type == F_IP) { | |
824 | wildcards |= str_to_ip(value, data) << f->shift; | |
825 | } else { | |
826 | NOT_REACHED(); | |
827 | } | |
828 | } | |
829 | } else { | |
830 | ovs_fatal(0, "unknown keyword %s", name); | |
831 | } | |
832 | } | |
833 | } | |
834 | match->wildcards = htonl(wildcards); | |
835 | } | |
836 | ||
837 | static void | |
675febfa | 838 | do_dump_flows(int argc, char *argv[]) |
064af421 BP |
839 | { |
840 | struct ofp_flow_stats_request *req; | |
841 | uint16_t out_port; | |
842 | struct ofpbuf *request; | |
843 | ||
844 | req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request); | |
845 | str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, | |
39997502 | 846 | &req->table_id, &out_port, NULL, NULL, NULL, NULL); |
064af421 BP |
847 | memset(&req->pad, 0, sizeof req->pad); |
848 | req->out_port = htons(out_port); | |
849 | ||
850 | dump_stats_transaction(argv[1], request); | |
851 | } | |
852 | ||
853 | static void | |
675febfa | 854 | do_dump_aggregate(int argc, char *argv[]) |
064af421 BP |
855 | { |
856 | struct ofp_aggregate_stats_request *req; | |
857 | struct ofpbuf *request; | |
858 | uint16_t out_port; | |
859 | ||
860 | req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request); | |
861 | str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, | |
39997502 | 862 | &req->table_id, &out_port, NULL, NULL, NULL, NULL); |
064af421 BP |
863 | memset(&req->pad, 0, sizeof req->pad); |
864 | req->out_port = htons(out_port); | |
865 | ||
866 | dump_stats_transaction(argv[1], request); | |
867 | } | |
868 | ||
869 | static void | |
c69ee87c | 870 | do_add_flow(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
871 | { |
872 | struct vconn *vconn; | |
873 | struct ofpbuf *buffer; | |
874 | struct ofp_flow_mod *ofm; | |
875 | uint16_t priority, idle_timeout, hard_timeout; | |
39997502 | 876 | uint64_t cookie; |
064af421 BP |
877 | struct ofp_match match; |
878 | ||
879 | /* Parse and send. str_to_flow() will expand and reallocate the data in | |
880 | * 'buffer', so we can't keep pointers to across the str_to_flow() call. */ | |
881 | make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); | |
882 | str_to_flow(argv[2], &match, buffer, | |
39997502 JP |
883 | NULL, NULL, &priority, &idle_timeout, &hard_timeout, |
884 | &cookie); | |
064af421 BP |
885 | ofm = buffer->data; |
886 | ofm->match = match; | |
887 | ofm->command = htons(OFPFC_ADD); | |
39997502 | 888 | ofm->cookie = htonll(cookie); |
064af421 BP |
889 | ofm->idle_timeout = htons(idle_timeout); |
890 | ofm->hard_timeout = htons(hard_timeout); | |
891 | ofm->buffer_id = htonl(UINT32_MAX); | |
892 | ofm->priority = htons(priority); | |
064af421 BP |
893 | |
894 | open_vconn(argv[1], &vconn); | |
895 | send_openflow_buffer(vconn, buffer); | |
896 | vconn_close(vconn); | |
897 | } | |
898 | ||
899 | static void | |
c69ee87c | 900 | do_add_flows(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
901 | { |
902 | struct vconn *vconn; | |
903 | FILE *file; | |
904 | char line[1024]; | |
905 | ||
906 | file = fopen(argv[2], "r"); | |
907 | if (file == NULL) { | |
908 | ovs_fatal(errno, "%s: open", argv[2]); | |
909 | } | |
910 | ||
911 | open_vconn(argv[1], &vconn); | |
912 | while (fgets(line, sizeof line, file)) { | |
913 | struct ofpbuf *buffer; | |
914 | struct ofp_flow_mod *ofm; | |
915 | uint16_t priority, idle_timeout, hard_timeout; | |
39997502 | 916 | uint64_t cookie; |
064af421 BP |
917 | struct ofp_match match; |
918 | ||
919 | char *comment; | |
920 | ||
921 | /* Delete comments. */ | |
922 | comment = strchr(line, '#'); | |
923 | if (comment) { | |
924 | *comment = '\0'; | |
925 | } | |
926 | ||
927 | /* Drop empty lines. */ | |
928 | if (line[strspn(line, " \t\n")] == '\0') { | |
929 | continue; | |
930 | } | |
931 | ||
932 | /* Parse and send. str_to_flow() will expand and reallocate the data | |
933 | * in 'buffer', so we can't keep pointers to across the str_to_flow() | |
934 | * call. */ | |
62ee3464 | 935 | make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); |
064af421 | 936 | str_to_flow(line, &match, buffer, |
39997502 JP |
937 | NULL, NULL, &priority, &idle_timeout, &hard_timeout, |
938 | &cookie); | |
064af421 BP |
939 | ofm = buffer->data; |
940 | ofm->match = match; | |
941 | ofm->command = htons(OFPFC_ADD); | |
39997502 | 942 | ofm->cookie = htonll(cookie); |
064af421 BP |
943 | ofm->idle_timeout = htons(idle_timeout); |
944 | ofm->hard_timeout = htons(hard_timeout); | |
945 | ofm->buffer_id = htonl(UINT32_MAX); | |
946 | ofm->priority = htons(priority); | |
064af421 BP |
947 | |
948 | send_openflow_buffer(vconn, buffer); | |
949 | } | |
950 | vconn_close(vconn); | |
951 | fclose(file); | |
952 | } | |
953 | ||
954 | static void | |
c69ee87c | 955 | do_mod_flows(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
956 | { |
957 | uint16_t priority, idle_timeout, hard_timeout; | |
39997502 | 958 | uint64_t cookie; |
064af421 BP |
959 | struct vconn *vconn; |
960 | struct ofpbuf *buffer; | |
961 | struct ofp_flow_mod *ofm; | |
6a5d4138 | 962 | struct ofp_match match; |
064af421 | 963 | |
6a5d4138 JT |
964 | /* Parse and send. str_to_flow() will expand and reallocate the data in |
965 | * 'buffer', so we can't keep pointers to across the str_to_flow() call. */ | |
966 | make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); | |
967 | str_to_flow(argv[2], &match, buffer, | |
39997502 JP |
968 | NULL, NULL, &priority, &idle_timeout, &hard_timeout, |
969 | &cookie); | |
6a5d4138 JT |
970 | ofm = buffer->data; |
971 | ofm->match = match; | |
675febfa | 972 | if (strict) { |
064af421 BP |
973 | ofm->command = htons(OFPFC_MODIFY_STRICT); |
974 | } else { | |
975 | ofm->command = htons(OFPFC_MODIFY); | |
976 | } | |
977 | ofm->idle_timeout = htons(idle_timeout); | |
978 | ofm->hard_timeout = htons(hard_timeout); | |
39997502 | 979 | ofm->cookie = htonll(cookie); |
064af421 BP |
980 | ofm->buffer_id = htonl(UINT32_MAX); |
981 | ofm->priority = htons(priority); | |
064af421 BP |
982 | |
983 | open_vconn(argv[1], &vconn); | |
984 | send_openflow_buffer(vconn, buffer); | |
985 | vconn_close(vconn); | |
986 | } | |
987 | ||
675febfa | 988 | static void do_del_flows(int argc, char *argv[]) |
064af421 BP |
989 | { |
990 | struct vconn *vconn; | |
991 | uint16_t priority; | |
992 | uint16_t out_port; | |
993 | struct ofpbuf *buffer; | |
994 | struct ofp_flow_mod *ofm; | |
995 | ||
996 | /* Parse and send. */ | |
997 | ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); | |
998 | str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL, | |
39997502 | 999 | &out_port, &priority, NULL, NULL, NULL); |
675febfa | 1000 | if (strict) { |
064af421 BP |
1001 | ofm->command = htons(OFPFC_DELETE_STRICT); |
1002 | } else { | |
1003 | ofm->command = htons(OFPFC_DELETE); | |
1004 | } | |
1005 | ofm->idle_timeout = htons(0); | |
1006 | ofm->hard_timeout = htons(0); | |
1007 | ofm->buffer_id = htonl(UINT32_MAX); | |
1008 | ofm->out_port = htons(out_port); | |
1009 | ofm->priority = htons(priority); | |
064af421 BP |
1010 | |
1011 | open_vconn(argv[1], &vconn); | |
1012 | send_openflow_buffer(vconn, buffer); | |
1013 | vconn_close(vconn); | |
1014 | } | |
1015 | ||
1016 | static void | |
c69ee87c | 1017 | do_monitor(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
1018 | { |
1019 | struct vconn *vconn; | |
1020 | ||
1021 | open_vconn(argv[1], &vconn); | |
1022 | if (argc > 2) { | |
1023 | int miss_send_len = atoi(argv[2]); | |
064af421 BP |
1024 | struct ofp_switch_config *osc; |
1025 | struct ofpbuf *buf; | |
1026 | ||
1027 | osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf); | |
064af421 BP |
1028 | osc->miss_send_len = htons(miss_send_len); |
1029 | send_openflow_buffer(vconn, buf); | |
1030 | } | |
1031 | for (;;) { | |
1032 | struct ofpbuf *b; | |
1033 | run(vconn_recv_block(vconn, &b), "vconn_recv"); | |
1034 | ofp_print(stderr, b->data, b->size, 2); | |
1035 | ofpbuf_delete(b); | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | static void | |
abaad8cf | 1040 | do_dump_ports(int argc, char *argv[]) |
064af421 | 1041 | { |
abaad8cf JP |
1042 | struct ofp_port_stats_request *req; |
1043 | struct ofpbuf *request; | |
1044 | uint16_t port; | |
1045 | ||
1046 | req = alloc_stats_request(sizeof *req, OFPST_PORT, &request); | |
1047 | port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE; | |
1048 | req->port_no = htons(port); | |
1049 | dump_stats_transaction(argv[1], request); | |
064af421 BP |
1050 | } |
1051 | ||
1052 | static void | |
c69ee87c | 1053 | do_probe(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
1054 | { |
1055 | struct ofpbuf *request; | |
1056 | struct vconn *vconn; | |
1057 | struct ofpbuf *reply; | |
1058 | ||
1059 | make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request); | |
1060 | open_vconn(argv[1], &vconn); | |
1061 | run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]); | |
1062 | if (reply->size != sizeof(struct ofp_header)) { | |
1063 | ovs_fatal(0, "reply does not match request"); | |
1064 | } | |
1065 | ofpbuf_delete(reply); | |
1066 | vconn_close(vconn); | |
1067 | } | |
1068 | ||
1069 | static void | |
c69ee87c | 1070 | do_mod_port(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
1071 | { |
1072 | struct ofpbuf *request, *reply; | |
1073 | struct ofp_switch_features *osf; | |
1074 | struct ofp_port_mod *opm; | |
1075 | struct vconn *vconn; | |
1076 | char *endptr; | |
1077 | int n_ports; | |
1078 | int port_idx; | |
1079 | int port_no; | |
1080 | ||
1081 | ||
1082 | /* Check if the argument is a port index. Otherwise, treat it as | |
1083 | * the port name. */ | |
1084 | port_no = strtol(argv[2], &endptr, 10); | |
1085 | if (port_no == 0 && endptr == argv[2]) { | |
1086 | port_no = -1; | |
1087 | } | |
1088 | ||
1089 | /* Send a "Features Request" to get the information we need in order | |
1090 | * to modify the port. */ | |
1091 | make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request); | |
1092 | open_vconn(argv[1], &vconn); | |
1093 | run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]); | |
1094 | ||
1095 | osf = reply->data; | |
1096 | n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports; | |
1097 | ||
1098 | for (port_idx = 0; port_idx < n_ports; port_idx++) { | |
1099 | if (port_no != -1) { | |
1100 | /* Check argument as a port index */ | |
1101 | if (osf->ports[port_idx].port_no == htons(port_no)) { | |
1102 | break; | |
1103 | } | |
1104 | } else { | |
1105 | /* Check argument as an interface name */ | |
1106 | if (!strncmp((char *)osf->ports[port_idx].name, argv[2], | |
1107 | sizeof osf->ports[0].name)) { | |
1108 | break; | |
1109 | } | |
1110 | ||
1111 | } | |
1112 | } | |
1113 | if (port_idx == n_ports) { | |
1114 | ovs_fatal(0, "couldn't find monitored port: %s", argv[2]); | |
1115 | } | |
1116 | ||
1117 | opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request); | |
1118 | opm->port_no = osf->ports[port_idx].port_no; | |
1119 | memcpy(opm->hw_addr, osf->ports[port_idx].hw_addr, sizeof opm->hw_addr); | |
1120 | opm->config = htonl(0); | |
1121 | opm->mask = htonl(0); | |
1122 | opm->advertise = htonl(0); | |
1123 | ||
1124 | printf("modifying port: %s\n", osf->ports[port_idx].name); | |
1125 | ||
1126 | if (!strncasecmp(argv[3], MOD_PORT_CMD_UP, sizeof MOD_PORT_CMD_UP)) { | |
1127 | opm->mask |= htonl(OFPPC_PORT_DOWN); | |
1128 | } else if (!strncasecmp(argv[3], MOD_PORT_CMD_DOWN, | |
1129 | sizeof MOD_PORT_CMD_DOWN)) { | |
1130 | opm->mask |= htonl(OFPPC_PORT_DOWN); | |
1131 | opm->config |= htonl(OFPPC_PORT_DOWN); | |
1132 | } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD, | |
1133 | sizeof MOD_PORT_CMD_FLOOD)) { | |
1134 | opm->mask |= htonl(OFPPC_NO_FLOOD); | |
1135 | } else if (!strncasecmp(argv[3], MOD_PORT_CMD_NOFLOOD, | |
1136 | sizeof MOD_PORT_CMD_NOFLOOD)) { | |
1137 | opm->mask |= htonl(OFPPC_NO_FLOOD); | |
1138 | opm->config |= htonl(OFPPC_NO_FLOOD); | |
1139 | } else { | |
1140 | ovs_fatal(0, "unknown mod-port command '%s'", argv[3]); | |
1141 | } | |
1142 | ||
1143 | send_openflow_buffer(vconn, request); | |
1144 | ||
1145 | ofpbuf_delete(reply); | |
1146 | vconn_close(vconn); | |
1147 | } | |
1148 | ||
1149 | static void | |
675febfa | 1150 | do_ping(int argc, char *argv[]) |
064af421 BP |
1151 | { |
1152 | size_t max_payload = 65535 - sizeof(struct ofp_header); | |
1153 | unsigned int payload; | |
1154 | struct vconn *vconn; | |
1155 | int i; | |
1156 | ||
1157 | payload = argc > 2 ? atoi(argv[2]) : 64; | |
1158 | if (payload > max_payload) { | |
1159 | ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload); | |
1160 | } | |
1161 | ||
1162 | open_vconn(argv[1], &vconn); | |
1163 | for (i = 0; i < 10; i++) { | |
1164 | struct timeval start, end; | |
1165 | struct ofpbuf *request, *reply; | |
1166 | struct ofp_header *rq_hdr, *rpy_hdr; | |
1167 | ||
1168 | rq_hdr = make_openflow(sizeof(struct ofp_header) + payload, | |
1169 | OFPT_ECHO_REQUEST, &request); | |
1170 | random_bytes(rq_hdr + 1, payload); | |
1171 | ||
1172 | gettimeofday(&start, NULL); | |
1173 | run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact"); | |
1174 | gettimeofday(&end, NULL); | |
1175 | ||
1176 | rpy_hdr = reply->data; | |
1177 | if (reply->size != request->size | |
1178 | || memcmp(rpy_hdr + 1, rq_hdr + 1, payload) | |
1179 | || rpy_hdr->xid != rq_hdr->xid | |
1180 | || rpy_hdr->type != OFPT_ECHO_REPLY) { | |
1181 | printf("Reply does not match request. Request:\n"); | |
1182 | ofp_print(stdout, request, request->size, 2); | |
1183 | printf("Reply:\n"); | |
1184 | ofp_print(stdout, reply, reply->size, 2); | |
1185 | } | |
2886875a | 1186 | printf("%zu bytes from %s: xid=%08"PRIx32" time=%.1f ms\n", |
064af421 BP |
1187 | reply->size - sizeof *rpy_hdr, argv[1], rpy_hdr->xid, |
1188 | (1000*(double)(end.tv_sec - start.tv_sec)) | |
1189 | + (.001*(end.tv_usec - start.tv_usec))); | |
1190 | ofpbuf_delete(request); | |
1191 | ofpbuf_delete(reply); | |
1192 | } | |
1193 | vconn_close(vconn); | |
1194 | } | |
1195 | ||
1196 | static void | |
c69ee87c | 1197 | do_benchmark(int argc OVS_UNUSED, char *argv[]) |
064af421 BP |
1198 | { |
1199 | size_t max_payload = 65535 - sizeof(struct ofp_header); | |
1200 | struct timeval start, end; | |
1201 | unsigned int payload_size, message_size; | |
1202 | struct vconn *vconn; | |
1203 | double duration; | |
1204 | int count; | |
1205 | int i; | |
1206 | ||
1207 | payload_size = atoi(argv[2]); | |
1208 | if (payload_size > max_payload) { | |
1209 | ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload); | |
1210 | } | |
1211 | message_size = sizeof(struct ofp_header) + payload_size; | |
1212 | ||
1213 | count = atoi(argv[3]); | |
1214 | ||
1215 | printf("Sending %d packets * %u bytes (with header) = %u bytes total\n", | |
1216 | count, message_size, count * message_size); | |
1217 | ||
1218 | open_vconn(argv[1], &vconn); | |
1219 | gettimeofday(&start, NULL); | |
1220 | for (i = 0; i < count; i++) { | |
1221 | struct ofpbuf *request, *reply; | |
1222 | struct ofp_header *rq_hdr; | |
1223 | ||
1224 | rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request); | |
1225 | memset(rq_hdr + 1, 0, payload_size); | |
1226 | run(vconn_transact(vconn, request, &reply), "transact"); | |
1227 | ofpbuf_delete(reply); | |
1228 | } | |
1229 | gettimeofday(&end, NULL); | |
1230 | vconn_close(vconn); | |
1231 | ||
1232 | duration = ((1000*(double)(end.tv_sec - start.tv_sec)) | |
1233 | + (.001*(end.tv_usec - start.tv_usec))); | |
1234 | printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n", | |
1235 | duration, count / (duration / 1000.0), | |
1236 | count * message_size / (duration / 1000.0)); | |
1237 | } | |
1238 | ||
064af421 | 1239 | static void |
c69ee87c | 1240 | do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) |
064af421 BP |
1241 | { |
1242 | usage(); | |
1243 | } | |
1244 | ||
675febfa | 1245 | static const struct command all_commands[] = { |
064af421 BP |
1246 | { "show", 1, 1, do_show }, |
1247 | { "status", 1, 2, do_status }, | |
1248 | { "monitor", 1, 3, do_monitor }, | |
1249 | { "dump-desc", 1, 1, do_dump_desc }, | |
1250 | { "dump-tables", 1, 1, do_dump_tables }, | |
1251 | { "dump-flows", 1, 2, do_dump_flows }, | |
1252 | { "dump-aggregate", 1, 2, do_dump_aggregate }, | |
1253 | { "add-flow", 2, 2, do_add_flow }, | |
1254 | { "add-flows", 2, 2, do_add_flows }, | |
1255 | { "mod-flows", 2, 2, do_mod_flows }, | |
1256 | { "del-flows", 1, 2, do_del_flows }, | |
abaad8cf | 1257 | { "dump-ports", 1, 2, do_dump_ports }, |
064af421 BP |
1258 | { "mod-port", 3, 3, do_mod_port }, |
1259 | { "probe", 1, 1, do_probe }, | |
1260 | { "ping", 1, 2, do_ping }, | |
1261 | { "benchmark", 3, 3, do_benchmark }, | |
064af421 BP |
1262 | { "help", 0, INT_MAX, do_help }, |
1263 | { NULL, 0, 0, NULL }, | |
1264 | }; |