+ 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(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 ofp10_port_stats_request *req;
+ struct ofpbuf *request;
+ uint16_t port;
+
+ request = ofpraw_alloc(OFPRAW_OFPST_PORT_REQUEST, OFP10_VERSION, 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
+ port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE;
+ req->port_no = htons(port);
+ dump_stats_transaction(argv[1], request);
+}
+
+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 = (!strcasecmp(argv[2], "none") ? OFPP_NONE
+ : !strcasecmp(argv[2], "local") ? OFPP_LOCAL
+ : 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;
+
+ payload = argc > 2 ? atoi(argv[2]) : 64;
+ if (payload > max_payload) {
+ ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
+ }
+
+ open_vconn(argv[1], &vconn);
+ for (i = 0; i < 10; i++) {
+ struct timeval start, end;
+ struct ofpbuf *request, *reply;
+ const struct ofp_header *rpy_hdr;
+ enum ofptype type;
+
+ request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+ payload);
+ random_bytes(ofpbuf_put_uninit(request, payload), payload);
+
+ xgettimeofday(&start);
+ run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
+ xgettimeofday(&end);
+
+ rpy_hdr = reply->data;
+ if (ofptype_pull(&type, reply)
+ || type != OFPTYPE_ECHO_REPLY
+ || reply->size != payload
+ || memcmp(request->l3, reply->l3, payload)) {
+ printf("Reply does not match request. Request:\n");
+ ofp_print(stdout, request, request->size, verbosity + 2);
+ printf("Reply:\n");
+ ofp_print(stdout, reply, reply->size, verbosity + 2);
+ }
+ printf("%zu bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
+ reply->size, argv[1], ntohl(rpy_hdr->xid),
+ (1000*(double)(end.tv_sec - start.tv_sec))
+ + (.001*(end.tv_usec - start.tv_usec)));
+ ofpbuf_delete(request);
+ ofpbuf_delete(reply);
+ }
+ vconn_close(vconn);
+}
+
+static void
+ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
+{
+ size_t max_payload = 65535 - sizeof(struct ofp_header);
+ struct timeval start, end;
+ unsigned int payload_size, message_size;
+ struct vconn *vconn;
+ double duration;
+ int count;
+ int i;
+
+ payload_size = atoi(argv[2]);
+ if (payload_size > max_payload) {
+ ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
+ }
+ message_size = sizeof(struct ofp_header) + payload_size;
+
+ count = atoi(argv[3]);
+
+ printf("Sending %d packets * %u bytes (with header) = %u bytes total\n",
+ count, message_size, count * message_size);
+
+ open_vconn(argv[1], &vconn);
+ xgettimeofday(&start);
+ for (i = 0; i < count; i++) {
+ struct ofpbuf *request, *reply;
+
+ request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+ payload_size);
+ ofpbuf_put_zeros(request, payload_size);
+ run(vconn_transact(vconn, request, &reply), "transact");
+ ofpbuf_delete(reply);
+ }
+ xgettimeofday(&end);
+ vconn_close(vconn);
+
+ duration = ((1000*(double)(end.tv_sec - start.tv_sec))
+ + (.001*(end.tv_usec - start.tv_usec)));
+ printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n",
+ duration, count / (duration / 1000.0),
+ count * message_size / (duration / 1000.0));
+}
+
+static void
+ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ usage();
+}
+\f
+/* replace-flows and diff-flows commands. */
+
+/* A flow table entry, possibly with two different versions. */
+struct fte {
+ struct cls_rule rule; /* Within a "struct classifier". */
+ struct fte_version *versions[2];
+};
+
+/* One version of a Flow Table Entry. */
+struct fte_version {
+ ovs_be64 cookie;
+ uint16_t idle_timeout;
+ uint16_t hard_timeout;
+ uint16_t flags;
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
+};
+
+/* Frees 'version' and the data that it owns. */
+static void
+fte_version_free(struct fte_version *version)
+{
+ if (version) {
+ free(version->ofpacts);
+ free(version);
+ }
+}
+
+/* Returns true if 'a' and 'b' are the same, false if they differ.
+ *
+ * Ignores differences in 'flags' because there's no way to retrieve flags from
+ * an OpenFlow switch. We have to assume that they are the same. */
+static bool
+fte_version_equals(const struct fte_version *a, const struct fte_version *b)
+{
+ return (a->cookie == b->cookie
+ && a->idle_timeout == b->idle_timeout
+ && a->hard_timeout == b->hard_timeout
+ && ofpacts_equal(a->ofpacts, a->ofpacts_len,
+ b->ofpacts, b->ofpacts_len));
+}
+
+/* Clears 's', then if 's' has a version 'index', formats 'fte' and version
+ * 'index' into 's', followed by a new-line. */
+static void
+fte_version_format(const struct fte *fte, int index, struct ds *s)
+{
+ const struct fte_version *version = fte->versions[index];
+
+ ds_clear(s);
+ if (!version) {
+ return;
+ }
+
+ cls_rule_format(&fte->rule, s);
+ if (version->cookie != htonll(0)) {
+ ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie));
+ }
+ if (version->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, " idle_timeout=%"PRIu16, version->idle_timeout);
+ }
+ if (version->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, " hard_timeout=%"PRIu16, version->hard_timeout);
+ }
+
+ ds_put_char(s, ' ');
+ ofpacts_format(version->ofpacts, version->ofpacts_len, s);
+
+ ds_put_char(s, '\n');
+}
+
+static struct fte *
+fte_from_cls_rule(const struct cls_rule *cls_rule)
+{
+ return cls_rule ? CONTAINER_OF(cls_rule, struct fte, rule) : NULL;
+}
+
+/* Frees 'fte' and its versions. */
+static void
+fte_free(struct fte *fte)
+{
+ if (fte) {
+ fte_version_free(fte->versions[0]);
+ fte_version_free(fte->versions[1]);
+ free(fte);
+ }
+}
+
+/* Frees all of the FTEs within 'cls'. */
+static void
+fte_free_all(struct classifier *cls)
+{
+ struct cls_cursor cursor;
+ struct fte *fte, *next;
+
+ cls_cursor_init(&cursor, cls, NULL);
+ CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
+ classifier_remove(cls, &fte->rule);
+ fte_free(fte);
+ }
+ classifier_destroy(cls);
+}
+
+/* Searches 'cls' for an FTE matching 'rule', inserting a new one if
+ * necessary. Sets 'version' as the version of that rule with the given
+ * 'index', replacing any existing version, if any.
+ *
+ * Takes ownership of 'version'. */
+static void
+fte_insert(struct classifier *cls, const struct cls_rule *rule,
+ struct fte_version *version, int index)
+{
+ struct fte *old, *fte;
+
+ fte = xzalloc(sizeof *fte);
+ fte->rule = *rule;
+ fte->versions[index] = version;
+
+ old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+ if (old) {
+ fte_version_free(old->versions[index]);
+ fte->versions[!index] = old->versions[!index];
+ free(old);
+ }
+}
+
+/* Reads the flows in 'filename' as flow table entries in 'cls' for the version
+ * with the specified 'index'. Returns the flow formats able to represent the
+ * flows that were read. */
+static enum ofputil_protocol
+read_flows_from_file(const char *filename, struct classifier *cls, int index)
+{
+ enum ofputil_protocol usable_protocols;
+ struct ds s;
+ FILE *file;
+
+ file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
+ if (file == NULL) {
+ ovs_fatal(errno, "%s: open", filename);
+ }
+
+ ds_init(&s);
+ usable_protocols = OFPUTIL_P_ANY;
+ while (!ds_get_preprocessed_line(&s, file)) {
+ struct fte_version *version;
+ struct ofputil_flow_mod fm;
+
+ parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
+
+ version = xmalloc(sizeof *version);
+ version->cookie = fm.new_cookie;
+ version->idle_timeout = fm.idle_timeout;
+ version->hard_timeout = fm.hard_timeout;
+ version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF10_EMERG);
+ version->ofpacts = fm.ofpacts;
+ version->ofpacts_len = fm.ofpacts_len;
+
+ usable_protocols &= ofputil_usable_protocols(&fm.cr);
+
+ fte_insert(cls, &fm.cr, version, index);
+ }
+ ds_destroy(&s);
+
+ if (file != stdin) {
+ fclose(file);
+ }
+
+ 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 (;;) {
+ int retval;
+ bool more;
+
+ /* Get a flow stats reply message, if we don't already have one. */
+ if (!reply) {
+ enum ofptype type;
+ enum ofperr error;
+
+ do {
+ run(vconn_recv_block(vconn, &reply),
+ "OpenFlow packet receive failed");
+ } while (((struct ofp_header *) reply->data)->xid != send_xid);
+
+ error = ofptype_decode(&type, reply->data);
+ if (error || type != OFPTYPE_FLOW_STATS_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:
+ more = ofpmp_more(reply->l2);
+ ofpbuf_delete(reply);
+ reply = NULL;
+ if (!more) {
+ *replyp = NULL;
+ return false;
+ }
+ break;