bridge: Fix high cpu utilization.
[cascardo/ovs.git] / utilities / ovs-ofctl.c
index a8704bf..01b3f60 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -91,6 +91,10 @@ static int verbosity;
  * "snoop" command? */
 static bool timestamp;
 
+/* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop"
+     commands. */
+static char *unixctl_path;
+
 /* --sort, --rsort: Sort order. */
 enum sort_order { SORT_ASC, SORT_DESC };
 struct sort_criterion {
@@ -149,6 +153,7 @@ parse_options(int argc, char *argv[])
         OPT_TIMESTAMP,
         OPT_SORT,
         OPT_RSORT,
+        OPT_UNIXCTL,
         DAEMON_OPTION_ENUMS,
         OFP_VERSION_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
@@ -163,6 +168,7 @@ parse_options(int argc, char *argv[])
         {"timestamp", no_argument, NULL, OPT_TIMESTAMP},
         {"sort", optional_argument, NULL, OPT_SORT},
         {"rsort", optional_argument, NULL, OPT_RSORT},
+        {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
         {"help", no_argument, NULL, 'h'},
         DAEMON_LONG_OPTIONS,
         OFP_VERSION_LONG_OPTIONS,
@@ -174,6 +180,23 @@ parse_options(int argc, char *argv[])
     uint32_t versions;
     enum ofputil_protocol version_protocols;
 
+    /* For now, ovs-ofctl only enables OpenFlow 1.0 by default.  This is
+     * because ovs-ofctl implements command such as "add-flow" as raw OpenFlow
+     * requests, but those requests have subtly different semantics in
+     * different OpenFlow versions.  For example:
+     *
+     *     - In OpenFlow 1.0, a "mod-flow" operation that does not find any
+     *       existing flow to modify adds a new flow.
+     *
+     *     - In OpenFlow 1.1, a "mod-flow" operation that does not find any
+     *       existing flow to modify adds a new flow, but only if the mod-flow
+     *       did not match on the flow cookie.
+     *
+     *     - In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a
+     *       new flow.
+     */
+    set_allowed_ofp_versions("OpenFlow10");
+
     for (;;) {
         unsigned long int timeout;
         int c;
@@ -236,6 +259,10 @@ parse_options(int argc, char *argv[])
             add_sort_criterion(SORT_DESC, optarg);
             break;
 
+        case OPT_UNIXCTL:
+            unixctl_path = optarg;
+            break;
+
         DAEMON_OPTION_HANDLERS
         OFP_VERSION_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
@@ -288,7 +315,7 @@ usage(void)
            "  get-frags SWITCH            print fragment handling behavior\n"
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
-           "  dump-ports-desc SWITCH      print port descriptions\n"
+           "  dump-ports-desc SWITCH [PORT]  print port descriptions\n"
            "  dump-flows SWITCH           print all flow entries\n"
            "  dump-flows SWITCH FLOW      print matching FLOWs\n"
            "  dump-aggregate SWITCH       print aggregate flow statistics\n"
@@ -310,7 +337,7 @@ usage(void)
            "  mod-group SWITCH GROUP      modify specific group\n"
            "  del-groups SWITCH [GROUP]   delete matching GROUPs\n"
            "  dump-group-features SWITCH  print group features\n"
-           "  dump-groups SWITCH          print group description\n"
+           "  dump-groups SWITCH [GROUP]  print group description\n"
            "  dump-group-stats SWITCH [GROUP]  print group statistics\n"
            "  queue-get-config SWITCH PORT  print queue information for port\n"
            "  add-meter SWITCH METER      add meter described by METER\n"
@@ -344,6 +371,7 @@ usage(void)
            "  -t, --timeout=SECS          give up after SECS seconds\n"
            "  --sort[=field]              sort in ascending order\n"
            "  --rsort[=field]             sort in descending order\n"
+           "  --unixctl=SOCKET            set control socket name\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
     exit(EXIT_SUCCESS);
@@ -615,27 +643,24 @@ static void
 ofctl_show(int argc OVS_UNUSED, char *argv[])
 {
     const char *vconn_name = argv[1];
+    enum ofp_version version;
     struct vconn *vconn;
     struct ofpbuf *request;
     struct ofpbuf *reply;
-    bool trunc;
+    bool has_ports;
 
     open_vconn(vconn_name, &vconn);
-    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
-                           vconn_get_version(vconn), 0);
+    version = vconn_get_version(vconn);
+    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0);
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
 
-    trunc = ofputil_switch_features_ports_trunc(reply);
+    has_ports = ofputil_switch_features_has_ports(reply);
     ofp_print(stdout, ofpbuf_data(reply), ofpbuf_size(reply), verbosity + 1);
-
     ofpbuf_delete(reply);
 
-    if (trunc) {
-        /* The Features Reply may not contain all the ports, so send a
-         * Port Description stats request, which doesn't have size
-         * constraints. */
-        dump_trivial_stats_transaction(vconn_name,
-                                       OFPRAW_OFPST_PORT_DESC_REQUEST);
+    if (!has_ports) {
+        request = ofputil_encode_port_desc_stats_request(version, OFPP_ANY);
+        dump_stats_transaction(vconn, request);
     }
     dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST);
     vconn_close(vconn);
@@ -668,43 +693,53 @@ ofctl_dump_table_features(int argc OVS_UNUSED, char *argv[])
     vconn_close(vconn);
 }
 
+static bool fetch_port_by_stats(struct vconn *,
+                                const char *port_name, ofp_port_t port_no,
+                                struct ofputil_phy_port *);
+
+/* Uses OFPT_FEATURES_REQUEST to attempt to fetch information about the port
+ * named 'port_name' or numbered 'port_no' into '*pp'.  Returns true if
+ * successful, false on failure.
+ *
+ * This is only appropriate for OpenFlow 1.0, 1.1, and 1.2, which include a
+ * list of ports in OFPT_FEATURES_REPLY. */
 static bool
-fetch_port_by_features(const char *vconn_name,
+fetch_port_by_features(struct vconn *vconn,
                        const char *port_name, ofp_port_t port_no,
-                       struct ofputil_phy_port *pp, bool *trunc)
+                       struct ofputil_phy_port *pp)
 {
     struct ofputil_switch_features features;
     const struct ofp_header *oh;
     struct ofpbuf *request, *reply;
-    struct vconn *vconn;
     enum ofperr error;
     enum ofptype type;
     struct ofpbuf b;
     bool found = false;
 
     /* Fetch the switch's ofp_switch_features. */
-    open_vconn(vconn_name, &vconn);
     request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
                            vconn_get_version(vconn), 0);
-    run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
-    vconn_close(vconn);
+    run(vconn_transact(vconn, request, &reply),
+        "talking to %s", vconn_get_name(vconn));
 
     oh = ofpbuf_data(reply);
     if (ofptype_decode(&type, ofpbuf_data(reply))
         || type != OFPTYPE_FEATURES_REPLY) {
-        ovs_fatal(0, "%s: received bad features reply", vconn_name);
+        ovs_fatal(0, "%s: received bad features reply", vconn_get_name(vconn));
     }
-
-    *trunc = false;
-    if (ofputil_switch_features_ports_trunc(reply)) {
-        *trunc = true;
-        goto exit;
+    if (!ofputil_switch_features_has_ports(reply)) {
+        /* The switch features reply does not contain a complete list of ports.
+         * Probably, there are more ports than will fit into a single 64 kB
+         * OpenFlow message.  Use OFPST_PORT_DESC to get a complete list of
+         * ports. */
+        ofpbuf_delete(reply);
+        return fetch_port_by_stats(vconn, port_name, port_no, pp);
     }
 
     error = ofputil_decode_switch_features(oh, &features, &b);
     if (error) {
         ovs_fatal(0, "%s: failed to decode features reply (%s)",
-                  vconn_name, ofperr_to_string(error));
+                  vconn_get_name(vconn), ofperr_to_string(error));
     }
 
     while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
@@ -712,30 +747,35 @@ fetch_port_by_features(const char *vconn_name,
             ? port_no == pp->port_no
             : !strcmp(pp->name, port_name)) {
             found = true;
-            goto exit;
+            break;
         }
     }
