X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=utilities%2Fovs-ofctl.c;h=39c3dae96a5e1620ee1fe44fcc25b883911a13f3;hb=e8087a87a3155656596d92ebecfa37841b637ef7;hp=3a85f57ad15ac93baf3c3f415cd01a66def20022;hpb=4ce9c31573b0dc01696d1b0080b67b726b31cc19;p=openvswitch diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 3a85f57a..39c3dae9 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -55,6 +56,8 @@ #include "util.h" #include "vconn.h" #include "vlog.h" +#include "meta-flow.h" +#include "sort.h" VLOG_DEFINE_THIS_MODULE(ofctl); @@ -83,11 +86,24 @@ static int verbosity; * "snoop" command? */ static bool timestamp; +/* --sort, --rsort: Sort order. */ +enum sort_order { SORT_ASC, SORT_DESC }; +struct sort_criterion { + const struct mf_field *field; /* NULL means to sort by priority. */ + enum sort_order order; +}; +static struct sort_criterion *criteria; +static size_t n_criteria, allocated_criteria; + static const struct command all_commands[]; static void usage(void) NO_RETURN; static void parse_options(int argc, char *argv[]); +static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid, + struct ofpbuf **replyp, + struct ofputil_flow_stats *, + struct ofpbuf *ofpacts); int main(int argc, char *argv[]) { @@ -98,6 +114,27 @@ main(int argc, char *argv[]) return 0; } +static void +add_sort_criterion(enum sort_order order, const char *field) +{ + struct sort_criterion *sc; + + if (n_criteria >= allocated_criteria) { + criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria); + } + + sc = &criteria[n_criteria++]; + if (!field || !strcasecmp(field, "priority")) { + sc->field = NULL; + } else { + sc->field = mf_from_name(field); + if (!sc->field) { + ovs_fatal(0, "%s: unknown field name", field); + } + } + sc->order = order; +} + static void parse_options(int argc, char *argv[]) { @@ -105,6 +142,8 @@ parse_options(int argc, char *argv[]) OPT_STRICT = UCHAR_MAX + 1, OPT_READD, OPT_TIMESTAMP, + OPT_SORT, + OPT_RSORT, DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS }; @@ -116,6 +155,8 @@ parse_options(int argc, char *argv[]) {"packet-in-format", required_argument, NULL, 'P'}, {"more", no_argument, NULL, 'm'}, {"timestamp", no_argument, NULL, OPT_TIMESTAMP}, + {"sort", optional_argument, NULL, OPT_SORT}, + {"rsort", optional_argument, NULL, OPT_RSORT}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, @@ -183,6 +224,14 @@ parse_options(int argc, char *argv[]) timestamp = true; break; + case OPT_SORT: + add_sort_criterion(SORT_ASC, optarg); + break; + + case OPT_RSORT: + add_sort_criterion(SORT_DESC, optarg); + break; + DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS @@ -194,6 +243,12 @@ parse_options(int argc, char *argv[]) abort(); } } + + if (n_criteria) { + /* Always do a final sort pass based on priority. */ + add_sort_criterion(SORT_DESC, "priority"); + } + free(short_options); } @@ -224,7 +279,7 @@ usage(void) " diff-flows SOURCE1 SOURCE2 compare flows from two sources\n" " packet-out SWITCH IN_PORT ACTIONS PACKET...\n" " execute ACTIONS on PACKET\n" - " monitor SWITCH [MISSLEN] [invalid_ttl]\n" + " monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n" " print packets received from SWITCH\n" " snoop SWITCH snoop on SWITCH and its controller\n" "\nFor OpenFlow switches and controllers:\n" @@ -244,6 +299,8 @@ usage(void) " -m, --more be more verbose printing OpenFlow\n" " --timestamp (monitor, snoop) print timestamps\n" " -t, --timeout=SECS give up after SECS seconds\n" + " --sort[=field] sort in ascending order\n" + " --rsort[=field] sort in descending order\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); @@ -770,12 +827,12 @@ set_protocol_for_flow_dump(struct vconn *vconn, } } -static void -ofctl_dump_flows__(int argc, char *argv[], bool aggregate) +static struct vconn * +prepare_dump_flows(int argc, char *argv[], bool aggregate, + struct ofpbuf **requestp) { enum ofputil_protocol usable_protocols, protocol; struct ofputil_flow_stats_request fsr; - struct ofpbuf *request; struct vconn *vconn; parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : ""); @@ -783,15 +840,121 @@ ofctl_dump_flows__(int argc, char *argv[], bool aggregate) protocol = open_vconn(argv[1], &vconn); protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); - request = ofputil_encode_flow_stats_request(&fsr, protocol); + *requestp = ofputil_encode_flow_stats_request(&fsr, protocol); + return vconn; +} + +static void +ofctl_dump_flows__(int argc, char *argv[], bool aggregate) +{ + struct ofpbuf *request; + struct vconn *vconn; + + vconn = prepare_dump_flows(argc, argv, aggregate, &request); dump_stats_transaction__(vconn, request); vconn_close(vconn); } +static int +compare_flows(const void *afs_, const void *bfs_) +{ + const struct ofputil_flow_stats *afs = afs_; + const struct ofputil_flow_stats *bfs = bfs_; + const struct cls_rule *a = &afs->rule; + const struct cls_rule *b = &bfs->rule; + const struct sort_criterion *sc; + + for (sc = criteria; sc < &criteria[n_criteria]; sc++) { + const struct mf_field *f = sc->field; + int ret; + + if (!f) { + ret = a->priority < b->priority ? -1 : a->priority > b->priority; + } else { + bool ina, inb; + + ina = mf_are_prereqs_ok(f, &a->flow) && !mf_is_all_wild(f, &a->wc); + inb = mf_are_prereqs_ok(f, &b->flow) && !mf_is_all_wild(f, &b->wc); + if (ina != inb) { + /* Skip the test for sc->order, so that missing fields always + * sort to the end whether we're sorting in ascending or + * descending order. */ + return ina ? -1 : 1; + } else { + union mf_value aval, bval; + + mf_get_value(f, &a->flow, &aval); + mf_get_value(f, &b->flow, &bval); + ret = memcmp(&aval, &bval, f->n_bytes); + } + } + + if (ret) { + return sc->order == SORT_ASC ? ret : -ret; + } + } + + return 0; +} + static void ofctl_dump_flows(int argc, char *argv[]) { - return ofctl_dump_flows__(argc, argv, false); + if (!n_criteria) { + return ofctl_dump_flows__(argc, argv, false); + } else { + struct ofputil_flow_stats *fses; + size_t n_fses, allocated_fses; + struct ofpbuf *request; + struct ofpbuf ofpacts; + struct ofpbuf *reply; + struct vconn *vconn; + ovs_be32 send_xid; + struct ds s; + size_t i; + + vconn = prepare_dump_flows(argc, argv, false, &request); + send_xid = ((struct ofp_header *) request->data)->xid; + send_openflow_buffer(vconn, request); + + fses = NULL; + n_fses = allocated_fses = 0; + reply = NULL; + ofpbuf_init(&ofpacts, 0); + for (;;) { + struct ofputil_flow_stats *fs; + + if (n_fses >= allocated_fses) { + fses = x2nrealloc(fses, &allocated_fses, sizeof *fses); + } + + fs = &fses[n_fses]; + if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs, + &ofpacts)) { + break; + } + fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len); + n_fses++; + } + ofpbuf_uninit(&ofpacts); + + qsort(fses, n_fses, sizeof *fses, compare_flows); + + ds_init(&s); + for (i = 0; i < n_fses; i++) { + ds_clear(&s); + ofp_print_flow_stats(&s, &fses[i]); + puts(ds_cstr(&s)); + } + ds_destroy(&s); + + for (i = 0; i < n_fses; i++) { + free(fses[i].ofpacts); + } + free(fses); + + vconn_close(vconn); + } } static void @@ -1093,12 +1256,41 @@ ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED, unixctl_command_reply(conn, NULL); } +static void +ofctl_block(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *blocked_) +{ + bool *blocked = blocked_; + + if (!*blocked) { + *blocked = true; + unixctl_command_reply(conn, NULL); + } else { + unixctl_command_reply(conn, "already blocking"); + } +} + +static void +ofctl_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *blocked_) +{ + bool *blocked = blocked_; + + if (*blocked) { + *blocked = false; + unixctl_command_reply(conn, NULL); + } else { + unixctl_command_reply(conn, "already unblocked"); + } +} + static void monitor_vconn(struct vconn *vconn) { struct barrier_aux barrier_aux = { vconn, NULL }; struct unixctl_server *server; bool exiting = false; + bool blocked = false; int error; daemon_save_fd(STDERR_FILENO); @@ -1114,6 +1306,11 @@ monitor_vconn(struct vconn *vconn) ofctl_barrier, &barrier_aux); unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1, ofctl_set_output_file, NULL); + + unixctl_command_register("ofctl/block", "", 0, 0, ofctl_block, &blocked); + unixctl_command_register("ofctl/unblock", "", 0, 0, ofctl_unblock, + &blocked); + daemonize_complete(); for (;;) { @@ -1121,8 +1318,7 @@ monitor_vconn(struct vconn *vconn) int retval; unixctl_server_run(server); - - for (;;) { + while (!blocked) { uint8_t msg_type; retval = vconn_recv(vconn, &b); @@ -1155,7 +1351,9 @@ monitor_vconn(struct vconn *vconn) vconn_run(vconn); vconn_run_wait(vconn); - vconn_recv_wait(vconn); + if (!blocked) { + vconn_recv_wait(vconn); + } unixctl_server_wait(server); poll_block(); } @@ -1167,20 +1365,34 @@ static void ofctl_monitor(int argc, char *argv[]) { struct vconn *vconn; + int i; open_vconn(argv[1], &vconn); - if (argc > 2) { - struct ofp_switch_config config; + for (i = 2; i < argc; i++) { + const char *arg = argv[i]; - fetch_switch_config(vconn, &config); - config.miss_send_len = htons(atoi(argv[2])); - set_switch_config(vconn, &config); - } - if (argc > 3) { - if (!strcmp(argv[3], "invalid_ttl")) { + if (isdigit((unsigned char) *arg)) { + struct ofp_switch_config config; + + fetch_switch_config(vconn, &config); + config.miss_send_len = htons(atoi(arg)); + set_switch_config(vconn, &config); + } else if (!strcmp(arg, "invalid_ttl")) { monitor_set_invalid_ttl_to_controller(vconn); + } else if (!strncmp(arg, "watch:", 6)) { + struct ofputil_flow_monitor_request fmr; + struct ofpbuf *msg; + + parse_flow_monitor_request(&fmr, arg + 6); + + msg = ofpbuf_new(0); + ofputil_append_flow_monitor_request(&fmr, msg); + dump_stats_transaction__(vconn, msg); + } else { + ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg); } } + if (preferred_packet_in_format >= 0) { set_packet_in_format(vconn, preferred_packet_in_format); } else { @@ -1691,6 +1903,7 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid, case EOF: flags = ((const struct ofp_stats_msg *) reply->l2)->flags; ofpbuf_delete(reply); + reply = NULL; if (!(flags & htons(OFPSF_REPLY_MORE))) { *replyp = NULL; return false; @@ -2020,7 +2233,8 @@ ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) } static void -print_differences(const void *a_, size_t a_len, +print_differences(const char *prefix, + const void *a_, size_t a_len, const void *b_, size_t b_len) { const uint8_t *a = a_; @@ -2029,14 +2243,15 @@ print_differences(const void *a_, size_t a_len, for (i = 0; i < MIN(a_len, b_len); i++) { if (a[i] != b[i]) { - printf("%2zu: %02"PRIx8" -> %02"PRIx8"\n", i, a[i], b[i]); + printf("%s%2zu: %02"PRIx8" -> %02"PRIx8"\n", + prefix, i, a[i], b[i]); } } for (i = a_len; i < b_len; i++) { - printf("%2zu: (none) -> %02"PRIx8"\n", i, b[i]); + printf("%s%2zu: (none) -> %02"PRIx8"\n", prefix, i, b[i]); } for (i = b_len; i < a_len; i++) { - printf("%2zu: %02"PRIx8" -> (none)\n", i, a[i]); + printf("%s%2zu: %02"PRIx8" -> (none)\n", prefix, i, a[i]); } } @@ -2086,7 +2301,7 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ofpbuf_init(&of10_out, 0); ofpacts_put_openflow10(ofpacts.data, ofpacts.size, &of10_out); - print_differences(of10_in.data, of10_in.size, + print_differences("", of10_in.data, of10_in.size, of10_out.data, of10_out.size); putchar('\n'); @@ -2097,6 +2312,54 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ds_destroy(&in); } +/* "parse-ofp10-match": reads a series of ofp10_match specifications as hex + * bytes from stdin, converts them to cls_rules, prints them as strings on + * stdout, and then converts them back to hex bytes and prints any differences + * from the input. */ +static void +ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + struct ds in; + + ds_init(&in); + while (!ds_get_preprocessed_line(&in, stdin)) { + struct ofpbuf match_in; + struct ofp10_match match_out; + struct ofp10_match match_normal; + struct cls_rule rule; + + /* Parse hex bytes. */ + ofpbuf_init(&match_in, 0); + if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') { + ovs_fatal(0, "Trailing garbage in hex data"); + } + if (match_in.size != sizeof(struct ofp10_match)) { + ovs_fatal(0, "Input is %zu bytes, expected %zu", + match_in.size, sizeof(struct ofp10_match)); + } + + /* Convert to cls_rule and print. */ + ofputil_cls_rule_from_ofp10_match(match_in.data, OFP_DEFAULT_PRIORITY, + &rule); + cls_rule_print(&rule); + + /* Convert back to ofp10_match and print differences from input. */ + ofputil_cls_rule_to_ofp10_match(&rule, &match_out); + print_differences("", match_in.data, match_in.size, + &match_out, sizeof match_out); + + /* Normalize, then convert and compare again. */ + ofputil_normalize_rule(&rule); + ofputil_cls_rule_to_ofp10_match(&rule, &match_normal); + print_differences("normal: ", &match_out, sizeof match_out, + &match_normal, sizeof match_normal); + putchar('\n'); + + ofpbuf_uninit(&match_in); + } + ds_destroy(&in); +} + /* "parse-ofp11-match": reads a series of ofp11_match specifications as hex * bytes from stdin, converts them to cls_rules, prints them as strings on * stdout, and then converts them back to hex bytes and prints any differences @@ -2138,7 +2401,7 @@ ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) /* Convert back to ofp11_match and print differences from input. */ ofputil_cls_rule_to_ofp11_match(&rule, &match_out); - print_differences(match_in.data, match_in.size, + print_differences("", match_in.data, match_in.size, &match_out, sizeof match_out); putchar('\n'); @@ -2194,7 +2457,7 @@ ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ofpbuf_init(&of11_out, 0); ofpacts_put_openflow11_actions(ofpacts.data, ofpacts.size, &of11_out); - print_differences(of11_in.data, of11_in.size, + print_differences("", of11_in.data, of11_in.size, of11_out.data, of11_out.size); putchar('\n'); @@ -2254,7 +2517,7 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ofpacts_put_openflow11_instructions(ofpacts.data, ofpacts.size, &of11_out); - print_differences(of11_in.data, of11_in.size, + print_differences("", of11_in.data, of11_in.size, of11_out.data, of11_out.size); putchar('\n'); @@ -2342,6 +2605,7 @@ static const struct command all_commands[] = { { "parse-nxm", 0, 0, ofctl_parse_nxm }, { "parse-oxm", 0, 0, ofctl_parse_oxm }, { "parse-ofp10-actions", 0, 0, ofctl_parse_ofp10_actions }, + { "parse-ofp10-match", 0, 0, ofctl_parse_ofp10_match }, { "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match }, { "parse-ofp11-actions", 0, 0, ofctl_parse_ofp11_actions }, { "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },