ovs-ofctl: Check that commands actually succeed.
[sliver-openvswitch.git] / utilities / ovs-ofctl.c
index ff43058..54e4b0c 100644 (file)
 #include <sys/time.h>
 
 #include "byte-order.h"
+#include "classifier.h"
 #include "command-line.h"
 #include "compiler.h"
 #include "dirs.h"
 #include "dpif.h"
+#include "dynamic-string.h"
 #include "netlink.h"
+#include "nx-match.h"
 #include "odp-util.h"
 #include "ofp-parse.h"
 #include "ofp-print.h"
 
 VLOG_DEFINE_THIS_MODULE(ofctl);
 
-
-#define MOD_PORT_CMD_UP      "up"
-#define MOD_PORT_CMD_DOWN    "down"
-#define MOD_PORT_CMD_FLOOD   "flood"
-#define MOD_PORT_CMD_NOFLOOD "noflood"
-
 /* Use strict matching for flow mod commands? */
 static bool strict;
 
@@ -214,7 +211,7 @@ open_vconn__(const char *name, const char *default_suffix,
     struct stat s;
     char *bridge_path, *datapath_name, *datapath_type;
 
-    bridge_path = xasprintf("%s/%s.%s", ovs_rundir, name, default_suffix);
+    bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
     dp_parse_name(name, &datapath_name, &datapath_type);
 
     if (strstr(name, ":")) {
@@ -236,7 +233,7 @@ open_vconn__(const char *name, const char *default_suffix,
         }
 
         socket_name = xasprintf("%s/%s.%s",
-                                ovs_rundir, dpif_name, default_suffix);
+                                ovs_rundir(), dpif_name, default_suffix);
         if (stat(socket_name, &s)) {
             ovs_fatal(errno, "cannot connect to %s: stat failed on %s",
                       name, socket_name);
@@ -304,7 +301,7 @@ dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
 static void
 dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
 {
-    uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
+    ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
     struct vconn *vconn;
     bool done = false;
 
@@ -340,6 +337,24 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
     dump_stats_transaction(vconn_name, request);
 }
 
+/* Sends 'request', which should be a request that only has a reply if an error
+ * occurs, and waits for it to succeed or fail.  If an error does occur, prints
+ * it and exits with an error. */
+static void
+dump_noreply_transaction(struct vconn *vconn, struct ofpbuf *request)
+{
+    struct ofpbuf *reply;
+
+    update_openflow_length(request);
+    run(vconn_transact_noreply(vconn, request, &reply),
+        "talking to %s", vconn_get_name(vconn));
+    if (reply) {
+        ofp_print(stderr, reply->data, reply->size, 2);
+        exit(1);
+    }
+    ofpbuf_delete(reply);
+}
+
 static void
 do_show(int argc OVS_UNUSED, char *argv[])
 {
@@ -389,60 +404,81 @@ do_dump_tables(int argc OVS_UNUSED, char *argv[])
     dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
 }
 
-static uint16_t
-str_to_port_no(const char *vconn_name, const char *str)
+/* Opens a connection to 'vconn_name', fetches the ofp_phy_port structure for
+ * 'port_name' (which may be a port name or number), and copies it into
+ * '*oppp'. */
+static void
+fetch_ofp_phy_port(const char *vconn_name, const char *port_name,
+                   struct ofp_phy_port *oppp)
 {
     struct ofpbuf *request, *reply;
     struct ofp_switch_features *osf;
+    unsigned int port_no;
     struct vconn *vconn;
     int n_ports;
     int port_idx;
-    unsigned int port_no;
-
 
-    /* Check if the argument is a port index.  Otherwise, treat it as
-     * the port name. */
-    if (str_to_uint(str, 10, &port_no)) {
-        return port_no;
+    /* Try to interpret the argument as a port number. */
+    if (!str_to_uint(port_name, 10, &port_no)) {
+        port_no = UINT_MAX;
     }
 
-    /* Send a "Features Request" to resolve the name into a number. */
+    /* Fetch the switch's ofp_switch_features. */
     make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
     open_vconn(vconn_name, &vconn);
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
 
     osf = reply->data;
+    if (reply->size < sizeof *osf) {
+        ovs_fatal(0, "%s: received too-short features reply (only %zu bytes)",
+                  vconn_name, reply->size);
+    }
     n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
 
     for (port_idx = 0; port_idx < n_ports; port_idx++) {
-        /* Check argument as an interface name */
-        if (!strncmp((char *)osf->ports[port_idx].name, str,
-                    sizeof osf->ports[0].name)) {
-            break;
+        const struct ofp_phy_port *opp = &osf->ports[port_idx];
+
+        if (port_no != UINT_MAX
+            ? htons(port_no) == opp->port_no
+            : !strncmp((char *) opp->name, port_name, sizeof opp->name)) {
+            *oppp = *opp;
+            ofpbuf_delete(reply);
+            vconn_close(vconn);
+            return;
         }
     }
-    if (port_idx == n_ports) {
-        ovs_fatal(0, "couldn't find monitored port: %s", str);
-    }
+    ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
+}
 
-    ofpbuf_delete(reply);
-    vconn_close(vconn);
+/* Returns the port number corresponding to 'port_name' (which may be a port
+ * name or number) within the switch 'vconn_name'. */
+static uint16_t
+str_to_port_no(const char *vconn_name, const char *port_name)
+{
+    unsigned int port_no;
+
+    if (str_to_uint(port_name, 10, &port_no)) {
+        return port_no;
+    } else {
+        struct ofp_phy_port opp;
 
-    return port_idx;
+        fetch_ofp_phy_port(vconn_name, port_name, &opp);
+        return ntohs(opp.port_no);
+    }
 }
 
 static void
 do_dump_flows(int argc, char *argv[])
 {
     struct ofp_flow_stats_request *req;
-    uint16_t out_port;
+    struct parsed_flow pf;
     struct ofpbuf *request;
 
     req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
-    parse_ofp_str(argc > 2 ? argv[2] : "", &req->match, NULL,
-                  &req->table_id, &out_port, NULL, NULL, NULL, NULL);
+    parse_ofp_str(&pf, NULL, argc > 2 ? argv[2] : "");
+    ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match);
     memset(&req->pad, 0, sizeof req->pad);
-    req->out_port = htons(out_port);
+    req->out_port = htons(pf.out_port);
 
     dump_stats_transaction(argv[1], request);
 }
@@ -452,13 +488,13 @@ do_dump_aggregate(int argc, char *argv[])
 {
     struct ofp_aggregate_stats_request *req;
     struct ofpbuf *request;
-    uint16_t out_port;
+    struct parsed_flow pf;
 
     req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
-    parse_ofp_str(argc > 2 ? argv[2] : "", &req->match, NULL,
-                  &req->table_id, &out_port, NULL, NULL, NULL, NULL);
+    parse_ofp_str(&pf, NULL, argc > 2 ? argv[2] : "");
+    ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match);
     memset(&req->pad, 0, sizeof req->pad);
-    req->out_port = htons(out_port);
+    req->out_port = htons(pf.out_port);
 
     dump_stats_transaction(argv[1], request);
 }
@@ -491,30 +527,12 @@ static void
 do_add_flow(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
-    struct ofpbuf *buffer;
-    struct ofp_flow_mod *ofm;
-    uint16_t priority, idle_timeout, hard_timeout;
-    uint64_t cookie;
-    struct ofp_match match;
-
-    /* Parse and send.  parse_ofp_str() will expand and reallocate the
-     * data in 'buffer', so we can't keep pointers to across the
-     * parse_ofp_str() call. */
-    make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    parse_ofp_str(argv[2], &match, buffer,
-                  NULL, NULL, &priority, &idle_timeout, &hard_timeout,
-                  &cookie);
-    ofm = buffer->data;
-    ofm->match = match;
-    ofm->command = htons(OFPFC_ADD);
-    ofm->cookie = htonll(cookie);
-    ofm->idle_timeout = htons(idle_timeout);
-    ofm->hard_timeout = htons(hard_timeout);
-    ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->priority = htons(priority);
+    struct ofpbuf *request;
+
+    request = parse_ofp_flow_mod_str(argv[2], OFPFC_ADD);
 
     open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
+    dump_noreply_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -532,7 +550,7 @@ do_add_flows(int argc OVS_UNUSED, char *argv[])
 
     open_vconn(argv[1], &vconn);
     while ((b = parse_ofp_add_flow_file(file)) != NULL) {
-        send_openflow_buffer(vconn, b);
+        dump_noreply_transaction(vconn, b);
     }
     vconn_close(vconn);
     fclose(file);
@@ -541,79 +559,28 @@ do_add_flows(int argc OVS_UNUSED, char *argv[])
 static void
 do_mod_flows(int argc OVS_UNUSED, char *argv[])
 {
-    uint16_t priority, idle_timeout, hard_timeout;
-    uint64_t cookie;
     struct vconn *vconn;
     struct ofpbuf *buffer;
-    struct ofp_flow_mod *ofm;
-    struct ofp_match match;
-
-    /* Parse and send.  parse_ofp_str() will expand and reallocate the
-     * data in 'buffer', so we can't keep pointers to across the
-     * parse_ofp_str() call. */
-    make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    parse_ofp_str(argv[2], &match, buffer,
-                  NULL, NULL, &priority, &idle_timeout, &hard_timeout,
-                  &cookie);
-    ofm = buffer->data;
-    ofm->match = match;
-    if (strict) {
-        ofm->command = htons(OFPFC_MODIFY_STRICT);
-    } else {
-        ofm->command = htons(OFPFC_MODIFY);
-    }
-    ofm->idle_timeout = htons(idle_timeout);
-    ofm->hard_timeout = htons(hard_timeout);
-    ofm->cookie = htonll(cookie);
-    ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->priority = htons(priority);
+    uint16_t command;
 
+    command = strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY;
+    buffer = parse_ofp_flow_mod_str(argv[2], command);
     open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
+    dump_noreply_transaction(vconn, buffer);
     vconn_close(vconn);
 }
 
 static void do_del_flows(int argc, char *argv[])
 {
     struct vconn *vconn;
-    uint16_t priority;
-    uint16_t out_port;
     struct ofpbuf *buffer;
-    struct ofp_flow_mod *ofm;
-
-    /* Parse and send. */
-    ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    parse_ofp_str(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL,
-                  &out_port, &priority, NULL, NULL, NULL);
-    if (strict) {
-        ofm->command = htons(OFPFC_DELETE_STRICT);
-    } else {
-        ofm->command = htons(OFPFC_DELETE);
-    }
-    ofm->idle_timeout = htons(0);
-    ofm->hard_timeout = htons(0);
-    ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->out_port = htons(out_port);
-    ofm->priority = htons(priority);
+    uint16_t command;
 
-    open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
-    vconn_close(vconn);
-}
-
-static void
-do_tun_cookie(int argc OVS_UNUSED, char *argv[])
-{
-    struct nxt_tun_id_cookie *tun_id_cookie;
-    struct ofpbuf *buffer;
-    struct vconn *vconn;
-
-    tun_id_cookie = make_nxmsg(sizeof *tun_id_cookie, NXT_TUN_ID_FROM_COOKIE,
-                               &buffer);
-    tun_id_cookie->set = !strcmp(argv[2], "true");
+    command = strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE;
+    buffer = parse_ofp_flow_mod_str(argc > 2 ? argv[2] : "", command);
 
     open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
+    dump_noreply_transaction(vconn, buffer);
     vconn_close(vconn);
 }
 
@@ -641,7 +608,7 @@ do_monitor(int argc, char *argv[])
 
         osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
         osc->miss_send_len = htons(miss_send_len);
-        send_openflow_buffer(vconn, buf);
+        dump_noreply_transaction(vconn, buf);
     }
     monitor_vconn(vconn);
 }
@@ -688,80 +655,36 @@ do_probe(int argc OVS_UNUSED, char *argv[])
 static void
 do_mod_port(int argc OVS_UNUSED, char *argv[])
 {
-    struct ofpbuf *request, *reply;
-    struct ofp_switch_features *osf;
     struct ofp_port_mod *opm;
+    struct ofp_phy_port opp;
+    struct ofpbuf *request;
     struct vconn *vconn;
-    char *endptr;
-    int n_ports;
-    int port_idx;
-    int port_no;
-
-
-    /* Check if the argument is a port index.  Otherwise, treat it as
-     * the port name. */
-    port_no = strtol(argv[2], &endptr, 10);
-    if (port_no == 0 && endptr == argv[2]) {
-        port_no = -1;
-    }
 
-    /* Send a "Features Request" to get the information we need in order
-     * to modify the port. */
-    make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
-    open_vconn(argv[1], &vconn);
-    run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
-
-    osf = reply->data;
-    n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
-
-    for (port_idx = 0; port_idx < n_ports; port_idx++) {
-        if (port_no != -1) {
-            /* Check argument as a port index */
-            if (osf->ports[port_idx].port_no == htons(port_no)) {
-                break;
-            }
-        } else {
-            /* Check argument as an interface name */
-            if (!strncmp((char *)osf->ports[port_idx].name, argv[2],
-                        sizeof osf->ports[0].name)) {
-                break;
-            }
-
-        }
-    }
-    if (port_idx == n_ports) {
-        ovs_fatal(0, "couldn't find monitored port: %s", argv[2]);
-    }
+    fetch_ofp_phy_port(argv[1], argv[2], &opp);
 
     opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request);
-    opm->port_no = osf->ports[port_idx].port_no;
-    memcpy(opm->hw_addr, osf->ports[port_idx].hw_addr, sizeof opm->hw_addr);
+    opm->port_no = opp.port_no;
+    memcpy(opm->hw_addr, opp.hw_addr, sizeof opm->hw_addr);
     opm->config = htonl(0);
     opm->mask = htonl(0);
     opm->advertise = htonl(0);
 
-    printf("modifying port: %s\n", osf->ports[port_idx].name);
-
-    if (!strncasecmp(argv[3], MOD_PORT_CMD_UP, sizeof MOD_PORT_CMD_UP)) {
+    if (!strcasecmp(argv[3], "up")) {
         opm->mask |= htonl(OFPPC_PORT_DOWN);
-    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_DOWN,
-                sizeof MOD_PORT_CMD_DOWN)) {
+    } else if (!strcasecmp(argv[3], "down")) {
         opm->mask |= htonl(OFPPC_PORT_DOWN);
         opm->config |= htonl(OFPPC_PORT_DOWN);
-    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD,
-                sizeof MOD_PORT_CMD_FLOOD)) {
+    } else if (!strcasecmp(argv[3], "flood")) {
         opm->mask |= htonl(OFPPC_NO_FLOOD);
-    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_NOFLOOD,
-                sizeof MOD_PORT_CMD_NOFLOOD)) {
+    } else if (!strcasecmp(argv[3], "noflood")) {
         opm->mask |= htonl(OFPPC_NO_FLOOD);
         opm->config |= htonl(OFPPC_NO_FLOOD);
     } else {
         ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
     }
 