-
-exit:
     ofpbuf_delete(reply);
     return found;
 }
 
+/* Uses a OFPST_PORT_DESC request to attempt to fetch information about the
+ * port named 'port_name' or numbered 'port_no' into '*pp'.  Returns true if
+ * successful, false on failure.
+ *
+ * This is most appropriate for OpenFlow 1.3 and later.  Open vSwitch 1.7 and
+ * later also implements OFPST_PORT_DESC, as an extension, for OpenFlow 1.0,
+ * 1.1, and 1.2, so this can be used as a fallback in those versions when there
+ * are too many ports than fit in an OFPT_FEATURES_REPLY. */
 static bool
-fetch_port_by_stats(const char *vconn_name,
+fetch_port_by_stats(struct vconn *vconn,
                     const char *port_name, ofp_port_t port_no,
                     struct ofputil_phy_port *pp)
 {
     struct ofpbuf *request;
-    struct vconn *vconn;
     ovs_be32 send_xid;
     bool done = false;
     bool found = false;
 
-    request = ofpraw_alloc(OFPRAW_OFPST_PORT_DESC_REQUEST, OFP10_VERSION, 0);
+    request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn),
+                                                     port_no);
     send_xid = ((struct ofp_header *) ofpbuf_data(request))->xid;
 
-    open_vconn(vconn_name, &vconn);
     send_openflow_buffer(vconn, request);
     while (!done) {
         ovs_be32 recv_xid;
@@ -779,7 +819,6 @@ fetch_port_by_stats(const char *vconn_name,
         }
         ofpbuf_delete(reply);
     }
