+static enum ofperr
+ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
+ struct ofpbuf *b, bool aggregate)
+{
+ const struct ofp11_flow_stats_request *ofsr;
+ enum ofperr error;
+
+ ofsr = ofpbuf_pull(b, sizeof *ofsr);
+ fsr->aggregate = aggregate;
+ fsr->table_id = ofsr->table_id;
+ error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port);
+ if (error) {
+ return error;
+ }
+ fsr->out_group = ntohl(ofsr->out_group);
+ fsr->cookie = ofsr->cookie;
+ fsr->cookie_mask = ofsr->cookie_mask;
+ error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
+ struct ofpbuf *b, bool aggregate)
+{
+ const struct nx_flow_stats_request *nfsr;
+ enum ofperr error;
+
+ nfsr = ofpbuf_pull(b, sizeof *nfsr);
+ error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match,
+ &fsr->cookie, &fsr->cookie_mask);
+ if (error) {
+ return error;
+ }
+ if (ofpbuf_size(b)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ fsr->aggregate = aggregate;
+ fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port));
+ fsr->out_group = OFPG11_ANY;
+ fsr->table_id = nfsr->table_id;
+
+ return 0;
+}
+
+/* Constructs and returns an OFPT_QUEUE_GET_CONFIG request for the specified
+ * 'port', suitable for OpenFlow version 'version'. */
+struct ofpbuf *
+ofputil_encode_queue_get_config_request(enum ofp_version version,
+ ofp_port_t port)
+{
+ struct ofpbuf *request;
+
+ if (version == OFP10_VERSION) {
+ struct ofp10_queue_get_config_request *qgcr10;
+
+ request = ofpraw_alloc(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST,
+ version, 0);
+ qgcr10 = ofpbuf_put_zeros(request, sizeof *qgcr10);
+ qgcr10->port = htons(ofp_to_u16(port));
+ } else {
+ struct ofp11_queue_get_config_request *qgcr11;
+
+ request = ofpraw_alloc(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST,
+ version, 0);
+ qgcr11 = ofpbuf_put_zeros(request, sizeof *qgcr11);
+ qgcr11->port = ofputil_port_to_ofp11(port);
+ }
+
+ return request;
+}
+
+/* Parses OFPT_QUEUE_GET_CONFIG request 'oh', storing the port specified by the
+ * request into '*port'. Returns 0 if successful, otherwise an OpenFlow error
+ * code. */
+enum ofperr
+ofputil_decode_queue_get_config_request(const struct ofp_header *oh,
+ ofp_port_t *port)
+{
+ const struct ofp10_queue_get_config_request *qgcr10;
+ const struct ofp11_queue_get_config_request *qgcr11;
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ switch ((int) raw) {
+ case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
+ qgcr10 = ofpbuf_data(&b);
+ *port = u16_to_ofp(ntohs(qgcr10->port));
+ return 0;
+
+ case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
+ qgcr11 = ofpbuf_data(&b);
+ return ofputil_port_from_ofp11(qgcr11->port, port);
+ }
+
+ OVS_NOT_REACHED();
+}
+
+/* Constructs and returns the beginning of a reply to
+ * OFPT_QUEUE_GET_CONFIG_REQUEST 'oh'. The caller may append information about
+ * individual queues with ofputil_append_queue_get_config_reply(). */
+struct ofpbuf *
+ofputil_encode_queue_get_config_reply(const struct ofp_header *oh)
+{
+ struct ofp10_queue_get_config_reply *qgcr10;
+ struct ofp11_queue_get_config_reply *qgcr11;
+ struct ofpbuf *reply;
+ enum ofperr error;
+ struct ofpbuf b;
+ enum ofpraw raw;
+ ofp_port_t port;
+
+ error = ofputil_decode_queue_get_config_request(oh, &port);
+ ovs_assert(!error);
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ switch ((int) raw) {
+ case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
+ reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY,
+ oh, 0);
+ qgcr10 = ofpbuf_put_zeros(reply, sizeof *qgcr10);
+ qgcr10->port = htons(ofp_to_u16(port));
+ break;
+
+ case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
+ reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY,
+ oh, 0);
+ qgcr11 = ofpbuf_put_zeros(reply, sizeof *qgcr11);
+ qgcr11->port = ofputil_port_to_ofp11(port);
+ break;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ return reply;
+}
+
+static void
+put_queue_rate(struct ofpbuf *reply, enum ofp_queue_properties property,
+ uint16_t rate)
+{
+ if (rate != UINT16_MAX) {
+ struct ofp_queue_prop_rate *oqpr;
+
+ oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr);
+ oqpr->prop_header.property = htons(property);
+ oqpr->prop_header.len = htons(sizeof *oqpr);
+ oqpr->rate = htons(rate);
+ }
+}
+
+/* Appends a queue description for 'queue_id' to the
+ * OFPT_QUEUE_GET_CONFIG_REPLY already in 'oh'. */
+void
+ofputil_append_queue_get_config_reply(struct ofpbuf *reply,
+ const struct ofputil_queue_config *oqc)
+{
+ const struct ofp_header *oh = ofpbuf_data(reply);
+ size_t start_ofs, len_ofs;
+ ovs_be16 *len;
+
+ start_ofs = ofpbuf_size(reply);
+ if (oh->version < OFP12_VERSION) {
+ struct ofp10_packet_queue *opq10;
+
+ opq10 = ofpbuf_put_zeros(reply, sizeof *opq10);
+ opq10->queue_id = htonl(oqc->queue_id);
+ len_ofs = (char *) &opq10->len - (char *) ofpbuf_data(reply);
+ } else {
+ struct ofp11_queue_get_config_reply *qgcr11;
+ struct ofp12_packet_queue *opq12;
+ ovs_be32 port;
+
+ qgcr11 = ofpbuf_l3(reply);
+ port = qgcr11->port;
+
+ opq12 = ofpbuf_put_zeros(reply, sizeof *opq12);
+ opq12->port = port;
+ opq12->queue_id = htonl(oqc->queue_id);
+ len_ofs = (char *) &opq12->len - (char *) ofpbuf_data(reply);
+ }
+
+ put_queue_rate(reply, OFPQT_MIN_RATE, oqc->min_rate);
+ put_queue_rate(reply, OFPQT_MAX_RATE, oqc->max_rate);
+
+ len = ofpbuf_at(reply, len_ofs, sizeof *len);
+ *len = htons(ofpbuf_size(reply) - start_ofs);
+}
+
+/* Decodes the initial part of an OFPT_QUEUE_GET_CONFIG_REPLY from 'reply' and
+ * stores in '*port' the port that the reply is about. The caller may call
+ * ofputil_pull_queue_get_config_reply() to obtain information about individual
+ * queues included in the reply. Returns 0 if successful, otherwise an
+ * ofperr.*/
+enum ofperr
+ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *port)
+{
+ const struct ofp10_queue_get_config_reply *qgcr10;
+ const struct ofp11_queue_get_config_reply *qgcr11;
+ enum ofpraw raw;
+
+ raw = ofpraw_pull_assert(reply);
+ switch ((int) raw) {
+ case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY:
+ qgcr10 = ofpbuf_pull(reply, sizeof *qgcr10);
+ *port = u16_to_ofp(ntohs(qgcr10->port));
+ return 0;
+
+ case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY:
+ qgcr11 = ofpbuf_pull(reply, sizeof *qgcr11);
+ return ofputil_port_from_ofp11(qgcr11->port, port);
+ }
+
+ OVS_NOT_REACHED();
+}
+
+static enum ofperr
+parse_queue_rate(const struct ofp_queue_prop_header *hdr, uint16_t *rate)
+{
+ const struct ofp_queue_prop_rate *oqpr;
+
+ if (hdr->len == htons(sizeof *oqpr)) {
+ oqpr = (const struct ofp_queue_prop_rate *) hdr;
+ *rate = ntohs(oqpr->rate);
+ return 0;
+ } else {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+}
+
+/* Decodes information about a queue from the OFPT_QUEUE_GET_CONFIG_REPLY in
+ * 'reply' and stores it in '*queue'. ofputil_decode_queue_get_config_reply()
+ * must already have pulled off the main header.
+ *
+ * This function returns EOF if the last queue has already been decoded, 0 if a
+ * queue was successfully decoded into '*queue', or an ofperr if there was a
+ * problem decoding 'reply'. */
+int
+ofputil_pull_queue_get_config_reply(struct ofpbuf *reply,
+ struct ofputil_queue_config *queue)
+{
+ const struct ofp_header *oh;
+ unsigned int opq_len;
+ unsigned int len;
+
+ if (!ofpbuf_size(reply)) {
+ return EOF;
+ }
+
+ queue->min_rate = UINT16_MAX;
+ queue->max_rate = UINT16_MAX;
+
+ oh = reply->frame;
+ if (oh->version < OFP12_VERSION) {
+ const struct ofp10_packet_queue *opq10;
+
+ opq10 = ofpbuf_try_pull(reply, sizeof *opq10);
+ if (!opq10) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ queue->queue_id = ntohl(opq10->queue_id);
+ len = ntohs(opq10->len);
+ opq_len = sizeof *opq10;
+ } else {
+ const struct ofp12_packet_queue *opq12;
+
+ opq12 = ofpbuf_try_pull(reply, sizeof *opq12);
+ if (!opq12) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ queue->queue_id = ntohl(opq12->queue_id);
+ len = ntohs(opq12->len);
+ opq_len = sizeof *opq12;
+ }
+
+ if (len < opq_len || len > ofpbuf_size(reply) + opq_len || len % 8) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ len -= opq_len;
+
+ while (len > 0) {
+ const struct ofp_queue_prop_header *hdr;
+ unsigned int property;
+ unsigned int prop_len;
+ enum ofperr error = 0;
+
+ hdr = ofpbuf_at_assert(reply, 0, sizeof *hdr);
+ prop_len = ntohs(hdr->len);
+ if (prop_len < sizeof *hdr || prop_len > ofpbuf_size(reply) || prop_len % 8) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ property = ntohs(hdr->property);
+ switch (property) {
+ case OFPQT_MIN_RATE:
+ error = parse_queue_rate(hdr, &queue->min_rate);
+ break;
+
+ case OFPQT_MAX_RATE:
+ error = parse_queue_rate(hdr, &queue->max_rate);
+ break;
+
+ default:
+ VLOG_INFO_RL(&bad_ofmsg_rl, "unknown queue property %u", property);
+ break;
+ }
+ if (error) {
+ return error;
+ }
+
+ ofpbuf_pull(reply, prop_len);
+ len -= prop_len;
+ }
+ return 0;
+}
+
+/* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
+ * request 'oh', into an abstract flow_stats_request in 'fsr'. Returns 0 if
+ * successful, otherwise an OpenFlow error code. */
+enum ofperr
+ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
+ const struct ofp_header *oh)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ switch ((int) raw) {
+ case OFPRAW_OFPST10_FLOW_REQUEST:
+ return ofputil_decode_ofpst10_flow_request(fsr, ofpbuf_data(&b), false);
+
+ case OFPRAW_OFPST10_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst10_flow_request(fsr, ofpbuf_data(&b), true);
+
+ case OFPRAW_OFPST11_FLOW_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, false);
+
+ case OFPRAW_OFPST11_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, true);
+
+ case OFPRAW_NXST_FLOW_REQUEST:
+ return ofputil_decode_nxst_flow_request(fsr, &b, false);
+
+ case OFPRAW_NXST_AGGREGATE_REQUEST:
+ return ofputil_decode_nxst_flow_request(fsr, &b, true);
+
+ default:
+ /* Hey, the caller lied. */
+ OVS_NOT_REACHED();
+ }
+}
+
+/* Converts abstract flow_stats_request 'fsr' into an OFPST_FLOW,
+ * OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE request 'oh' according to
+ * 'protocol', and returns the message. */
+struct ofpbuf *
+ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
+ enum ofputil_protocol protocol)
+{
+ struct ofpbuf *msg;
+ enum ofpraw raw;
+
+ switch (protocol) {
+ case OFPUTIL_P_OF11_STD:
+ case OFPUTIL_P_OF12_OXM:
+ case OFPUTIL_P_OF13_OXM:
+ case OFPUTIL_P_OF14_OXM: {
+ struct ofp11_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST11_AGGREGATE_REQUEST
+ : OFPRAW_OFPST11_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
+ ofputil_match_typical_len(protocol));
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofsr->table_id = fsr->table_id;
+ ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
+ ofsr->out_group = htonl(fsr->out_group);
+ ofsr->cookie = fsr->cookie;
+ ofsr->cookie_mask = fsr->cookie_mask;
+ ofputil_put_ofp11_match(msg, &fsr->match, protocol);
+ break;
+ }
+
+ case OFPUTIL_P_OF10_STD:
+ case OFPUTIL_P_OF10_STD_TID: {
+ struct ofp10_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST10_AGGREGATE_REQUEST
+ : OFPRAW_OFPST10_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP10_VERSION, 0);
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofputil_match_to_ofp10_match(&fsr->match, &ofsr->match);
+ ofsr->table_id = fsr->table_id;
+ ofsr->out_port = htons(ofp_to_u16(fsr->out_port));
+ break;
+ }
+
+ case OFPUTIL_P_OF10_NXM:
+ case OFPUTIL_P_OF10_NXM_TID: {
+ struct nx_flow_stats_request *nfsr;
+ int match_len;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_NXST_AGGREGATE_REQUEST
+ : OFPRAW_NXST_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP10_VERSION, NXM_TYPICAL_LEN);
+ ofpbuf_put_zeros(msg, sizeof *nfsr);
+ match_len = nx_put_match(msg, &fsr->match,
+ fsr->cookie, fsr->cookie_mask);
+
+ nfsr = ofpbuf_l3(msg);
+ nfsr->out_port = htons(ofp_to_u16(fsr->out_port));
+ nfsr->match_len = htons(match_len);
+ nfsr->table_id = fsr->table_id;
+ break;
+ }
+
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ return msg;
+}
+
+/* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract
+ * ofputil_flow_stats in 'fs'.
+ *
+ * Multiple OFPST_FLOW or NXST_FLOW replies can be packed into a single