X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=utilities%2Fovs-ofctl.c;h=f925d846fab3bc66374c1a518de4bb99a14252c2;hb=4f8f2439c3cd93b856442816f97e77e95e605042;hp=77a1434ecb1a8aa5d5c224540dcc924528d7fba1;hpb=e1fef0f921cbee185ce728f43f6b6ddbc4942e24;p=openvswitch diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 77a1434e..f925d846 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); @@ -377,14 +434,12 @@ dump_trivial_transaction(const char *vconn_name, uint8_t request_type) } static void -dump_stats_transaction(const char *vconn_name, struct ofpbuf *request) +dump_stats_transaction__(struct vconn *vconn, struct ofpbuf *request) { ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid; ovs_be16 stats_type = ((struct ofp_stats_msg *) request->data)->type; - struct vconn *vconn; bool done = false; - open_vconn(vconn_name, &vconn); send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; @@ -414,6 +469,15 @@ dump_stats_transaction(const char *vconn_name, struct ofpbuf *request) } ofpbuf_delete(reply); } +} + +static void +dump_stats_transaction(const char *vconn_name, struct ofpbuf *request) +{ + struct vconn *vconn; + + open_vconn(vconn_name, &vconn); + dump_stats_transaction__(vconn, request); vconn_close(vconn); } @@ -763,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] : ""); @@ -776,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); - dump_stats_transaction(argv[1], request); + *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 @@ -1086,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); @@ -1107,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 (;;) { @@ -1114,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); @@ -1148,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(); } @@ -1160,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 { @@ -1643,6 +1862,60 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) return usable_protocols; } +static bool +recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid, + struct ofpbuf **replyp, + struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts) +{ + struct ofpbuf *reply = *replyp; + + for (;;) { + ovs_be16 flags; + int retval; + + /* Get a flow stats reply message, if we don't already have one. */ + if (!reply) { + const struct ofputil_msg_type *type; + enum ofputil_msg_code code; + + do { + run(vconn_recv_block(vconn, &reply), + "OpenFlow packet receive failed"); + } while (((struct ofp_header *) reply->data)->xid != send_xid); + + ofputil_decode_msg_type(reply->data, &type); + code = ofputil_msg_type_code(type); + if (code != OFPUTIL_OFPST_FLOW_REPLY && + code != OFPUTIL_NXST_FLOW_REPLY) { + ovs_fatal(0, "received bad reply: %s", + ofp_to_string(reply->data, reply->size, + verbosity + 1)); + } + } + + /* Pull an individual flow stats reply out of the message. */ + retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts); + switch (retval) { + case 0: + *replyp = reply; + return true; + + case EOF: + flags = ((const struct ofp_stats_msg *) reply->l2)->flags; + ofpbuf_delete(reply); + if (!(flags & htons(OFPSF_REPLY_MORE))) { + *replyp = NULL; + return false; + } + break; + + default: + ovs_fatal(0, "parse error in reply (%s)", + ofperr_to_string(retval)); + } + } +} + /* Reads the OpenFlow flow table from 'vconn', which has currently active flow * format 'protocol', and adds them as flow table entries in 'cls' for the * version with the specified 'index'. */ @@ -1652,9 +1925,11 @@ read_flows_from_switch(struct vconn *vconn, struct classifier *cls, int index) { struct ofputil_flow_stats_request fsr; + struct ofputil_flow_stats fs; struct ofpbuf *request; + struct ofpbuf ofpacts; + struct ofpbuf *reply; ovs_be32 send_xid; - bool done; fsr.aggregate = false; cls_rule_init_catchall(&fsr.match, 0); @@ -1665,65 +1940,22 @@ read_flows_from_switch(struct vconn *vconn, send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); - done = false; - while (!done) { - ovs_be32 recv_xid; - struct ofpbuf *reply; - - run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); - recv_xid = ((struct ofp_header *) reply->data)->xid; - if (send_xid == recv_xid) { - const struct ofputil_msg_type *type; - const struct ofp_stats_msg *osm; - enum ofputil_msg_code code; - - ofputil_decode_msg_type(reply->data, &type); - code = ofputil_msg_type_code(type); - if (code != OFPUTIL_OFPST_FLOW_REPLY && - code != OFPUTIL_NXST_FLOW_REPLY) { - ovs_fatal(0, "received bad reply: %s", - ofp_to_string(reply->data, reply->size, - verbosity + 1)); - } - - osm = reply->data; - if (!(osm->flags & htons(OFPSF_REPLY_MORE))) { - done = true; - } - - for (;;) { - struct fte_version *version; - struct ofputil_flow_stats fs; - struct ofpbuf ofpacts; - int retval; - - ofpbuf_init(&ofpacts, 64); - retval = ofputil_decode_flow_stats_reply(&fs, reply, false, - &ofpacts); - if (retval) { - ofpbuf_uninit(&ofpacts); - if (retval != EOF) { - ovs_fatal(0, "parse error in reply"); - } - break; - } + reply = NULL; + ofpbuf_init(&ofpacts, 0); + while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) { + struct fte_version *version; - version = xmalloc(sizeof *version); - version->cookie = fs.cookie; - version->idle_timeout = fs.idle_timeout; - version->hard_timeout = fs.hard_timeout; - version->flags = 0; - version->ofpacts = ofpbuf_steal_data(&ofpacts); - version->ofpacts_len = ofpacts.size; + version = xmalloc(sizeof *version); + version->cookie = fs.cookie; + version->idle_timeout = fs.idle_timeout; + version->hard_timeout = fs.hard_timeout; + version->flags = 0; + version->ofpacts_len = fs.ofpacts_len; + version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len); - fte_insert(cls, &fs.rule, version, index); - } - } else { - VLOG_DBG("received reply with xid %08"PRIx32" " - "!= expected %08"PRIx32, recv_xid, send_xid); - } - ofpbuf_delete(reply); + fte_insert(cls, &fs.rule, version, index); } + ofpbuf_uninit(&ofpacts); } static void