+static void
+set_packet_in_format(struct vconn *vconn,
+ enum nx_packet_in_format packet_in_format)
+{
+ struct ofpbuf *spif = ofputil_make_set_packet_in_format(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();
+ 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) {
+ uint8_t msg_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);
+ }
+
+ msg_type = ((const struct ofp_header *) b->data)->type;
+ ofp_print(stderr, b->data, b->size, verbosity + 2);
+ ofpbuf_delete(b);
+
+ if (barrier_aux.conn && msg_type == OFPT10_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);
+ }