-    vconn_close(vconn);
 
     return found;
 }
@@ -802,23 +841,23 @@ static void
 fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
                        struct ofputil_phy_port *pp)
 {
+    struct vconn *vconn;
     ofp_port_t port_no;
     bool found;
-    bool trunc;
 
     /* Try to interpret the argument as a port number. */
     if (!str_to_ofp(port_name, &port_no)) {
         port_no = OFPP_NONE;
     }
 
-    /* Try to find the port based on the Features Reply.  If it looks
-     * like the results may be truncated, then use the Port Description
-     * stats message introduced in OVS 1.7. */
-    found = fetch_port_by_features(vconn_name, port_name, port_no, pp,
-                                   &trunc);
-    if (trunc) {
-        found = fetch_port_by_stats(vconn_name, port_name, port_no, pp);
-    }
+    /* OpenFlow 1.0, 1.1, and 1.2 put the list of ports in the
+     * OFPT_FEATURES_REPLY message.  OpenFlow 1.3 and later versions put it
+     * into the OFPST_PORT_DESC reply.  Try it the correct way. */
+    open_vconn(vconn_name, &vconn);
+    found = (vconn_get_version(vconn) < OFP13_VERSION
+             ? fetch_port_by_features(vconn, port_name, port_no, pp)
+             : fetch_port_by_stats(vconn, port_name, port_no, pp));
+    vconn_close(vconn);
 
     if (!found) {
         ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
@@ -1026,7 +1065,7 @@ ofctl_dump_flows(int argc, char *argv[])
         ds_destroy(&s);
 
         for (i = 0; i < n_fses; i++) {
-            free(fses[i].ofpacts);
+            free(CONST_CAST(struct ofpact *, fses[i].ofpacts));
         }
         free(fses);
 
@@ -1136,7 +1175,7 @@ ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
         struct ofputil_flow_mod *fm = &fms[i];
 
         transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
-        free(fm->ofpacts);
+        free(CONST_CAST(struct ofpact *, fm->ofpacts));
     }
     vconn_close(vconn);
 }
@@ -1409,7 +1448,7 @@ monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests)
 
     daemon_save_fd(STDERR_FILENO);
     daemonize_start();
-    error = unixctl_server_create(NULL, &server);
+    error = unixctl_server_create(unixctl_path, &server);
     if (error) {
         ovs_fatal(error, "failed to create unixctl server");
     }
@@ -1556,6 +1595,7 @@ ofctl_monitor(int argc, char *argv[])
         case OFP12_VERSION:
         case OFP13_VERSION:
         case OFP14_VERSION:
+        case OFP15_VERSION:
             break;
         default:
             OVS_NOT_REACHED();
@@ -1591,7 +1631,16 @@ ofctl_dump_ports(int argc, char *argv[])
 static void
 ofctl_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
 {
-    dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_PORT_DESC_REQUEST);
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    ofp_port_t port;
+
+    open_vconn(argv[1], &vconn);
+    port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
+    request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn),
+                                                     port);
+    dump_stats_transaction(vconn, request);
+    vconn_close(vconn);
 }
 
 static void
@@ -2144,10 +2193,16 @@ ofctl_dump_group_desc(int argc OVS_UNUSED, char *argv[])
 {
     struct ofpbuf *request;
     struct vconn *vconn;
+    uint32_t group_id;
 
     open_vconn(argv[1], &vconn);
 
-    request = ofputil_encode_group_desc_request(vconn_get_version(vconn));
+    if (argc < 3 || !ofputil_group_from_string(argv[2], &group_id)) {
+        group_id = OFPG11_ALL;
+    }
+
+    request = ofputil_encode_group_desc_request(vconn_get_version(vconn),
+                                                group_id);
     if (request) {
         dump_stats_transaction(vconn, request);
     }
@@ -2199,7 +2254,7 @@ static void
 fte_version_free(struct fte_version *version)
 {
     if (version) {
-        free(version->ofpacts);
+        free(CONST_CAST(struct ofpact *, version->ofpacts));
         free(version);
     }
 }
@@ -2396,7 +2451,7 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
             return true;
 
         case EOF:
-            more = ofpmp_more(reply->l2);
+            more = ofpmp_more(reply->frame);
             ofpbuf_delete(reply);
             reply = NULL;
             if (!more) {
@@ -2739,7 +2794,7 @@ ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
         ofp_print(stdout, ofpbuf_data(msg), ofpbuf_size(msg), verbosity);
         ofpbuf_delete(msg);
 
-        free(fm->ofpacts);
+        free(CONST_CAST(struct ofpact *, fm->ofpacts));
     }
 }
 
