*/
#include <config.h>
+#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include "util.h"
#include "vconn.h"
#include "vlog.h"
+#include "meta-flow.h"
+#include "sort.h"
VLOG_DEFINE_THIS_MODULE(ofctl);
* "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[])
{
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[])
{
OPT_STRICT = UCHAR_MAX + 1,
OPT_READD,
OPT_TIMESTAMP,
+ OPT_SORT,
+ OPT_RSORT,
DAEMON_OPTION_ENUMS,
VLOG_OPTION_ENUMS
};
{"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,
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
abort();
}
}
+
+ if (n_criteria) {
+ /* Always do a final sort pass based on priority. */
+ add_sort_criterion(SORT_DESC, "priority");
+ }
+
free(short_options);
}
" 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"
" -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);
}
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;
}
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);
}
}
}
-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] : "");
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
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);
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 (;;) {
int retval;
unixctl_server_run(server);
-
- for (;;) {
+ while (!blocked) {
uint8_t msg_type;
retval = vconn_recv(vconn, &b);
vconn_run(vconn);
vconn_run_wait(vconn);
- vconn_recv_wait(vconn);
+ if (!blocked) {
+ vconn_recv_wait(vconn);
+ }
unixctl_server_wait(server);
poll_block();
}
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 {
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'. */
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);
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