+ const struct ofputil_flow_stats *afs = afs_;
+ const struct ofputil_flow_stats *bfs = bfs_;
+ const struct match *a = &afs->match;
+ const struct match *b = &bfs->match;
+ const struct sort_criterion *sc;
+
+ for (sc = criteria; sc < &criteria[n_criteria]; sc++) {
+ const struct mf_field *f = sc->field;
+ int ret;
+
+ if (!f) {
+ unsigned int a_pri = afs->priority;
+ unsigned int b_pri = bfs->priority;
+ ret = a_pri < b_pri ? -1 : a_pri > b_pri;
+ } 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[])
+{
+ 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 *) ofpbuf_data(request))->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
+ofctl_dump_aggregate(int argc, char *argv[])
+{
+ return ofctl_dump_flows__(argc, argv, true);
+}
+
+static void
+ofctl_queue_stats(int argc, char *argv[])
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ struct ofputil_queue_stats_request oqs;
+
+ open_vconn(argv[1], &vconn);
+
+ if (argc > 2 && argv[2][0] && strcasecmp(argv[2], "all")) {
+ oqs.port_no = str_to_port_no(argv[1], argv[2]);
+ } else {
+ oqs.port_no = OFPP_ANY;
+ }
+ if (argc > 3 && argv[3][0] && strcasecmp(argv[3], "all")) {
+ oqs.queue_id = atoi(argv[3]);
+ } else {
+ oqs.queue_id = OFPQ_ALL;
+ }
+
+ request = ofputil_encode_queue_stats_request(vconn_get_version(vconn), &oqs);
+ dump_stats_transaction(vconn, request);
+ vconn_close(vconn);
+}
+
+static void
+ofctl_queue_get_config(int argc OVS_UNUSED, char *argv[])
+{
+ const char *vconn_name = argv[1];
+ const char *port_name = argv[2];
+ enum ofputil_protocol protocol;
+ enum ofp_version version;
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ ofp_port_t port;
+
+ port = str_to_port_no(vconn_name, port_name);
+
+ protocol = open_vconn(vconn_name, &vconn);
+ version = ofputil_protocol_to_ofp_version(protocol);
+ request = ofputil_encode_queue_get_config_request(version, port);
+ dump_transaction(vconn, request);
+ vconn_close(vconn);
+}
+
+static enum ofputil_protocol
+open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
+ enum ofputil_protocol usable_protocols)
+{
+ enum ofputil_protocol cur_protocol;
+ char *usable_s;
+ int i;
+
+ if (!(usable_protocols & allowed_protocols)) {
+ char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ ovs_fatal(0, "none of the usable flow formats (%s) is among the "
+ "allowed flow formats (%s)", usable_s, allowed_s);
+ }
+
+ /* If the initial flow format is allowed and usable, keep it. */
+ cur_protocol = open_vconn(remote, vconnp);
+ if (usable_protocols & allowed_protocols & cur_protocol) {
+ return cur_protocol;
+ }
+
+ /* Otherwise try each flow format in turn. */
+ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+ enum ofputil_protocol f = 1 << i;
+
+ if (f != cur_protocol
+ && f & usable_protocols & allowed_protocols
+ && try_set_protocol(*vconnp, f, &cur_protocol)) {
+ return f;
+ }
+ }
+
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ ovs_fatal(0, "switch does not support any of the usable flow "
+ "formats (%s)", usable_s);
+}
+
+static void
+ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
+ size_t n_fms, enum ofputil_protocol usable_protocols)
+{
+ enum ofputil_protocol protocol;
+ struct vconn *vconn;
+ size_t i;
+
+ protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
+
+ for (i = 0; i < n_fms; i++) {
+ struct ofputil_flow_mod *fm = &fms[i];
+
+ transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
+ free(fm->ofpacts);
+ }
+ vconn_close(vconn);
+}
+
+static void
+ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+{
+ enum ofputil_protocol usable_protocols;
+ struct ofputil_flow_mod *fms = NULL;
+ size_t n_fms = 0;
+ char *error;
+
+ error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
+ free(fms);
+}
+
+static void
+ofctl_flow_mod(int argc, char *argv[], uint16_t command)
+{
+ if (argc > 2 && !strcmp(argv[2], "-")) {
+ ofctl_flow_mod_file(argc, argv, command);
+ } else {
+ struct ofputil_flow_mod fm;
+ char *error;
+ enum ofputil_protocol usable_protocols;
+
+ error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
+ }
+}
+
+static void
+ofctl_add_flow(int argc, char *argv[])
+{
+ ofctl_flow_mod(argc, argv, OFPFC_ADD);
+}
+
+static void
+ofctl_add_flows(int argc, char *argv[])
+{
+ ofctl_flow_mod_file(argc, argv, OFPFC_ADD);
+}
+
+static void
+ofctl_mod_flows(int argc, char *argv[])
+{
+ ofctl_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
+}
+
+static void
+ofctl_del_flows(int argc, char *argv[])
+{
+ ofctl_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
+}
+
+static void
+set_packet_in_format(struct vconn *vconn,
+ enum nx_packet_in_format packet_in_format)
+{
+ struct ofpbuf *spif;
+
+ spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn),
+ packet_in_format);
+ transact_noreply(vconn, spif);
+ VLOG_DBG("%s: using user-specified packet in format %s",
+ vconn_get_name(vconn),
+ ofputil_packet_in_format_to_string(packet_in_format));
+}
+
+static int
+monitor_set_invalid_ttl_to_controller(struct vconn *vconn)
+{
+ struct ofp_switch_config config;
+ enum ofp_config_flags flags;
+
+ fetch_switch_config(vconn, &config);
+ flags = ntohs(config.flags);
+ if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+ /* Set the invalid ttl config. */
+ flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
+
+ config.flags = htons(flags);
+ set_switch_config(vconn, &config);
+
+ /* Then retrieve the configuration to see if it really took. OpenFlow
+ * doesn't define error reporting for bad modes, so this is all we can
+ * do. */
+ fetch_switch_config(vconn, &config);
+ flags = ntohs(config.flags);
+ if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+ ovs_fatal(0, "setting invalid_ttl_to_controller failed (this "
+ "switch probably doesn't support mode)");
+ return -EOPNOTSUPP;
+ }
+ }
+ return 0;
+}
+
+/* Converts hex digits in 'hex' to an OpenFlow message in '*msgp'. The
+ * caller must free '*msgp'. On success, returns NULL. On failure, returns
+ * an error message and stores NULL in '*msgp'. */
+static const char *
+openflow_from_hex(const char *hex, struct ofpbuf **msgp)
+{
+ struct ofp_header *oh;
+ struct ofpbuf *msg;
+
+ msg = ofpbuf_new(strlen(hex) / 2);
+ *msgp = NULL;
+
+ if (ofpbuf_put_hex(msg, hex, NULL)[0] != '\0') {
+ ofpbuf_delete(msg);
+ return "Trailing garbage in hex data";
+ }
+
+ if (ofpbuf_size(msg) < sizeof(struct ofp_header)) {
+ ofpbuf_delete(msg);
+ return "Message too short for OpenFlow";
+ }
+
+ oh = ofpbuf_data(msg);
+ if (ofpbuf_size(msg) != ntohs(oh->length)) {
+ ofpbuf_delete(msg);
+ return "Message size does not match length in OpenFlow header";
+ }
+
+ *msgp = msg;
+ return NULL;
+}
+
+static void
+ofctl_send(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *vconn_)
+{
+ struct vconn *vconn = vconn_;
+ struct ds reply;
+ bool ok;
+ int i;
+
+ ok = true;
+ ds_init(&reply);
+ for (i = 1; i < argc; i++) {
+ const char *error_msg;
+ struct ofpbuf *msg;
+ int error;
+
+ error_msg = openflow_from_hex(argv[i], &msg);
+ if (error_msg) {
+ ds_put_format(&reply, "%s\n", error_msg);
+ ok = false;
+ continue;
+ }
+
+ fprintf(stderr, "send: ");
+ ofp_print(stderr, ofpbuf_data(msg), ofpbuf_size(msg), verbosity);
+
+ error = vconn_send_block(vconn, msg);
+ if (error) {
+ ofpbuf_delete(msg);
+ ds_put_format(&reply, "%s\n", ovs_strerror(error));
+ ok = false;
+ } else {
+ ds_put_cstr(&reply, "sent\n");
+ }
+ }
+
+ if (ok) {
+ unixctl_command_reply(conn, ds_cstr(&reply));
+ } else {
+ unixctl_command_reply_error(conn, ds_cstr(&reply));
+ }
+ ds_destroy(&reply);
+}
+
+struct barrier_aux {
+ struct vconn *vconn; /* OpenFlow connection for sending barrier. */
+ struct unixctl_conn *conn; /* Connection waiting for barrier response. */
+};
+
+static void
+ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *aux_)
+{
+ struct barrier_aux *aux = aux_;
+ struct ofpbuf *msg;
+ int error;
+
+ if (aux->conn) {
+ unixctl_command_reply_error(conn, "already waiting for barrier reply");
+ return;
+ }
+
+ msg = ofputil_encode_barrier_request(vconn_get_version(aux->vconn));
+ error = vconn_send_block(aux->vconn, msg);
+ if (error) {
+ ofpbuf_delete(msg);
+ unixctl_command_reply_error(conn, ovs_strerror(error));
+ } else {
+ aux->conn = conn;
+ }
+}
+
+static void
+ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ int fd;
+
+ fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (fd < 0) {
+ unixctl_command_reply_error(conn, ovs_strerror(errno));
+ return;
+ }
+
+ fflush(stderr);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ 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");
+ }
+}
+
+/* Prints to stdout all of the messages received on 'vconn'.
+ *
+ * Iff 'reply_to_echo_requests' is true, sends a reply to any echo request
+ * received on 'vconn'. */
+static void
+monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests)
+{
+ struct barrier_aux barrier_aux = { vconn, NULL };
+ struct unixctl_server *server;
+ bool exiting = false;
+ bool blocked = false;
+ int error;
+
+ daemon_save_fd(STDERR_FILENO);
+ daemonize_start();
+ error = unixctl_server_create(NULL, &server);
+ if (error) {
+ ovs_fatal(error, "failed to create unixctl server");
+ }
+ unixctl_command_register("exit", "", 0, 0, ofctl_exit, &exiting);
+ unixctl_command_register("ofctl/send", "OFMSG...", 1, INT_MAX,
+ ofctl_send, vconn);
+ unixctl_command_register("ofctl/barrier", "", 0, 0,
+ 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 (;;) {
+ struct ofpbuf *b;
+ int retval;
+
+ unixctl_server_run(server);
+
+ while (!blocked) {
+ enum ofptype type;
+
+ retval = vconn_recv(vconn, &b);
+ if (retval == EAGAIN) {
+ break;
+ }
+ run(retval, "vconn_recv");
+
+ if (timestamp) {
+ char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ",
+ time_wall_msec(), true);
+ fputs(s, stderr);
+ free(s);
+ }
+
+ ofptype_decode(&type, ofpbuf_data(b));
+ ofp_print(stderr, ofpbuf_data(b), ofpbuf_size(b), verbosity + 2);
+
+ switch ((int) type) {
+ case OFPTYPE_BARRIER_REPLY:
+ if (barrier_aux.conn) {
+ unixctl_command_reply(barrier_aux.conn, NULL);
+ barrier_aux.conn = NULL;
+ }
+ break;
+
+ case OFPTYPE_ECHO_REQUEST:
+ if (reply_to_echo_requests) {
+ struct ofpbuf *reply;
+
+ reply = make_echo_reply(ofpbuf_data(b));
+ retval = vconn_send_block(vconn, reply);
+ if (retval) {
+ ovs_fatal(retval, "failed to send echo reply");
+ }
+ }
+ break;
+ }
+ ofpbuf_delete(b);
+ }
+
+ if (exiting) {
+ break;
+ }
+
+ vconn_run(vconn);
+ vconn_run_wait(vconn);
+ if (!blocked) {
+ vconn_recv_wait(vconn);
+ }
+ unixctl_server_wait(server);
+ poll_block();
+ }
+ vconn_close(vconn);
+ unixctl_server_destroy(server);
+}
+
+static void
+ofctl_monitor(int argc, char *argv[])
+{
+ struct vconn *vconn;
+ int i;
+ enum ofputil_protocol usable_protocols;
+
+ open_vconn(argv[1], &vconn);
+ for (i = 2; i < argc; i++) {
+ const char *arg = argv[i];
+
+ 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;
+ char *error;
+
+ error = parse_flow_monitor_request(&fmr, arg + 6,
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ 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 {
+ enum ofp_version version = vconn_get_version(vconn);
+
+ switch (version) {
+ case OFP10_VERSION: {
+ struct ofpbuf *spif, *reply;
+
+ spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn),
+ NXPIF_NXM);
+ run(vconn_transact_noreply(vconn, spif, &reply),
+ "talking to %s", vconn_get_name(vconn));
+ if (reply) {
+ char *s = ofp_to_string(ofpbuf_data(reply), ofpbuf_size(reply), 2);
+ VLOG_DBG("%s: failed to set packet in format to nxm, controller"
+ " replied: %s. Falling back to the switch default.",
+ vconn_get_name(vconn), s);
+ free(s);
+ ofpbuf_delete(reply);
+ }
+ break;
+ }
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+ }
+
+ monitor_vconn(vconn, true);
+}
+
+static void
+ofctl_snoop(int argc OVS_UNUSED, char *argv[])
+{
+ struct vconn *vconn;
+
+ open_vconn__(argv[1], SNOOP, &vconn);
+ monitor_vconn(vconn, false);
+}
+
+static void
+ofctl_dump_ports(int argc, char *argv[])
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ ofp_port_t port;
+
+ open_vconn(argv[1], &vconn);
+ port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
+ request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port);
+ dump_stats_transaction(vconn, request);
+ vconn_close(vconn);
+}
+
+static void
+ofctl_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
+{
+ dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_PORT_DESC_REQUEST);
+}
+
+static void
+ofctl_probe(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ struct ofpbuf *reply;
+
+ open_vconn(argv[1], &vconn);
+ request = make_echo_request(vconn_get_version(vconn));
+ run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
+ if (ofpbuf_size(reply) != sizeof(struct ofp_header)) {
+ ovs_fatal(0, "reply does not match request");
+ }
+ ofpbuf_delete(reply);
+ vconn_close(vconn);
+}
+
+static void
+ofctl_packet_out(int argc, char *argv[])
+{
+ enum ofputil_protocol protocol;
+ struct ofputil_packet_out po;
+ struct ofpbuf ofpacts;
+ struct vconn *vconn;
+ char *error;
+ int i;
+ enum ofputil_protocol usable_protocols; /* XXX: Use in proto selection */
+
+ ofpbuf_init(&ofpacts, 64);
+ error = parse_ofpacts(argv[3], &ofpacts, &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ po.buffer_id = UINT32_MAX;
+ po.in_port = str_to_port_no(argv[1], argv[2]);
+ po.ofpacts = ofpbuf_data(&ofpacts);
+ po.ofpacts_len = ofpbuf_size(&ofpacts);
+
+ protocol = open_vconn(argv[1], &vconn);
+ for (i = 4; i < argc; i++) {
+ struct ofpbuf *packet, *opo;
+ const char *error_msg;
+
+ error_msg = eth_from_hex(argv[i], &packet);
+ if (error_msg) {
+ ovs_fatal(0, "%s", error_msg);
+ }
+
+ po.packet = ofpbuf_data(packet);
+ po.packet_len = ofpbuf_size(packet);
+ opo = ofputil_encode_packet_out(&po, protocol);
+ transact_noreply(vconn, opo);
+ ofpbuf_delete(packet);
+ }
+ vconn_close(vconn);
+ ofpbuf_uninit(&ofpacts);
+}
+
+static void
+ofctl_mod_port(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofp_config_flag {
+ const char *name; /* The flag's name. */
+ enum ofputil_port_config bit; /* Bit to turn on or off. */
+ bool on; /* Value to set the bit to. */
+ };
+ static const struct ofp_config_flag flags[] = {
+ { "up", OFPUTIL_PC_PORT_DOWN, false },
+ { "down", OFPUTIL_PC_PORT_DOWN, true },
+ { "stp", OFPUTIL_PC_NO_STP, false },
+ { "receive", OFPUTIL_PC_NO_RECV, false },
+ { "receive-stp", OFPUTIL_PC_NO_RECV_STP, false },
+ { "flood", OFPUTIL_PC_NO_FLOOD, false },
+ { "forward", OFPUTIL_PC_NO_FWD, false },
+ { "packet-in", OFPUTIL_PC_NO_PACKET_IN, false },
+ };
+
+ const struct ofp_config_flag *flag;
+ enum ofputil_protocol protocol;
+ struct ofputil_port_mod pm;
+ struct ofputil_phy_port pp;
+ struct vconn *vconn;
+ const char *command;
+ bool not;
+
+ fetch_ofputil_phy_port(argv[1], argv[2], &pp);
+
+ pm.port_no = pp.port_no;
+ memcpy(pm.hw_addr, pp.hw_addr, ETH_ADDR_LEN);
+ pm.config = 0;
+ pm.mask = 0;
+ pm.advertise = 0;
+
+ if (!strncasecmp(argv[3], "no-", 3)) {
+ command = argv[3] + 3;
+ not = true;
+ } else if (!strncasecmp(argv[3], "no", 2)) {
+ command = argv[3] + 2;
+ not = true;
+ } else {
+ command = argv[3];
+ not = false;
+ }
+ for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) {
+ if (!strcasecmp(command, flag->name)) {
+ pm.mask = flag->bit;
+ pm.config = flag->on ^ not ? flag->bit : 0;
+ goto found;
+ }
+ }
+ ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
+
+found:
+ protocol = open_vconn(argv[1], &vconn);
+ transact_noreply(vconn, ofputil_encode_port_mod(&pm, protocol));
+ vconn_close(vconn);
+}
+
+static void
+ofctl_mod_table(int argc OVS_UNUSED, char *argv[])
+{
+ enum ofputil_protocol protocol, usable_protocols;
+ struct ofputil_table_mod tm;
+ struct vconn *vconn;
+ char *error;
+ int i;
+
+ error = parse_ofp_table_mod(&tm, argv[2], argv[3], &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ protocol = open_vconn(argv[1], &vconn);
+ if (!(protocol & usable_protocols)) {
+ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+ enum ofputil_protocol f = 1 << i;
+ if (f != protocol
+ && f & usable_protocols
+ && try_set_protocol(vconn, f, &protocol)) {
+ protocol = f;
+ break;
+ }
+ }
+ }
+
+ if (!(protocol & usable_protocols)) {
+ char *usable_s = ofputil_protocols_to_string(usable_protocols);
+ ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
+ }
+
+ transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
+ vconn_close(vconn);
+}
+
+static void
+ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofp_switch_config config;
+ struct vconn *vconn;
+
+ open_vconn(argv[1], &vconn);
+ fetch_switch_config(vconn, &config);
+ puts(ofputil_frag_handling_to_string(ntohs(config.flags)));
+ vconn_close(vconn);
+}
+
+static void
+ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofp_switch_config config;
+ enum ofp_config_flags mode;
+ struct vconn *vconn;
+ ovs_be16 flags;
+
+ if (!ofputil_frag_handling_from_string(argv[2], &mode)) {
+ ovs_fatal(0, "%s: unknown fragment handling mode", argv[2]);
+ }
+
+ open_vconn(argv[1], &vconn);
+ fetch_switch_config(vconn, &config);
+ flags = htons(mode) | (config.flags & htons(~OFPC_FRAG_MASK));
+ if (flags != config.flags) {
+ /* Set the configuration. */
+ config.flags = flags;
+ set_switch_config(vconn, &config);
+
+ /* Then retrieve the configuration to see if it really took. OpenFlow
+ * doesn't define error reporting for bad modes, so this is all we can
+ * do. */
+ fetch_switch_config(vconn, &config);
+ if (flags != config.flags) {
+ ovs_fatal(0, "%s: setting fragment handling mode failed (this "
+ "switch probably doesn't support mode \"%s\")",
+ argv[1], ofputil_frag_handling_to_string(mode));
+ }
+ }
+ vconn_close(vconn);
+}
+
+static void
+ofctl_ofp_parse(int argc OVS_UNUSED, char *argv[])
+{
+ const char *filename = argv[1];
+ struct ofpbuf b;
+ FILE *file;
+
+ file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
+ if (file == NULL) {
+ ovs_fatal(errno, "%s: open", filename);
+ }
+
+ ofpbuf_init(&b, 65536);
+ for (;;) {
+ struct ofp_header *oh;
+ size_t length, tail_len;
+ void *tail;
+ size_t n;
+
+ ofpbuf_clear(&b);
+ oh = ofpbuf_put_uninit(&b, sizeof *oh);
+ n = fread(oh, 1, sizeof *oh, file);
+ if (n == 0) {
+ break;
+ } else if (n < sizeof *oh) {
+ ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
+ }
+
+ length = ntohs(oh->length);
+ if (length < sizeof *oh) {
+ ovs_fatal(0, "%s: %"PRIuSIZE"-byte message is too short for OpenFlow",
+ filename, length);
+ }
+
+ tail_len = length - sizeof *oh;
+ tail = ofpbuf_put_uninit(&b, tail_len);
+ n = fread(tail, 1, tail_len, file);
+ if (n < tail_len) {
+ ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
+ }
+
+ ofp_print(stdout, ofpbuf_data(&b), ofpbuf_size(&b), verbosity + 2);
+ }
+ ofpbuf_uninit(&b);
+
+ if (file != stdin) {
+ fclose(file);
+ }
+}
+
+static bool
+is_openflow_port(ovs_be16 port_, char *ports[])
+{
+ uint16_t port = ntohs(port_);
+ if (ports[0]) {
+ int i;
+
+ for (i = 0; ports[i]; i++) {
+ if (port == atoi(ports[i])) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return port == OFP_PORT || port == OFP_OLD_PORT;
+ }
+}
+
+static void
+ofctl_ofp_parse_pcap(int argc OVS_UNUSED, char *argv[])
+{
+ struct tcp_reader *reader;
+ FILE *file;
+ int error;
+ bool first;
+
+ file = ovs_pcap_open(argv[1], "rb");
+ if (!file) {
+ ovs_fatal(errno, "%s: open failed", argv[1]);
+ }
+
+ reader = tcp_reader_open();
+ first = true;
+ for (;;) {
+ struct ofpbuf *packet;
+ long long int when;
+ struct flow flow;
+ const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+
+ error = ovs_pcap_read(file, &packet, &when);
+ if (error) {
+ break;
+ }
+ flow_extract(packet, &md, &flow);
+ if (flow.dl_type == htons(ETH_TYPE_IP)
+ && flow.nw_proto == IPPROTO_TCP
+ && (is_openflow_port(flow.tp_src, argv + 2) ||
+ is_openflow_port(flow.tp_dst, argv + 2))) {
+ struct ofpbuf *payload = tcp_reader_run(reader, &flow, packet);
+ if (payload) {
+ while (ofpbuf_size(payload) >= sizeof(struct ofp_header)) {
+ const struct ofp_header *oh;
+ void *data = ofpbuf_data(payload);
+ int length;
+
+ /* Align OpenFlow on 8-byte boundary for safe access. */
+ ofpbuf_shift(payload, -((intptr_t) data & 7));
+
+ oh = ofpbuf_data(payload);
+ length = ntohs(oh->length);
+ if (ofpbuf_size(payload) < length) {
+ break;
+ }
+
+ if (!first) {
+ putchar('\n');
+ }
+ first = false;
+
+ if (timestamp) {
+ char *s = xastrftime_msec("%H:%M:%S.### ", when, true);
+ fputs(s, stdout);
+ free(s);
+ }
+
+ printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n",
+ IP_ARGS(flow.nw_src), ntohs(flow.tp_src),
+ IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst));
+ ofp_print(stdout, ofpbuf_data(payload), length, verbosity + 1);
+ ofpbuf_pull(payload, length);
+ }
+ }
+ }
+ ofpbuf_delete(packet);
+ }
+ tcp_reader_close(reader);
+}
+
+static void
+ofctl_ping(int argc, char *argv[])
+{
+ size_t max_payload = 65535 - sizeof(struct ofp_header);
+ unsigned int payload;
+ struct vconn *vconn;
+ int i;