-    send_openflow_buffer(vconn, request);
-
-    ofpbuf_delete(reply);
+    open_vconn(argv[1], &vconn);
+    dump_noreply_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -803,7 +726,7 @@ do_ping(int argc, char *argv[])
             ofp_print(stdout, reply, reply->size, 2);
         }
         printf("%zu bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
-               reply->size - sizeof *rpy_hdr, argv[1], rpy_hdr->xid,
+               reply->size - sizeof *rpy_hdr, argv[1], ntohl(rpy_hdr->xid),
                    (1000*(double)(end.tv_sec - start.tv_sec))
                    + (.001*(end.tv_usec - start.tv_usec)));
         ofpbuf_delete(request);
@@ -855,8 +778,14 @@ do_benchmark(int argc OVS_UNUSED, char *argv[])
            count * message_size / (duration / 1000.0));
 }
 
-/* This command is really only useful for testing the flow parser (ofp_parse),
- * so it is undocumented. */
+static void
+do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    usage();
+}
+\f
+/* Undocumented commands for unit testing. */
+
 static void
 do_parse_flows(int argc OVS_UNUSED, char *argv[])
 {
@@ -876,9 +805,57 @@ do_parse_flows(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
-{
-    usage();
+do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct ds in;
+
+    ds_init(&in);
+    while (!ds_get_line(&in, stdin)) {
+        struct ofpbuf nx_match;
+        struct cls_rule rule;
+        int match_len;
+        int error;
+        char *s;
+
+        /* Delete comments, skip blank lines. */
+        s = ds_cstr(&in);
+        if (*s == '#') {
+            puts(s);
+            continue;
+        }
+        if (strchr(s, '#')) {
+            *strchr(s, '#') = '\0';
+        }
+        if (s[strspn(s, " ")] == '\0') {
+            putchar('\n');
+            continue;
+        }
+
+        /* Convert string to nx_match. */
+        ofpbuf_init(&nx_match, 0);
+        match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
+
+        /* Convert nx_match to cls_rule. */
+        error = nx_pull_match(&nx_match, match_len, 0, &rule);
+        if (!error) {
+            char *out;
+
+            /* Convert cls_rule back to nx_match. */
+            ofpbuf_uninit(&nx_match);
+            ofpbuf_init(&nx_match, 0);
+            match_len = nx_put_match(&nx_match, &rule);
+
+            /* Convert nx_match to string. */
+            out = nx_match_to_string(nx_match.data, match_len);
+            puts(out);
+            free(out);
+        } else {
+            printf("nx_pull_match() returned error %x\n", error);
+        }
+
+        ofpbuf_uninit(&nx_match);
+    }
+    ds_destroy(&in);
 }
 
 static const struct command all_commands[] = {
@@ -895,13 +872,16 @@ static const struct command all_commands[] = {
     { "add-flows", 2, 2, do_add_flows },
     { "mod-flows", 2, 2, do_mod_flows },
     { "del-flows", 1, 2, do_del_flows },
-    { "tun-cookie", 2, 2, do_tun_cookie },
     { "dump-ports", 1, 2, do_dump_ports },
     { "mod-port", 3, 3, do_mod_port },
     { "probe", 1, 1, do_probe },
     { "ping", 1, 2, do_ping },
     { "benchmark", 3, 3, do_benchmark },
-    { "parse-flows", 1, 1, do_parse_flows },
     { "help", 0, INT_MAX, do_help },
+
+    /* Undocumented commands for testing. */
+    { "parse-flows", 1, 1, do_parse_flows },
+    { "parse-nx-match", 0, 0, do_parse_nx_match },
+
     { NULL, 0, 0, NULL },
 };