tests: Prefer development Python files over installed ones.
[openvswitch] / utilities / ovs-ofctl.c
index 7df0a90a3ae69db9466b0539e49118eaf91d8709..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>
 
@@ -45,6 +46,7 @@
 #include "ofproto/ofproto.h"
 #include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
+#include "packets.h"
 #include "poll-loop.h"
 #include "random.h"
 #include "stream-ssl.h"
@@ -77,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;
@@ -98,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
     };
@@ -108,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,
@@ -171,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
@@ -209,6 +221,8 @@ usage(void)
            "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
            "  replace-flows SWITCH FILE   replace flows with those in FILE\n"
            "  diff-flows SOURCE1 SOURCE2  compare flows from two sources\n"
+           "  packet-out SWITCH IN_PORT ACTIONS PACKET...\n"
+           "                              execute ACTIONS on PACKET\n"
            "  monitor SWITCH [MISSLEN] [invalid_ttl]\n"
            "                              print packets received from SWITCH\n"
            "  snoop SWITCH                snoop on SWITCH and its controller\n"
@@ -227,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");
@@ -239,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, ...)
@@ -828,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;
@@ -842,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 (;;) {
@@ -851,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) {
@@ -953,6 +1115,43 @@ do_probe(int argc OVS_UNUSED, char *argv[])
     vconn_close(vconn);
 }
 
+static void
+do_packet_out(int argc, char *argv[])
+{
+    struct ofputil_packet_out po;
+    struct ofpbuf actions;
+    struct vconn *vconn;
+    int i;
+
+    ofpbuf_init(&actions, sizeof(union ofp_action));
+    parse_ofp_actions(argv[3], &actions);
+
+    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.actions = actions.data;
+    po.n_actions = actions.size / sizeof(union ofp_action);
+
+    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);
+        transact_noreply(vconn, opo);
+        ofpbuf_delete(packet);
+    }
+    vconn_close(vconn);
+}
+
 static void
 do_mod_port(int argc OVS_UNUSED, char *argv[])
 {
@@ -1354,7 +1553,7 @@ read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format,
                 struct ofputil_flow_stats fs;
                 int retval;
 
-                retval = ofputil_decode_flow_stats_reply(&fs, reply);
+                retval = ofputil_decode_flow_stats_reply(&fs, reply, false);
                 if (retval) {
                     if (retval != EOF) {
                         ovs_fatal(0, "parse error in reply");
@@ -1682,6 +1881,7 @@ static const struct command all_commands[] = {
     { "del-flows", 1, 2, do_del_flows },
     { "replace-flows", 2, 2, do_replace_flows },
     { "diff-flows", 2, 2, do_diff_flows },
+    { "packet-out", 4, INT_MAX, do_packet_out },
     { "dump-ports", 1, 2, do_dump_ports },
     { "mod-port", 3, 3, do_mod_port },
     { "get-frags", 1, 1, do_get_frags },