+ 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
+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_ALL;
+ }
+ 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 enum ofputil_protocol
+open_vconn_for_flow_mod(const char *remote,
+ const struct ofputil_flow_mod *fms, size_t n_fms,
+ struct vconn **vconnp)
+{
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol cur_protocol;
+ char *usable_s;
+ int i;
+
+ /* Figure out what flow formats will work. */
+ usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
+ 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 protocol;
+ struct vconn *vconn;
+ size_t i;
+
+ protocol = open_vconn_for_flow_mod(remote, fms, n_fms, &vconn);
+
+ 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)
+{
+ struct ofputil_flow_mod *fms = NULL;
+ size_t n_fms = 0;
+
+ parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+ ofctl_flow_mod__(argv[1], fms, n_fms);
+ 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;
+ parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
+ ofctl_flow_mod__(argv[1], &fm, 1);
+ }
+}
+
+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 (msg->size < sizeof(struct ofp_header)) {
+ ofpbuf_delete(msg);
+ return "Message too short for OpenFlow";
+ }
+
+ oh = msg->data;
+ if (msg->size != 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, msg->data, msg->size, verbosity);
+
+ error = vconn_send_block(vconn, msg);
+ if (error) {
+ ofpbuf_delete(msg);
+ ds_put_format(&reply, "%s\n", 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, 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, 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");
+ }
+}
+
+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);
+ 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) {
+ time_t now = time_wall();
+ char s[32];
+
+ strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", gmtime(&now));
+ fputs(s, stderr);
+ }
+
+ ofptype_decode(&type, b->data);
+ ofp_print(stderr, b->data, b->size, verbosity + 2);
+ ofpbuf_delete(b);
+
+ if (barrier_aux.conn && type == OFPTYPE_BARRIER_REPLY) {
+ unixctl_command_reply(barrier_aux.conn, NULL);
+ barrier_aux.conn = NULL;
+ }
+ }
+
+ 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;
+
+ 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;
+
+ 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 {
+ 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(reply->data, reply->size, 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);
+ }
+ }
+
+ monitor_vconn(vconn);
+}
+
+static void
+ofctl_snoop(int argc OVS_UNUSED, char *argv[])
+{
+ struct vconn *vconn;
+
+ open_vconn__(argv[1], "snoop", &vconn);
+ monitor_vconn(vconn);
+}
+
+static void
+ofctl_dump_ports(int argc, char *argv[])
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ uint16_t port;
+
+ open_vconn(argv[1], &vconn);
+ port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE;
+ 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 (reply->size != 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;
+ int i;
+
+ ofpbuf_init(&ofpacts, 64);
+ parse_ofpacts(argv[3], &ofpacts);
+
+ po.buffer_id = UINT32_MAX;
+ po.in_port = str_to_port_no(argv[1], argv[2]);
+ po.ofpacts = ofpacts.data;
+ po.ofpacts_len = ofpacts.size;
+
+ 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 = packet->data;
+ po.packet_len = packet->size;
+ 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_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_ping(int argc, char *argv[])
+{
+ size_t max_payload = 65535 - sizeof(struct ofp_header);
+ unsigned int payload;
+ struct vconn *vconn;
+ int i;
+