" 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"
}
-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(struct vconn *vconn,
- const char *port_name, ofp_port_t port_no,
- struct ofputil_phy_port *pp)
+str_to_ofp(const char *s, ofp_port_t *ofp_port)
{
- struct ofputil_switch_features features;
- const struct ofp_header *oh;
- struct ofpbuf *request, *reply;
- enum ofperr error;
- enum ofptype type;
- struct ofpbuf b;
- bool found = false;
+ bool ret;
+ uint32_t port_;
+
+ ret = str_to_uint(s, 10, &port_);
+ *ofp_port = u16_to_ofp(port_);
+ return ret;
+}
+
+struct port_iterator {
+ struct vconn *vconn;
+
+ enum { PI_FEATURES, PI_PORT_DESC } variant;
+ struct ofpbuf *reply;
+ ovs_be32 send_xid;
+ bool more;
+};
+
+static void
+port_iterator_fetch_port_desc(struct port_iterator *pi)
+{
+ pi->variant = PI_PORT_DESC;
+ pi->more = true;
+
+ struct ofpbuf *rq = ofputil_encode_port_desc_stats_request(
+ vconn_get_version(pi->vconn), OFPP_ANY);
+ pi->send_xid = ((struct ofp_header *) rq->data)->xid;
+ send_openflow_buffer(pi->vconn, rq);
+}
+
+static void
+port_iterator_fetch_features(struct port_iterator *pi)
+{
+ pi->variant = PI_FEATURES;
/* Fetch the switch's ofp_switch_features. */
- request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
- vconn_get_version(vconn), 0);
- run(vconn_transact(vconn, request, &reply),
- "talking to %s", vconn_get_name(vconn));
+ enum ofp_version version = vconn_get_version(pi->vconn);
+ struct ofpbuf *rq = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0);
+ run(vconn_transact(pi->vconn, rq, &pi->reply),
+ "talking to %s", vconn_get_name(pi->vconn));
- oh = reply->data;
- if (ofptype_decode(&type, reply->data)
+ const struct ofp_header *oh = pi->reply->data;
+ enum ofptype type;
+ if (ofptype_decode(&type, pi->reply->data)
|| type != OFPTYPE_FEATURES_REPLY) {
- ovs_fatal(0, "%s: received bad features reply", vconn_get_name(vconn));
+ ovs_fatal(0, "%s: received bad features reply",
+ vconn_get_name(pi->vconn));
}
- if (!ofputil_switch_features_has_ports(reply)) {
+ if (!ofputil_switch_features_has_ports(pi->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);
+ ofpbuf_delete(pi->reply);
+ pi->reply = NULL;
+ port_iterator_fetch_port_desc(pi);
+ return;
}
- error = ofputil_decode_switch_features(oh, &features, &b);
+ struct ofputil_switch_features features;
+ enum ofperr error = ofputil_decode_switch_features(oh, &features,
+ pi->reply);
if (error) {
ovs_fatal(0, "%s: failed to decode features reply (%s)",
- vconn_get_name(vconn), ofperr_to_string(error));
+ vconn_get_name(pi->vconn), ofperr_to_string(error));
}
+}
- while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
- if (port_no != OFPP_NONE
- ? port_no == pp->port_no
- : !strcmp(pp->name, port_name)) {
- found = true;
- break;
- }
+/* Initializes 'pi' to prepare for iterating through all of the ports on the
+ * OpenFlow switch to which 'vconn' is connected.
+ *
+ * During iteration, the client should not make other use of 'vconn', because
+ * that can cause other messages to be interleaved with the replies used by the
+ * iterator and thus some ports may be missed or a hang can occur. */
+static void
+port_iterator_init(struct port_iterator *pi, struct vconn *vconn)
+{
+ memset(pi, 0, sizeof *pi);
+ pi->vconn = vconn;
+ if (vconn_get_version(vconn) < OFP13_VERSION) {
+ port_iterator_fetch_features(pi);
+ } else {
+ port_iterator_fetch_port_desc(pi);
}
- 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. */
+/* Obtains the next port from 'pi'. On success, initializes '*pp' with the
+ * port's details and returns true, otherwise (if all the ports have already
+ * been seen), returns false. */
static bool
-fetch_port_by_stats(struct vconn *vconn,
- const char *port_name, ofp_port_t port_no,
- struct ofputil_phy_port *pp)
+port_iterator_next(struct port_iterator *pi, struct ofputil_phy_port *pp)
{
- struct ofpbuf *request;
- ovs_be32 send_xid;
- bool done = false;
- bool found = false;
-
- request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn),
- port_no);
- send_xid = ((struct ofp_header *) request->data)->xid;
-
- 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) {
- struct ofp_header *oh = reply->data;
- enum ofptype type;
- struct ofpbuf b;
- uint16_t flags;
-
- ofpbuf_use_const(&b, oh, ntohs(oh->length));
- if (ofptype_pull(&type, &b)
- || type != OFPTYPE_PORT_DESC_STATS_REPLY) {
+ for (;;) {
+ if (pi->reply) {
+ int retval = ofputil_pull_phy_port(vconn_get_version(pi->vconn),
+ pi->reply, pp);
+ if (!retval) {
+ return true;
+ } else if (retval != EOF) {
ovs_fatal(0, "received bad reply: %s",
- ofp_to_string(reply->data, reply->size,
+ ofp_to_string(pi->reply->data, pi->reply->size,
verbosity + 1));
}
+ }
- flags = ofpmp_flags(oh);
- done = !(flags & OFPSF_REPLY_MORE);
-
- if (found) {
- /* We've already found the port, but we need to drain
- * the queue of any other replies for this request. */
- continue;
- }
+ if (pi->variant == PI_FEATURES || !pi->more) {
+ return false;
+ }
- while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
- if (port_no != OFPP_NONE ? port_no == pp->port_no
- : !strcmp(pp->name, port_name)) {
- found = true;
- break;
- }
- }
- } else {
- VLOG_DBG("received reply with xid %08"PRIx32" "
- "!= expected %08"PRIx32, recv_xid, send_xid);
+ ovs_be32 recv_xid;
+ do {
+ ofpbuf_delete(pi->reply);
+ run(vconn_recv_block(pi->vconn, &pi->reply),
+ "OpenFlow receive failed");
+ recv_xid = ((struct ofp_header *) pi->reply->data)->xid;
+ } while (pi->send_xid != recv_xid);
+
+ struct ofp_header *oh = pi->reply->data;
+ enum ofptype type;
+ if (ofptype_pull(&type, pi->reply)
+ || type != OFPTYPE_PORT_DESC_STATS_REPLY) {
+ ovs_fatal(0, "received bad reply: %s",
+ ofp_to_string(pi->reply->data, pi->reply->size,
+ verbosity + 1));
}
- ofpbuf_delete(reply);
- }
- return found;
+ pi->more = (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0;
+ }
}
-static bool
-str_to_ofp(const char *s, ofp_port_t *ofp_port)
+/* Destroys iterator 'pi'. */
+static void
+port_iterator_destroy(struct port_iterator *pi)
{
- bool ret;
- uint32_t port_;
+ if (pi) {
+ while (pi->variant == PI_PORT_DESC && pi->more) {
+ /* Drain vconn's queue of any other replies for this request. */
+ struct ofputil_phy_port pp;
+ port_iterator_next(pi, &pp);
+ }
- ret = str_to_uint(s, 10, &port_);
- *ofp_port = u16_to_ofp(port_);
- return ret;
+ ofpbuf_delete(pi->reply);
+ }
}
/* Opens a connection to 'vconn_name', fetches the port structure for
{
struct vconn *vconn;
ofp_port_t port_no;
- bool found;
+ bool found = false;
/* Try to interpret the argument as a port number. */
if (!str_to_ofp(port_name, &port_no)) {
* 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));
+ struct port_iterator pi;
+ for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, pp); ) {
+ if (port_no != OFPP_NONE
+ ? port_no == pp->port_no
+ : !strcmp(pp->name, port_name)) {
+ found = true;
+ break;
+ }
+ }
+ port_iterator_destroy(&pi);
vconn_close(vconn);
if (!found) {
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;
- struct vconn *vconn;
- ofp_port_t port;
+ const char *port_name = ctx->argc >= 3 ? ctx->argv[2] : NULL;
+ ofp_port_t port = (port_name
+ ? str_to_port_no(vconn_name, port_name)
+ : OFPP_ANY);
- 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);
+ struct vconn *vconn;
+ 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));
+ }
+ }
+ port_iterator_destroy(&pi);
+ vconn_close(vconn2);
+ } else {
+ dump_transaction(vconn, ofputil_encode_queue_get_config_request(
+ version, port));
+ }
vconn_close(vconn);
}
}
bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC);
+ ofpbuf_list_delete(&requests);
vconn_close(vconn);
}
} else {
transact_multiple_noreply(vconn, &requests);
}
+
+ ofpbuf_list_delete(&requests);
vconn_close(vconn);
fte_free_all(&tables);
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]",
+ 1, 2, ofctl_queue_get_config },
{ "add-flow", "switch flow",
2, 2, ofctl_add_flow },
{ "add-flows", "switch file",