@@ -2779,7 +2834,7 @@ ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-ofctl_parse_nxm__(bool oxm)
+ofctl_parse_nxm__(bool oxm, enum ofp_version version)
 {
     struct ds in;
 
@@ -2824,7 +2879,7 @@ ofctl_parse_nxm__(bool oxm)
             ofpbuf_uninit(&nx_match);
             ofpbuf_init(&nx_match, 0);
             if (oxm) {
-                match_len = oxm_put_match(&nx_match, &match);
+                match_len = oxm_put_match(&nx_match, &match, version);
                 out = oxm_match_to_string(&nx_match, match_len);
             } else {
                 match_len = nx_put_match(&nx_match, &match,
@@ -2850,16 +2905,22 @@ ofctl_parse_nxm__(bool oxm)
 static void
 ofctl_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
-    return ofctl_parse_nxm__(false);
+    return ofctl_parse_nxm__(false, 0);
 }
 
-/* "parse-oxm": reads a series of OXM nx_match specifications as strings from
- * stdin, does some internal fussing with them, and then prints them back as
- * strings on stdout. */
+/* "parse-oxm VERSION": reads a series of OXM nx_match specifications as
+ * strings from stdin, does some internal fussing with them, and then prints
+ * them back as strings on stdout.  VERSION must specify an OpenFlow version,
+ * e.g. "OpenFlow12". */
 static void
-ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[])
 {
-    return ofctl_parse_nxm__(true);
+    enum ofp_version version = ofputil_version_from_string(argv[1]);
+    if (version < OFP12_VERSION) {
+        ovs_fatal(0, "%s: not a valid version for OXM", argv[1]);
+    }
+
+    return ofctl_parse_nxm__(true, version);
 }
 
 static void
@@ -3305,7 +3366,7 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
 
     /* Convert to and from OXM. */
     ofpbuf_init(&nxm, 0);
-    nxm_match_len = oxm_put_match(&nxm, &match);
+    nxm_match_len = oxm_put_match(&nxm, &match, OFP12_VERSION);
     nxm_s = oxm_match_to_string(&nxm, nxm_match_len);
     error = oxm_pull_match(&nxm, &nxm_match);
     printf("OXM: %s -> ", nxm_s);
@@ -3469,7 +3530,7 @@ static const struct command all_commands[] = {
     { "meter-features", 1, 1, ofctl_meter_features },
     { "packet-out", 4, INT_MAX, ofctl_packet_out },
     { "dump-ports", 1, 2, ofctl_dump_ports },
-    { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
+    { "dump-ports-desc", 1, 2, ofctl_dump_ports_desc },
     { "mod-port", 3, 3, ofctl_mod_port },
     { "mod-table", 3, 3, ofctl_mod_table },
     { "get-frags", 1, 1, ofctl_get_frags },
@@ -3485,7 +3546,7 @@ static const struct command all_commands[] = {
     { "add-groups", 1, 2, ofctl_add_groups },
     { "mod-group", 1, 2, ofctl_mod_group },
     { "del-groups", 1, 2, ofctl_del_groups },
-    { "dump-groups", 1, 1, ofctl_dump_group_desc },
+    { "dump-groups", 1, 2, ofctl_dump_group_desc },
     { "dump-group-stats", 1, 2, ofctl_dump_group_stats },
     { "dump-group-features", 1, 1, ofctl_dump_group_features },
     { "help", 0, INT_MAX, ofctl_help },
@@ -3495,7 +3556,7 @@ static const struct command all_commands[] = {
     { "parse-flows", 1, 1, ofctl_parse_flows },
     { "parse-nx-match", 0, 0, ofctl_parse_nxm },
     { "parse-nxm", 0, 0, ofctl_parse_nxm },
-    { "parse-oxm", 0, 0, ofctl_parse_oxm },
+    { "parse-oxm", 1, 1, ofctl_parse_oxm },
     { "parse-ofp10-actions", 0, 0, ofctl_parse_ofp10_actions },
     { "parse-ofp10-match", 0, 0, ofctl_parse_ofp10_match },
     { "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match },