openflow: Implement OF1.4+ OFPMP_QUEUE_DESC multipart message.
[cascardo/ovs.git] / utilities / ovs-ofctl.c
index 0b529e0..1ad48c3 100644 (file)
@@ -378,7 +378,7 @@ usage(void)
            "  dump-group-features SWITCH  print group features\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"
+           "  queue-get-config SWITCH [PORT]  print queue config for PORT\n"
            "  add-meter SWITCH METER      add meter described by METER\n"
            "  mod-meter SWITCH METER      modify specific METER\n"
            "  del-meter SWITCH METER      delete METER\n"
@@ -532,78 +532,64 @@ send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
 static void
 dump_transaction(struct vconn *vconn, struct ofpbuf *request)
 {
-    struct ofpbuf *reply;
-
-    run(vconn_transact(vconn, request, &reply), "talking to %s",
-        vconn_get_name(vconn));
-    ofp_print(stdout, reply->data, reply->size, verbosity + 1);
-    ofpbuf_delete(reply);
-}
-
-static void
-dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
-{
-    struct ofpbuf *request;
-    struct vconn *vconn;
-
-    open_vconn(vconn_name, &vconn);
-    request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
-    dump_transaction(vconn, request);
-    vconn_close(vconn);
-}
-
-static void
-dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request)
-{
-    const struct ofp_header *request_oh = request->data;
-    ovs_be32 send_xid = request_oh->xid;
-    enum ofpraw request_raw;
-    enum ofpraw reply_raw;
-    bool done = false;
+    const struct ofp_header *oh = request->data;
+    if (ofpmsg_is_stat_request(oh)) {
+        ovs_be32 send_xid = oh->xid;
+        enum ofpraw request_raw;
+        enum ofpraw reply_raw;
+        bool done = false;
 
-    ofpraw_decode_partial(&request_raw, request->data, request->size);
-    reply_raw = ofpraw_stats_request_to_reply(request_raw,
-                                              request_oh->version);
+        ofpraw_decode_partial(&request_raw, request->data, request->size);
+        reply_raw = ofpraw_stats_request_to_reply(request_raw, oh->version);
 
-    send_openflow_buffer(vconn, request);
-    while (!done) {
-        ovs_be32 recv_xid;
-        struct ofpbuf *reply;
+        send_openflow_buffer(vconn, request);
+        while (!done) {
+            ovs_be32 recv_xid;
+            struct ofpbuf *reply;
 
-        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
-        recv_xid = ((struct ofp_header *) reply->data)->xid;
-        if (send_xid == recv_xid) {
-            enum ofpraw raw;
+            run(vconn_recv_block(vconn, &reply),
+                "OpenFlow packet receive failed");
+            recv_xid = ((struct ofp_header *) reply->data)->xid;
+            if (send_xid == recv_xid) {
+                enum ofpraw raw;
 
-            ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+                ofp_print(stdout, reply->data, reply->size, verbosity + 1);
 
-            ofpraw_decode(&raw, reply->data);
-            if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
-                done = true;
-            } else if (raw == reply_raw) {
-                done = !ofpmp_more(reply->data);
+                ofpraw_decode(&raw, reply->data);
+                if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
+                    done = true;
+                } else if (raw == reply_raw) {
+                    done = !ofpmp_more(reply->data);
+                } else {
+                    ovs_fatal(0, "received bad reply: %s",
+                              ofp_to_string(reply->data, reply->size,
+                                            verbosity + 1));
+                }
             } else {
-                ovs_fatal(0, "received bad reply: %s",
-                          ofp_to_string(reply->data, reply->size,
-                                        verbosity + 1));
+                VLOG_DBG("received reply with xid %08"PRIx32" "
+                         "!= expected %08"PRIx32, recv_xid, send_xid);
             }
-        } else {
-            VLOG_DBG("received reply with xid %08"PRIx32" "
-                     "!= expected %08"PRIx32, recv_xid, send_xid);
+            ofpbuf_delete(reply);
         }
+    } else {
+        struct ofpbuf *reply;
+
+        run(vconn_transact(vconn, request, &reply), "talking to %s",
+            vconn_get_name(vconn));
+        ofp_print(stdout, reply->data, reply->size, verbosity + 1);
         ofpbuf_delete(reply);
     }
 }
 
 static void
-dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw)
+dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
 {
     struct ofpbuf *request;
     struct vconn *vconn;
 
     open_vconn(vconn_name, &vconn);
     request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
-    dump_stats_transaction(vconn, request);
+    dump_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -656,9 +642,8 @@ transact_noreply(struct vconn *vconn, struct ofpbuf *request)
 }
 
 static void
-fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
+fetch_switch_config(struct vconn *vconn, struct ofputil_switch_config *config)
 {
-    struct ofp_switch_config *config;
     struct ofpbuf *request;
     struct ofpbuf *reply;
     enum ofptype type;
@@ -668,25 +653,20 @@ fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
     run(vconn_transact(vconn, request, &reply),
         "talking to %s", vconn_get_name(vconn));
 
-    if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) {
+    if (ofptype_decode(&type, reply->data)
+        || type != OFPTYPE_GET_CONFIG_REPLY) {
         ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn));
     }
-
-    config = ofpbuf_pull(reply, sizeof *config);
-    *config_ = *config;
-
+    ofputil_decode_get_config_reply(reply->data, config);
     ofpbuf_delete(reply);
 }
 
 static void
-set_switch_config(struct vconn *vconn, const struct ofp_switch_config *config)
+set_switch_config(struct vconn *vconn,
+                  const struct ofputil_switch_config *config)
 {
-    struct ofpbuf *request;
-
-    request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, vconn_get_version(vconn), 0);
-    ofpbuf_put(request, config, sizeof *config);
-
-    transact_noreply(vconn, request);
+    enum ofp_version version = vconn_get_version(vconn);
+    transact_noreply(vconn, ofputil_encode_set_config(config, version));
 }
 
 static void
@@ -710,7 +690,7 @@ ofctl_show(struct ovs_cmdl_context *ctx)
 
     if (!has_ports) {
         request = ofputil_encode_port_desc_stats_request(version, OFPP_ANY);
-        dump_stats_transaction(vconn, request);
+        dump_transaction(vconn, request);
     }
     dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST);
     vconn_close(vconn);
@@ -719,13 +699,13 @@ ofctl_show(struct ovs_cmdl_context *ctx)
 static void
 ofctl_dump_desc(struct ovs_cmdl_context *ctx)
 {
-    dump_trivial_stats_transaction(ctx->argv[1], OFPRAW_OFPST_DESC_REQUEST);
+    dump_trivial_transaction(ctx->argv[1], OFPRAW_OFPST_DESC_REQUEST);
 }
 
 static void
 ofctl_dump_tables(struct ovs_cmdl_context *ctx)
 {
-    dump_trivial_stats_transaction(ctx->argv[1], OFPRAW_OFPST_TABLE_REQUEST);
+    dump_trivial_transaction(ctx->argv[1], OFPRAW_OFPST_TABLE_REQUEST);
 }
 
 static void
@@ -737,7 +717,7 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
     open_vconn(ctx->argv[1], &vconn);
     request = ofputil_encode_table_features_request(vconn_get_version(vconn));
 
-    /* The following is similar to dump_trivial_stats_transaction(), but it
+    /* The following is similar to dump_trivial_transaction(), but it
      * maintains the previous 'ofputil_table_features' from one stats reply
      * message to the next, which allows duplication to be eliminated in the
      * output across messages.  Otherwise the output is much larger and harder
@@ -816,7 +796,7 @@ ofctl_dump_table_desc(struct ovs_cmdl_context *ctx)
     open_vconn(ctx->argv[1], &vconn);
     request = ofputil_encode_table_desc_request(vconn_get_version(vconn));
     if (request) {
-        dump_stats_transaction(vconn, request);
+        dump_transaction(vconn, request);
     }
 
     vconn_close(vconn);
@@ -1108,7 +1088,7 @@ ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
     struct vconn *vconn;
 
     vconn = prepare_dump_flows(argc, argv, aggregate, &request);
-    dump_stats_transaction(vconn, request);
+    dump_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -1244,7 +1224,7 @@ ofctl_queue_stats(struct ovs_cmdl_context *ctx)
     }
 
     request = ofputil_encode_queue_stats_request(vconn_get_version(vconn), &oqs);
-    dump_stats_transaction(vconn, request);
+    dump_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -1252,19 +1232,42 @@ static void
 ofctl_queue_get_config(struct ovs_cmdl_context *ctx)
 {
     const char *vconn_name = ctx->argv[1];
-    const char *port_name = ctx->argv[2];
-    enum ofputil_protocol protocol;
-    enum ofp_version version;
-    struct ofpbuf *request;
+    const char *port_name = ctx->argc > 2 ? ctx->argv[2] : "any";
+    ofp_port_t port = str_to_port_no(vconn_name, port_name);
+    const char *queue_name = ctx->argc > 3 ? ctx->argv[3] : "all";
+    uint32_t queue = (!strcasecmp(queue_name, "all")
+                      ? OFPQ_ALL
+                      : atoi(queue_name));
     struct vconn *vconn;
-    ofp_port_t port;
-
-    port = str_to_port_no(vconn_name, port_name);
 
-    protocol = open_vconn(vconn_name, &vconn);
-    version = ofputil_protocol_to_ofp_version(protocol);
-    request = ofputil_encode_queue_get_config_request(version, port);
-    dump_transaction(vconn, request);
+    enum ofputil_protocol protocol = open_vconn(vconn_name, &vconn);
+    enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
+    if (port == OFPP_ANY && version == OFP10_VERSION) {
+        /* The user requested all queues on all ports.  OpenFlow 1.0 only
+         * supports getting queues for an individual port, so to implement the
+         * user's request we have to get a list of all the ports.
+         *
+         * We use a second vconn to avoid having to accumulate a list of all of
+         * the ports. */
+        struct vconn *vconn2;
+        enum ofputil_protocol protocol2 = open_vconn(vconn_name, &vconn2);
+        enum ofp_version version2 = ofputil_protocol_to_ofp_version(protocol2);
+
+        struct port_iterator pi;
+        struct ofputil_phy_port pp;
+        for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, &pp); ) {
+            if (ofp_to_u16(pp.port_no) < ofp_to_u16(OFPP_MAX)) {
+                dump_transaction(vconn2,
+                                 ofputil_encode_queue_get_config_request(
+                                     version2, pp.port_no, queue));
+            }
+        }
+        port_iterator_destroy(&pi);
+        vconn_close(vconn2);
+    } else {
+        dump_transaction(vconn, ofputil_encode_queue_get_config_request(
+                             version, port, queue));
+    }
     vconn_close(vconn);
 }
 
