tests: Prefer development Python files over installed ones.
[openvswitch] / utilities / ovs-ofctl.c
index c565e8640a76f200d20eb863be937b096b997b16..fd0829eaa42922b098a2b41f690d380d011a8293 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/fcntl.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 
@@ -78,6 +79,10 @@ static int preferred_packet_in_format = -1;
 /* -m, --more: Additional verbosity for ofp-print functions. */
 static int verbosity;
 
+/* --timestamp: Print a timestamp before each received packet on "monitor" and
+ * "snoop" command? */
+static bool timestamp;
+
 static const struct command all_commands[];
 
 static void usage(void) NO_RETURN;
@@ -99,6 +104,7 @@ parse_options(int argc, char *argv[])
     enum {
         OPT_STRICT = UCHAR_MAX + 1,
         OPT_READD,
+        OPT_TIMESTAMP,
         DAEMON_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
     };
@@ -109,6 +115,7 @@ parse_options(int argc, char *argv[])
         {"flow-format", required_argument, NULL, 'F'},
         {"packet-in-format", required_argument, NULL, 'P'},
         {"more", no_argument, NULL, 'm'},
+        {"timestamp", no_argument, NULL, OPT_TIMESTAMP},
         {"help", no_argument, NULL, 'h'},
         {"version", no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
@@ -172,6 +179,10 @@ parse_options(int argc, char *argv[])
             readd = true;
             break;
 
+        case OPT_TIMESTAMP:
+            timestamp = true;
+            break;
+
         DAEMON_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
@@ -230,6 +241,7 @@ usage(void)
            "  -F, --flow-format=FORMAT    force particular flow format\n"
            "  -P, --packet-in-format=FRMT force particular packet in format\n"
            "  -m, --more                  be more verbose printing OpenFlow\n"
+           "  --timestamp                 (monitor, snoop) print timestamps\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
@@ -242,7 +254,7 @@ ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
 {
     bool *exiting = exiting_;
     *exiting = true;
-    unixctl_command_reply(conn, 200, "");
+    unixctl_command_reply(conn, NULL);
 }
 
 static void run(int retval, const char *message, ...)
@@ -831,9 +843,135 @@ monitor_set_invalid_ttl_to_controller(struct vconn *vconn)
     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();
+    fprintf(stderr, "send: ");
+    ofp_print(stderr, msg->data, msg->size, verbosity);
+
+    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
 monitor_vconn(struct vconn *vconn)
 {
+    struct barrier_aux barrier_aux = { vconn, NULL };
     struct unixctl_server *server;
     bool exiting = false;
     int error;
@@ -845,6 +983,12 @@ monitor_vconn(struct vconn *vconn)
         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);
     daemonize_complete();
 
     for (;;) {
@@ -854,14 +998,29 @@ monitor_vconn(struct vconn *vconn)
         unixctl_server_run(server);
 
         for (;;) {
+            uint8_t msg_type;
+
             retval = vconn_recv(vconn, &b);
             if (retval == EAGAIN) {
                 break;
             }
+            msg_type = ((const struct ofp_header *) b->data)->type;
 
             run(retval, "vconn_recv");
+            if (timestamp) {
+                time_t now = time_wall();
+                char s[32];
+
+                strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", localtime(&now));
+                fputs(s, stderr);
+            }
             ofp_print(stderr, b->data, b->size, verbosity + 2);
             ofpbuf_delete(b);
+
+            if (barrier_aux.conn && msg_type == OFPT_BARRIER_REPLY) {
+                unixctl_command_reply(barrier_aux.conn, NULL);
+                barrier_aux.conn = NULL;
+            }
         }
 
         if (exiting) {