*/
#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
-do_show(int argc OVS_UNUSED, char *argv[])
+ofctl_show(int argc OVS_UNUSED, char *argv[])
{
const char *vconn_name = argv[1];
struct vconn *vconn;
}
static void
-do_dump_desc(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_desc(int argc OVS_UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_DESC);
}
static void
-do_dump_tables(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_tables(int argc OVS_UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
}
}
}
-static void
-do_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
-do_dump_flows(int argc, char *argv[])
+ofctl_dump_flows(int argc, char *argv[])
{
- return do_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
-do_dump_aggregate(int argc, char *argv[])
+ofctl_dump_aggregate(int argc, char *argv[])
{
- return do_dump_flows__(argc, argv, true);
+ return ofctl_dump_flows__(argc, argv, true);
}
static void
-do_queue_stats(int argc, char *argv[])
+ofctl_queue_stats(int argc, char *argv[])
{
struct ofp_queue_stats_request *req;
struct ofpbuf *request;
}
static void
-do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
+ size_t n_fms)
{
enum ofputil_protocol protocol;
struct vconn *vconn;
}
static void
-do_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
{
struct ofputil_flow_mod *fms = NULL;
size_t n_fms = 0;
parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
- do_flow_mod__(argv[1], fms, n_fms);
+ ofctl_flow_mod__(argv[1], fms, n_fms);
free(fms);
}
static void
-do_flow_mod(int argc, char *argv[], uint16_t command)
+ofctl_flow_mod(int argc, char *argv[], uint16_t command)
{
if (argc > 2 && !strcmp(argv[2], "-")) {
- do_flow_mod_file(argc, argv, command);
+ ofctl_flow_mod_file(argc, argv, command);
} else {
struct ofputil_flow_mod fm;
parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
- do_flow_mod__(argv[1], &fm, 1);
+ ofctl_flow_mod__(argv[1], &fm, 1);
}
}
static void
-do_add_flow(int argc, char *argv[])
+ofctl_add_flow(int argc, char *argv[])
{
- do_flow_mod(argc, argv, OFPFC_ADD);
+ ofctl_flow_mod(argc, argv, OFPFC_ADD);
}
static void
-do_add_flows(int argc, char *argv[])
+ofctl_add_flows(int argc, char *argv[])
{
- do_flow_mod_file(argc, argv, OFPFC_ADD);
+ ofctl_flow_mod_file(argc, argv, OFPFC_ADD);
}
static void
-do_mod_flows(int argc, char *argv[])
+ofctl_mod_flows(int argc, char *argv[])
{
- do_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
+ ofctl_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
}
static void
-do_del_flows(int argc, char *argv[])
+ofctl_del_flows(int argc, char *argv[])
{
- do_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
+ ofctl_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
}
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();
}
}
static void
-do_monitor(int argc, char *argv[])
+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 {
}
static void
-do_snoop(int argc OVS_UNUSED, char *argv[])
+ofctl_snoop(int argc OVS_UNUSED, char *argv[])
{
struct vconn *vconn;
}
static void
-do_dump_ports(int argc, char *argv[])
+ofctl_dump_ports(int argc, char *argv[])
{
struct ofp_port_stats_request *req;
struct ofpbuf *request;
}
static void
-do_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_PORT_DESC);
}
static void
-do_probe(int argc OVS_UNUSED, char *argv[])
+ofctl_probe(int argc OVS_UNUSED, char *argv[])
{
struct ofpbuf *request;
struct vconn *vconn;
}
static void
-do_packet_out(int argc, char *argv[])
+ofctl_packet_out(int argc, char *argv[])
{
struct ofputil_packet_out po;
struct ofpbuf ofpacts;
}
static void
-do_mod_port(int argc OVS_UNUSED, char *argv[])
+ofctl_mod_port(int argc OVS_UNUSED, char *argv[])
{
struct ofp_config_flag {
const char *name; /* The flag's name. */
}
static void
-do_get_frags(int argc OVS_UNUSED, char *argv[])
+ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
{
struct ofp_switch_config config;
struct vconn *vconn;
}
static void
-do_set_frags(int argc OVS_UNUSED, char *argv[])
+ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
{
struct ofp_switch_config config;
enum ofp_config_flags mode;
}
static void
-do_ping(int argc, char *argv[])
+ofctl_ping(int argc, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
unsigned int payload;
}
static void
-do_benchmark(int argc OVS_UNUSED, char *argv[])
+ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
struct timeval start, end;
}
static void
-do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
usage();
}
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);
+ reply = NULL;
+ 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
}
static void
-do_replace_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
{
enum { FILE_IDX = 0, SWITCH_IDX = 1 };
enum ofputil_protocol usable_protocols, protocol;
}
static void
-do_diff_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
{
bool differences = false;
struct cls_cursor cursor;
/* Undocumented commands for unit testing. */
static void
-do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
{
enum ofputil_protocol usable_protocols;
enum ofputil_protocol protocol = 0;
/* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
* it back to stdout. */
static void
-do_parse_flow(int argc OVS_UNUSED, char *argv[])
+ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
{
struct ofputil_flow_mod fm;
parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
- do_parse_flows__(&fm, 1);
+ ofctl_parse_flows__(&fm, 1);
}
/* "parse-flows FILENAME": reads the named file as a sequence of flows (like
* add-flows) and prints each of the flows back to stdout. */
static void
-do_parse_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
{
struct ofputil_flow_mod *fms = NULL;
size_t n_fms = 0;
parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
- do_parse_flows__(fms, n_fms);
+ ofctl_parse_flows__(fms, n_fms);
free(fms);
}
static void
-do_parse_nxm__(bool oxm)
+ofctl_parse_nxm__(bool oxm)
{
struct ds in;
* stdin, does some internal fussing with them, and then prints them back as
* strings on stdout. */
static void
-do_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
- return do_parse_nxm__(false);
+ return ofctl_parse_nxm__(false);
}
/* "parse-oxm": reads a series of OXM nx_match specifications as strings from
* stdin, does some internal fussing with them, and then prints them back as
* strings on stdout. */
static void
-do_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
- return do_parse_nxm__(true);
+ return ofctl_parse_nxm__(true);
}
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_;
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]);
}
}
* on stdout, and then converts them back to hex bytes and prints any
* differences from the input. */
static void
-do_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
struct ds in;
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');
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
* from the input. */
static void
-do_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
struct ds in;
/* 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');
* on stdout, and then converts them back to hex bytes and prints any
* differences from the input. */
static void
-do_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
struct ds in;
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');
* them as strings on stdout, and then converts them back to hex bytes and
* prints any differences from the input. */
static void
-do_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
struct ds in;
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');
ds_destroy(&in);
}
+/* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and
+ * mask values to and from various formats and prints the results. */
+static void
+ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
+{
+ struct cls_rule rule;
+
+ char *string_s;
+ struct ofputil_flow_mod fm;
+
+ struct ofpbuf nxm;
+ struct cls_rule nxm_rule;
+ int nxm_match_len;
+ char *nxm_s;
+
+ struct ofp10_match of10_match;
+ struct cls_rule of10_rule;
+
+ struct ofp11_match of11_match;
+ struct cls_rule of11_rule;
+
+ enum ofperr error;
+
+ cls_rule_init_catchall(&rule, OFP_DEFAULT_PRIORITY);
+ rule.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
+ rule.wc.vlan_tci_mask = htons(strtoul(argv[2], NULL, 16));
+
+ /* Convert to and from string. */
+ string_s = cls_rule_to_string(&rule);
+ printf("%s -> ", string_s);
+ fflush(stdout);
+ parse_ofp_str(&fm, -1, string_s, false);
+ printf("%04"PRIx16"/%04"PRIx16"\n",
+ ntohs(fm.cr.flow.vlan_tci),
+ ntohs(fm.cr.wc.vlan_tci_mask));
+
+ /* Convert to and from NXM. */
+ ofpbuf_init(&nxm, 0);
+ nxm_match_len = nx_put_match(&nxm, false, &rule, htonll(0), htonll(0));
+ nxm_s = nx_match_to_string(nxm.data, nxm_match_len);
+ error = nx_pull_match(&nxm, nxm_match_len, 0, &nxm_rule, NULL, NULL);
+ printf("NXM: %s -> ", nxm_s);
+ if (error) {
+ printf("%s\n", ofperr_to_string(error));
+ } else {
+ printf("%04"PRIx16"/%04"PRIx16"\n",
+ ntohs(nxm_rule.flow.vlan_tci),
+ ntohs(nxm_rule.wc.vlan_tci_mask));
+ }
+ free(nxm_s);
+ ofpbuf_uninit(&nxm);
+
+ /* Convert to and from OpenFlow 1.0. */
+ ofputil_cls_rule_to_ofp10_match(&rule, &of10_match);
+ ofputil_cls_rule_from_ofp10_match(&of10_match, 0, &of10_rule);
+ printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+ ntohs(of10_match.dl_vlan),
+ (of10_match.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
+ of10_match.dl_vlan_pcp,
+ (of10_match.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
+ ntohs(of10_rule.flow.vlan_tci),
+ ntohs(of10_rule.wc.vlan_tci_mask));
+
+ /* Convert to and from OpenFlow 1.1. */
+ ofputil_cls_rule_to_ofp11_match(&rule, &of11_match);
+ ofputil_cls_rule_from_ofp11_match(&of11_match, 0, &of11_rule);
+ printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+ ntohs(of11_match.dl_vlan),
+ (of11_match.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
+ of11_match.dl_vlan_pcp,
+ (of11_match.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
+ ntohs(of11_rule.flow.vlan_tci),
+ ntohs(of11_rule.wc.vlan_tci_mask));
+}
+
/* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
* version. */
static void
-do_print_error(int argc OVS_UNUSED, char *argv[])
+ofctl_print_error(int argc OVS_UNUSED, char *argv[])
{
enum ofperr error;
int version;
* binary data, interpreting them as an OpenFlow message, and prints the
* OpenFlow message on stdout, at VERBOSITY (level 2 by default). */
static void
-do_ofp_print(int argc, char *argv[])
+ofctl_ofp_print(int argc, char *argv[])
{
struct ofpbuf packet;
}
static const struct command all_commands[] = {
- { "show", 1, 1, do_show },
- { "monitor", 1, 3, do_monitor },
- { "snoop", 1, 1, do_snoop },
- { "dump-desc", 1, 1, do_dump_desc },
- { "dump-tables", 1, 1, do_dump_tables },
- { "dump-flows", 1, 2, do_dump_flows },
- { "dump-aggregate", 1, 2, do_dump_aggregate },
- { "queue-stats", 1, 3, do_queue_stats },
- { "add-flow", 2, 2, do_add_flow },
- { "add-flows", 2, 2, do_add_flows },
- { "mod-flows", 2, 2, do_mod_flows },
- { "del-flows", 1, 2, do_del_flows },
- { "replace-flows", 2, 2, do_replace_flows },
- { "diff-flows", 2, 2, do_diff_flows },
- { "packet-out", 4, INT_MAX, do_packet_out },
- { "dump-ports", 1, 2, do_dump_ports },
- { "dump-ports-desc", 1, 1, do_dump_ports_desc },
- { "mod-port", 3, 3, do_mod_port },
- { "get-frags", 1, 1, do_get_frags },
- { "set-frags", 2, 2, do_set_frags },
- { "probe", 1, 1, do_probe },
- { "ping", 1, 2, do_ping },
- { "benchmark", 3, 3, do_benchmark },
- { "help", 0, INT_MAX, do_help },
+ { "show", 1, 1, ofctl_show },
+ { "monitor", 1, 3, ofctl_monitor },
+ { "snoop", 1, 1, ofctl_snoop },
+ { "dump-desc", 1, 1, ofctl_dump_desc },
+ { "dump-tables", 1, 1, ofctl_dump_tables },
+ { "dump-flows", 1, 2, ofctl_dump_flows },
+ { "dump-aggregate", 1, 2, ofctl_dump_aggregate },
+ { "queue-stats", 1, 3, ofctl_queue_stats },
+ { "add-flow", 2, 2, ofctl_add_flow },
+ { "add-flows", 2, 2, ofctl_add_flows },
+ { "mod-flows", 2, 2, ofctl_mod_flows },
+ { "del-flows", 1, 2, ofctl_del_flows },
+ { "replace-flows", 2, 2, ofctl_replace_flows },
+ { "diff-flows", 2, 2, ofctl_diff_flows },
+ { "packet-out", 4, INT_MAX, ofctl_packet_out },
+ { "dump-ports", 1, 2, ofctl_dump_ports },
+ { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
+ { "mod-port", 3, 3, ofctl_mod_port },
+ { "get-frags", 1, 1, ofctl_get_frags },
+ { "set-frags", 2, 2, ofctl_set_frags },
+ { "probe", 1, 1, ofctl_probe },
+ { "ping", 1, 2, ofctl_ping },
+ { "benchmark", 3, 3, ofctl_benchmark },
+ { "help", 0, INT_MAX, ofctl_help },
/* Undocumented commands for testing. */
- { "parse-flow", 1, 1, do_parse_flow },
- { "parse-flows", 1, 1, do_parse_flows },
- { "parse-nx-match", 0, 0, do_parse_nxm },
- { "parse-nxm", 0, 0, do_parse_nxm },
- { "parse-oxm", 0, 0, do_parse_oxm },
- { "parse-ofp10-actions", 0, 0, do_parse_ofp10_actions },
- { "parse-ofp11-match", 0, 0, do_parse_ofp11_match },
- { "parse-ofp11-actions", 0, 0, do_parse_ofp11_actions },
- { "parse-ofp11-instructions", 0, 0, do_parse_ofp11_instructions },
- { "print-error", 1, 1, do_print_error },
- { "ofp-print", 1, 2, do_ofp_print },
+ { "parse-flow", 1, 1, ofctl_parse_flow },
+ { "parse-flows", 1, 1, ofctl_parse_flows },
+ { "parse-nx-match", 0, 0, ofctl_parse_nxm },
+ { "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 },
+ { "check-vlan", 2, 2, ofctl_check_vlan },
+ { "print-error", 1, 1, ofctl_print_error },
+ { "ofp-print", 1, 2, ofctl_ofp_print },
{ NULL, 0, 0, NULL },
};