@@ -1440,27 +1443,20 @@ set_packet_in_format(struct vconn *vconn,
 static int
 monitor_set_invalid_ttl_to_controller(struct vconn *vconn)
 {
-    struct ofp_switch_config config;
-    enum ofp_config_flags flags;
+    struct ofputil_switch_config config;
 
     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);
+    if (!config.invalid_ttl_to_controller) {
+        config.invalid_ttl_to_controller = 1;
         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. */
+         * has ill-defined error reporting for bad flags, so this is about the
+         * best we can do. */
         fetch_switch_config(vconn, &config);
-        flags = ntohs(config.flags);
-        if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+        if (!config.invalid_ttl_to_controller) {
             ovs_fatal(0, "setting invalid_ttl_to_controller failed (this "
-                      "switch probably doesn't support mode)");
-            return -EOPNOTSUPP;
+                      "switch probably doesn't support this flag)");
         }
     }
     return 0;
@@ -1721,15 +1717,37 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
     int i;
     enum ofputil_protocol usable_protocols;
 
+    /* If the user wants the invalid_ttl_to_controller feature, limit the
+     * OpenFlow versions to those that support that feature.  (Support in
+     * OpenFlow 1.0 is an Open vSwitch extension.) */
+    for (i = 2; i < ctx->argc; i++) {
+        if (!strcmp(ctx->argv[i], "invalid_ttl")) {
+            uint32_t usable_versions = ((1u << OFP10_VERSION) |
+                                        (1u << OFP11_VERSION) |
+                                        (1u << OFP12_VERSION));
+            uint32_t allowed_versions = get_allowed_ofp_versions();
+            if (!(allowed_versions & usable_versions)) {
+                struct ds versions = DS_EMPTY_INITIALIZER;
+                ofputil_format_version_bitmap_names(&versions,
+                                                    usable_versions);
+                ovs_fatal(0, "invalid_ttl requires one of the OpenFlow "
+                          "versions %s but none is enabled (use -O)",
+                          ds_cstr(&versions));
+            }
+            mask_allowed_ofp_versions(usable_versions);
+            break;
+        }
+    }
+
     open_vconn(ctx->argv[1], &vconn);
     for (i = 2; i < ctx->argc; i++) {
         const char *arg = ctx->argv[i];
 
         if (isdigit((unsigned char) *arg)) {
-            struct ofp_switch_config config;
+            struct ofputil_switch_config config;
 
             fetch_switch_config(vconn, &config);
-            config.miss_send_len = htons(atoi(arg));
+            config.miss_send_len = atoi(arg);
             set_switch_config(vconn, &config);
         } else if (!strcmp(arg, "invalid_ttl")) {
             monitor_set_invalid_ttl_to_controller(vconn);
@@ -1746,7 +1764,7 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
 
             msg = ofpbuf_new(0);
             ofputil_append_flow_monitor_request(&fmr, msg);
-            dump_stats_transaction(vconn, msg);
+            dump_transaction(vconn, msg);
             fflush(stdout);
         } else {
             ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg);
@@ -1809,7 +1827,7 @@ ofctl_dump_ports(struct ovs_cmdl_context *ctx)
     open_vconn(ctx->argv[1], &vconn);
     port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY;
     request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port);
-    dump_stats_transaction(vconn, request);
+    dump_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -1824,7 +1842,7 @@ ofctl_dump_ports_desc(struct ovs_cmdl_context *ctx)
     port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY;
     request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn),
                                                      port);
-    dump_stats_transaction(vconn, request);
+    dump_transaction(vconn, request);
     vconn_close(vconn);
 }
 
@@ -2066,43 +2084,41 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx)
 static void
 ofctl_get_frags(struct ovs_cmdl_context *ctx)
 {
-    struct ofp_switch_config config;
+    struct ofputil_switch_config config;
     struct vconn *vconn;
 
     open_vconn(ctx->argv[1], &vconn);
     fetch_switch_config(vconn, &config);
-    puts(ofputil_frag_handling_to_string(ntohs(config.flags)));
+    puts(ofputil_frag_handling_to_string(config.frag));
     vconn_close(vconn);
 }
 
 static void
 ofctl_set_frags(struct ovs_cmdl_context *ctx)
 {
-    struct ofp_switch_config config;
-    enum ofp_config_flags mode;
+    struct ofputil_switch_config config;
+    enum ofputil_frag_handling frag;
     struct vconn *vconn;
-    ovs_be16 flags;
 
-    if (!ofputil_frag_handling_from_string(ctx->argv[2], &mode)) {
+    if (!ofputil_frag_handling_from_string(ctx->argv[2], &frag)) {
         ovs_fatal(0, "%s: unknown fragment handling mode", ctx->argv[2]);
     }
 
     open_vconn(ctx->argv[1], &vconn);
     fetch_switch_config(vconn, &config);
-    flags = htons(mode) | (config.flags & htons(~OFPC_FRAG_MASK));
-    if (flags != config.flags) {
+    if (frag != config.frag) {
         /* Set the configuration. */
-        config.flags = flags;
+        config.frag = frag;
         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. */
+         * has ill-defined error reporting for bad flags, so this is about the
+         * best we can do. */
         fetch_switch_config(vconn, &config);
-        if (flags != config.flags) {
+        if (frag != config.frag) {
             ovs_fatal(0, "%s: setting fragment handling mode failed (this "
                       "switch probably doesn't support mode \"%s\")",
-                      ctx->argv[1], ofputil_frag_handling_to_string(mode));
+                      ctx->argv[1], ofputil_frag_handling_to_string(frag));
         }
     }
     vconn_close(vconn);
@@ -2467,7 +2483,7 @@ ofctl_dump_group_stats(struct ovs_cmdl_context *ctx)
     request = ofputil_encode_group_stats_request(vconn_get_version(vconn),
                                                  group_id);
     if (request) {
-        dump_stats_transaction(vconn, request);
+        dump_transaction(vconn, request);
     }
 
     vconn_close(vconn);
@@ -2489,7 +2505,7 @@ ofctl_dump_group_desc(struct ovs_cmdl_context *ctx)
     request = ofputil_encode_group_desc_request(vconn_get_version(vconn),
                                                 group_id);
     if (request) {
-        dump_stats_transaction(vconn, request);
+        dump_transaction(vconn, request);
     }
 
     vconn_close(vconn);
@@ -2504,7 +2520,7 @@ ofctl_dump_group_features(struct ovs_cmdl_context *ctx)
     open_vconn(ctx->argv[1], &vconn);
     request = ofputil_encode_group_features_request(vconn_get_version(vconn));
     if (request) {
-        dump_stats_transaction(vconn, request);
+        dump_transaction(vconn, request);
     }
 
     vconn_close(vconn);
@@ -3119,9 +3135,8 @@ ofctl_meter_request__(const char *bridge, const char *str,
 
     protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
     version = ofputil_protocol_to_ofp_version(protocol);
-    dump_stats_transaction(vconn,
-                           ofputil_encode_meter_request(version, type,
-                                                        mm.meter.meter_id));
+    dump_transaction(vconn, ofputil_encode_meter_request(version, type,
+                                                         mm.meter.meter_id));
     vconn_close(vconn);
 }
 
@@ -3870,8 +3885,8 @@ static const struct ovs_cmdl_command all_commands[] = {
       1, 2, ofctl_dump_aggregate },
     { "queue-stats", "switch [port [queue]]",
       1, 3, ofctl_queue_stats },
-    { "queue-get-config", "switch port",
-      2, 2, ofctl_queue_get_config },
+    { "queue-get-config", "switch [port [queue]]",
+      1, 3, ofctl_queue_get_config },
     { "add-flow", "switch flow",
       2, 2, ofctl_add_flow },
     { "add-flows", "switch file",