* in the peer and so there's not much point in showing a lot of them. */
static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+struct ofp_prop_header {
+ ovs_be16 type;
+ ovs_be16 len;
+};
+
+/* Pulls a property, beginning with struct ofp_prop_header, from the beginning
+ * of 'msg'. Stores the type of the property in '*typep' and, if 'property' is
+ * nonnull, the entire property, including the header, in '*property'. Returns
+ * 0 if successful, otherwise an error code. */
+static enum ofperr
+ofputil_pull_property(struct ofpbuf *msg, struct ofpbuf *property,
+ uint16_t *typep)
+{
+ struct ofp_prop_header *oph;
+ unsigned int len;
+
+ if (ofpbuf_size(msg) < sizeof *oph) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ oph = ofpbuf_data(msg);
+ len = ntohs(oph->len);
+ if (len < sizeof *oph || ROUND_UP(len, 8) > ofpbuf_size(msg)) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ *typep = ntohs(oph->type);
+ if (property) {
+ ofpbuf_use_const(property, ofpbuf_data(msg), len);
+ }
+ ofpbuf_pull(msg, ROUND_UP(len, 8));
+ return 0;
+}
+
+static void PRINTF_FORMAT(2, 3)
+log_property(bool loose, const char *message, ...)
+{
+ enum vlog_level level = loose ? VLL_DBG : VLL_WARN;
+ if (!vlog_should_drop(THIS_MODULE, level, &bad_ofmsg_rl)) {
+ va_list args;
+
+ va_start(args, message);
+ vlog_valist(THIS_MODULE, level, message, args);
+ va_end(args);
+ }
+}
+
/* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns
* an IP netmask with a 1 in each bit that must match and a 0 in each bit that
* is wildcarded.
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
+ case OFPUTIL_P_OF15_OXM:
return NXM_TYPICAL_LEN;
default:
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
- return oxm_put_match(b, match);
+ case OFPUTIL_P_OF15_OXM:
+ return oxm_put_match(b, match,
+ ofputil_protocol_to_ofp_version(protocol));
}
OVS_NOT_REACHED();
};
/* Most users really don't care about some of the differences between
- * protocols. These abbreviations help with that.
- *
- * Until it is safe to use the OpenFlow 1.4 protocol (which currently can
- * cause aborts due to unimplemented features), we omit OpenFlow 1.4 from all
- * abbrevations. */
+ * protocols. These abbreviations help with that. */
static const struct proto_abbrev proto_abbrevs[] = {
- { OFPUTIL_P_ANY & ~OFPUTIL_P_OF14_OXM, "any" },
- { OFPUTIL_P_OF10_STD_ANY & ~OFPUTIL_P_OF14_OXM, "OpenFlow10" },
- { OFPUTIL_P_OF10_NXM_ANY & ~OFPUTIL_P_OF14_OXM, "NXM" },
- { OFPUTIL_P_ANY_OXM & ~OFPUTIL_P_OF14_OXM, "OXM" },
+ { OFPUTIL_P_ANY, "any" },
+ { OFPUTIL_P_OF10_STD_ANY, "OpenFlow10" },
+ { OFPUTIL_P_OF10_NXM_ANY, "NXM" },
+ { OFPUTIL_P_ANY_OXM, "OXM" },
};
#define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs)
enum ofputil_protocol ofputil_flow_dump_protocols[] = {
+ OFPUTIL_P_OF15_OXM,
OFPUTIL_P_OF14_OXM,
OFPUTIL_P_OF13_OXM,
OFPUTIL_P_OF12_OXM,
return OFPUTIL_P_OF13_OXM;
case OFP14_VERSION:
return OFPUTIL_P_OF14_OXM;
+ case OFP15_VERSION:
+ return OFPUTIL_P_OF15_OXM;
default:
return 0;
}
return OFP13_VERSION;
case OFPUTIL_P_OF14_OXM:
return OFP14_VERSION;
+ case OFPUTIL_P_OF15_OXM:
+ return OFP15_VERSION;
}
OVS_NOT_REACHED();
case OFPUTIL_P_OF14_OXM:
return OFPUTIL_P_OF14_OXM;
+ case OFPUTIL_P_OF15_OXM:
+ return OFPUTIL_P_OF15_OXM;
+
default:
OVS_NOT_REACHED();
}
case OFPUTIL_P_OF14_OXM:
return ofputil_protocol_set_tid(OFPUTIL_P_OF14_OXM, tid);
+ case OFPUTIL_P_OF15_OXM:
+ return ofputil_protocol_set_tid(OFPUTIL_P_OF15_OXM, tid);
+
default:
OVS_NOT_REACHED();
}
case OFPUTIL_P_OF14_OXM:
return "OXM-OpenFlow14";
+
+ case OFPUTIL_P_OF15_OXM:
+ return "OXM-OpenFlow15";
}
/* Check abbreviations. */
return protocols;
}
-static int
+enum ofp_version
ofputil_version_from_string(const char *s)
{
if (!strcasecmp(s, "OpenFlow10")) {
if (!strcasecmp(s, "OpenFlow14")) {
return OFP14_VERSION;
}
+ if (!strcasecmp(s, "OpenFlow15")) {
+ return OFP15_VERSION;
+ }
return 0;
}
return "OpenFlow13";
case OFP14_VERSION:
return "OpenFlow14";
+ case OFP15_VERSION:
+ return "OpenFlow15";
default:
OVS_NOT_REACHED();
}
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
+ case OFPUTIL_P_OF15_OXM:
/* There is only one variant of each OpenFlow 1.1+ protocol, and we
* verified above that we're not trying to change versions. */
OVS_NOT_REACHED();
struct ofpbuf b;
enum ofpraw raw;
+ /* Ignored for non-delete actions */
+ fm->delete_reason = OFPRR_DELETE;
+
ofpbuf_use_const(&b, oh, ntohs(oh->length));
raw = ofpraw_pull_assert(&b);
if (raw == OFPRAW_OFPT11_FLOW_MOD) {
case OFPUTIL_P_OF11_STD:
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
- case OFPUTIL_P_OF14_OXM: {
+ case OFPUTIL_P_OF14_OXM:
+ case OFPUTIL_P_OF15_OXM: {
struct ofp11_flow_mod *ofm;
int tailroom;
case OFPUTIL_P_OF11_STD:
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
- case OFPUTIL_P_OF14_OXM: {
+ case OFPUTIL_P_OF14_OXM:
+ case OFPUTIL_P_OF15_OXM: {
struct ofp11_flow_stats_request *ofsr;
raw = (fsr->aggregate
struct ofp11_flow_stats *ofs;
ofpbuf_put_uninit(reply, sizeof *ofs);
- oxm_put_match(reply, &fs->match);
+ oxm_put_match(reply, &fs->match, version);
ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
version);
enum ofputil_protocol protocol)
{
struct ofpbuf *msg;
+ enum ofp_flow_removed_reason reason = fr->reason;
+
+ if (reason == OFPRR_METER_DELETE && !(protocol & OFPUTIL_P_OF14_UP)) {
+ reason = OFPRR_DELETE;
+ }
switch (protocol) {
case OFPUTIL_P_OF11_STD:
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
- case OFPUTIL_P_OF14_OXM: {
+ case OFPUTIL_P_OF14_OXM:
+ case OFPUTIL_P_OF15_OXM: {
struct ofp12_flow_removed *ofr;
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
ofr->cookie = fr->cookie;
ofr->priority = htons(fr->priority);
- ofr->reason = fr->reason;
+ ofr->reason = reason;
ofr->table_id = fr->table_id;
ofr->duration_sec = htonl(fr->duration_sec);
ofr->duration_nsec = htonl(fr->duration_nsec);
ofputil_match_to_ofp10_match(&fr->match, &ofr->match);
ofr->cookie = fr->cookie;
ofr->priority = htons(fr->priority);
- ofr->reason = fr->reason;
+ ofr->reason = reason;
ofr->duration_sec = htonl(fr->duration_sec);
ofr->duration_nsec = htonl(fr->duration_nsec);
ofr->idle_timeout = htons(fr->idle_timeout);
nfr = ofpbuf_l3(msg);
nfr->cookie = fr->cookie;
nfr->priority = htons(fr->priority);
- nfr->reason = fr->reason;
+ nfr->reason = reason;
nfr->table_id = fr->table_id + 1;
nfr->duration_sec = htonl(fr->duration_sec);
nfr->duration_nsec = htonl(fr->duration_nsec);
htonl(0), (sizeof(struct flow_metadata) * 2
+ 2 + pin->packet_len));
ofpbuf_put_zeros(packet, packet_in_size);
- oxm_put_match(packet, &match);
+ oxm_put_match(packet, &match, ofputil_protocol_to_ofp_version(protocol));
ofpbuf_put_zeros(packet, 2);
ofpbuf_put(packet, pin->packet, pin->packet_len);
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
+ case OFPUTIL_P_OF15_OXM:
packet = ofputil_encode_ofp12_packet_in(pin, protocol);
break;
ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
const struct ofp10_phy_port *opp)
{
- memset(pp, 0, sizeof *pp);
-
pp->port_no = u16_to_ofp(ntohs(opp->port_no));
memcpy(pp->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
{
enum ofperr error;
- memset(pp, 0, sizeof *pp);
-
error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
if (error) {
return error;
return 0;
}
-static size_t
-ofputil_get_phy_port_size(enum ofp_version ofp_version)
+static enum ofperr
+parse_ofp14_port_ethernet_property(const struct ofpbuf *payload,
+ struct ofputil_phy_port *pp)
{
- switch (ofp_version) {
- case OFP10_VERSION:
- return sizeof(struct ofp10_phy_port);
- case OFP11_VERSION:
- case OFP12_VERSION:
- case OFP13_VERSION:
- case OFP14_VERSION:
- return sizeof(struct ofp11_port);
- default:
- OVS_NOT_REACHED();
+ struct ofp14_port_desc_prop_ethernet *eth = ofpbuf_data(payload);
+
+ if (ofpbuf_size(payload) != sizeof *eth) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ pp->curr = netdev_port_features_from_ofp11(eth->curr);
+ pp->advertised = netdev_port_features_from_ofp11(eth->advertised);
+ pp->supported = netdev_port_features_from_ofp11(eth->supported);
+ pp->peer = netdev_port_features_from_ofp11(eth->peer);
+
+ pp->curr_speed = ntohl(eth->curr_speed);
+ pp->max_speed = ntohl(eth->max_speed);
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+ struct ofpbuf properties;
+ struct ofp14_port *op;
+ enum ofperr error;
+ size_t len;
+
+ op = ofpbuf_try_pull(msg, sizeof *op);
+ if (!op) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ len = ntohs(op->length);
+ if (len < sizeof *op || len - sizeof *op > ofpbuf_size(msg)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ len -= sizeof *op;
+ ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
+
+ error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+ if (error) {
+ return error;
+ }
+ memcpy(pp->hw_addr, op->hw_addr, OFP_ETH_ALEN);
+ ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
+
+ pp->config = ntohl(op->config) & OFPPC11_ALL;
+ pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+ while (ofpbuf_size(&properties) > 0) {
+ struct ofpbuf payload;
+ enum ofperr error;
+ uint16_t type;
+
+ error = ofputil_pull_property(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPPDPT14_ETHERNET:
+ error = parse_ofp14_port_ethernet_property(&payload, pp);
+ break;
+
+ default:
+ log_property(true, "unknown port property %"PRIu16, type);
+ error = 0;
+ break;
+ }
+
+ if (error) {
+ return error;
+ }
}
+
+ return 0;
}
static void
op->max_speed = htonl(pp->max_speed);
}
+static void
+ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
+ struct ofpbuf *b)
+{
+ struct ofp14_port *op;
+ struct ofp14_port_desc_prop_ethernet *eth;
+
+ ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth);
+
+ op = ofpbuf_put_zeros(b, sizeof *op);
+ op->port_no = ofputil_port_to_ofp11(pp->port_no);
+ op->length = htons(sizeof *op + sizeof *eth);
+ memcpy(op->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+ ovs_strlcpy(op->name, pp->name, sizeof op->name);
+ op->config = htonl(pp->config & OFPPC11_ALL);
+ op->state = htonl(pp->state & OFPPS11_ALL);
+
+ eth = ofpbuf_put_zeros(b, sizeof *eth);
+ eth->type = htons(OFPPDPT14_ETHERNET);
+ eth->length = htons(sizeof *eth);
+ eth->curr = netdev_port_features_to_ofp11(pp->curr);
+ eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
+ eth->supported = netdev_port_features_to_ofp11(pp->supported);
+ eth->peer = netdev_port_features_to_ofp11(pp->peer);
+ eth->curr_speed = htonl(pp->curr_speed);
+ eth->max_speed = htonl(pp->max_speed);
+}
+
static void
ofputil_put_phy_port(enum ofp_version ofp_version,
const struct ofputil_phy_port *pp, struct ofpbuf *b)
{
switch (ofp_version) {
case OFP10_VERSION: {
- struct ofp10_phy_port *opp;
- if (ofpbuf_size(b) + sizeof *opp <= UINT16_MAX) {
- opp = ofpbuf_put_uninit(b, sizeof *opp);
- ofputil_encode_ofp10_phy_port(pp, opp);
- }
+ struct ofp10_phy_port *opp = ofpbuf_put_uninit(b, sizeof *opp);
+ ofputil_encode_ofp10_phy_port(pp, opp);
break;
}
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION: {
- struct ofp11_port *op;
- if (ofpbuf_size(b) + sizeof *op <= UINT16_MAX) {
- op = ofpbuf_put_uninit(b, sizeof *op);
- ofputil_encode_ofp11_port(pp, op);
- }
+ struct ofp11_port *op = ofpbuf_put_uninit(b, sizeof *op);
+ ofputil_encode_ofp11_port(pp, op);
break;
}
case OFP14_VERSION:
- OVS_NOT_REACHED();
+ case OFP15_VERSION:
+ ofputil_put_ofp14_port(pp, b);
break;
default:
}
}
-void
-ofputil_append_port_desc_stats_reply(const struct ofputil_phy_port *pp,
- struct list *replies)
+enum ofperr
+ofputil_decode_port_desc_stats_request(const struct ofp_header *request,
+ ofp_port_t *port)
{
- switch (ofpmp_version(replies)) {
- case OFP10_VERSION: {
- struct ofp10_phy_port *opp;
+ struct ofpbuf b;
+ enum ofpraw raw;
- opp = ofpmp_append(replies, sizeof *opp);
- ofputil_encode_ofp10_phy_port(pp, opp);
- break;
+ ofpbuf_use_const(&b, request, ntohs(request->length));
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPST10_PORT_DESC_REQUEST) {
+ *port = OFPP_ANY;
+ return 0;
+ } else if (raw == OFPRAW_OFPST15_PORT_DESC_REQUEST) {
+ ovs_be32 *ofp11_port;
+
+ ofp11_port = ofpbuf_pull(&b, sizeof *ofp11_port);
+ return ofputil_port_from_ofp11(*ofp11_port, port);
+ } else {
+ OVS_NOT_REACHED();
}
+}
+
+struct ofpbuf *
+ofputil_encode_port_desc_stats_request(enum ofp_version ofp_version,
+ ofp_port_t port)
+{
+ struct ofpbuf *request;
+ ovs_be32 ofp11_port;
+ switch (ofp_version) {
+ case OFP10_VERSION:
case OFP11_VERSION:
case OFP12_VERSION:
- case OFP13_VERSION: {
- struct ofp11_port *op;
-
- op = ofpmp_append(replies, sizeof *op);
- ofputil_encode_ofp11_port(pp, op);
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ request = ofpraw_alloc(OFPRAW_OFPST10_PORT_DESC_REQUEST,
+ ofp_version, 0);
break;
- }
- case OFP14_VERSION:
- OVS_NOT_REACHED();
+ case OFP15_VERSION:
+ request = ofpraw_alloc(OFPRAW_OFPST15_PORT_DESC_REQUEST,
+ ofp_version, 0);
+ ofp11_port = ofputil_port_to_ofp11(port);
+ ofpbuf_put(request, &ofp11_port, sizeof ofp11_port);
break;
default:
- OVS_NOT_REACHED();
+ OVS_NOT_REACHED();
}
+
+ return request;
+}
+
+void
+ofputil_append_port_desc_stats_reply(const struct ofputil_phy_port *pp,
+ struct list *replies)
+{
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ size_t start_ofs = ofpbuf_size(reply);
+
+ ofputil_put_phy_port(ofpmp_version(replies), pp, reply);
+ ofpmp_postappend(replies, start_ofs);
}
\f
/* ofputil_switch_features */
return OFPC_COMMON | OFPC_ARP_MATCH_IP;
case OFP12_VERSION:
case OFP13_VERSION:
- return OFPC_COMMON | OFPC12_PORT_BLOCKED;
case OFP14_VERSION:
- OVS_NOT_REACHED();
- break;
+ case OFP15_VERSION:
+ return OFPC_COMMON | OFPC12_PORT_BLOCKED;
default:
/* Caller needs to check osf->header.version itself */
return 0;
features->capabilities = ntohl(osf->capabilities) &
ofputil_capabilities_mask(oh->version);
- if (ofpbuf_size(b) % ofputil_get_phy_port_size(oh->version)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
-
if (raw == OFPRAW_OFPT10_FEATURES_REPLY) {
if (osf->capabilities & htonl(OFPC10_STP)) {
features->capabilities |= OFPUTIL_C_STP;
return 0;
}
-/* Returns true if the maximum number of ports are in 'oh'. */
-static bool
-max_ports_in_features(const struct ofp_header *oh)
-{
- size_t pp_size = ofputil_get_phy_port_size(oh->version);
- return ntohs(oh->length) + pp_size > UINT16_MAX;
-}
-
/* In OpenFlow 1.0, 1.1, and 1.2, an OFPT_FEATURES_REPLY message lists all the
* switch's ports, unless there are too many to fit. In OpenFlow 1.3 and
* later, an OFPT_FEATURES_REPLY does not list ports at all.
ofputil_switch_features_has_ports(struct ofpbuf *b)
{
struct ofp_header *oh = ofpbuf_data(b);
+ size_t phy_port_size;
if (oh->version >= OFP13_VERSION) {
+ /* OpenFlow 1.3+ never has ports in the feature reply. */
return false;
- } else if (max_ports_in_features(oh)) {
- ofpbuf_set_size(b, sizeof *oh + sizeof(struct ofp_switch_features));
- ofpmsg_update_length(b);
- return false;
- } else {
+ }
+
+ phy_port_size = (oh->version == OFP10_VERSION
+ ? sizeof(struct ofp10_phy_port)
+ : sizeof(struct ofp11_port));
+ if (ntohs(oh->length) + phy_port_size <= UINT16_MAX) {
+ /* There's room for additional ports in the feature reply.
+ * Assume that the list is complete. */
return true;
}
+
+ /* The feature reply has no room for more ports. Probably the list is
+ * truncated. Drop the ports and tell the caller to retrieve them with
+ * OFPST_PORT_DESC. */
+ ofpbuf_set_size(b, sizeof *oh + sizeof(struct ofp_switch_features));
+ ofpmsg_update_length(b);
+ return false;
}
static ovs_be32
break;
case OFP13_VERSION:
case OFP14_VERSION:
+ case OFP15_VERSION:
raw = OFPRAW_OFPT13_FEATURES_REPLY;
break;
default:
break;
case OFP13_VERSION:
case OFP14_VERSION:
+ case OFP15_VERSION:
osf->auxiliary_id = features->auxiliary_id;
/* fall through */
case OFP11_VERSION:
const struct ofp_header *oh = ofpbuf_data(b);
if (oh->version < OFP13_VERSION) {
+ /* Try adding a port description to the message, but drop it again if
+ * the buffer overflows. (This possibility for overflow is why
+ * OpenFlow 1.3+ moved port descriptions into a multipart message.) */
+ size_t start_ofs = ofpbuf_size(b);
ofputil_put_phy_port(oh->version, pp, b);
+ if (ofpbuf_size(b) > UINT16_MAX) {
+ ofpbuf_set_size(b, start_ofs);
+ }
}
}
\f
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
- case OFP14_VERSION:
raw = OFPRAW_OFPT11_PORT_STATUS;
break;
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ raw = OFPRAW_OFPT14_PORT_STATUS;
+ break;
+
default:
OVS_NOT_REACHED();
}
/* ofputil_port_mod */
+static enum ofperr
+parse_port_mod_ethernet_property(struct ofpbuf *property,
+ struct ofputil_port_mod *pm)
+{
+ struct ofp14_port_mod_prop_ethernet *eth = ofpbuf_data(property);
+
+ if (ofpbuf_size(property) != sizeof *eth) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ pm->advertise = netdev_port_features_from_ofp11(eth->advertise);
+ return 0;
+}
+
/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
* '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
ofputil_decode_port_mod(const struct ofp_header *oh,
- struct ofputil_port_mod *pm)
+ struct ofputil_port_mod *pm, bool loose)
{
enum ofpraw raw;
struct ofpbuf b;
pm->config = ntohl(opm->config) & OFPPC11_ALL;
pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
+ } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
+ const struct ofp14_port_mod *opm = ofpbuf_pull(&b, sizeof *opm);
+ enum ofperr error;
+
+ memset(pm, 0, sizeof *pm);
+
+ error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ if (error) {
+ return error;
+ }
+
+ memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+ pm->config = ntohl(opm->config) & OFPPC11_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+
+ while (ofpbuf_size(&b) > 0) {
+ struct ofpbuf property;
+ enum ofperr error;
+ uint16_t type;
+
+ error = ofputil_pull_property(&b, &property, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPPMPT14_ETHERNET:
+ error = parse_port_mod_ethernet_property(&property, pm);
+ break;
+
+ default:
+ log_property(loose, "unknown port_mod property %"PRIu16, type);
+ if (loose) {
+ error = 0;
+ } else if (type == OFPPMPT14_EXPERIMENTER) {
+ error = OFPERR_OFPBPC_BAD_EXPERIMENTER;
+ } else {
+ error = OFPERR_OFPBRC_BAD_TYPE;
+ }
+ break;
+ }
+
+ if (error) {
+ return error;
+ }
+ }
} else {
return OFPERR_OFPBRC_BAD_TYPE;
}
break;
}
case OFP14_VERSION:
- OVS_NOT_REACHED();
+ case OFP15_VERSION: {
+ struct ofp14_port_mod_prop_ethernet *eth;
+ struct ofp14_port_mod *opm;
+
+ b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, sizeof *eth);
+ opm = ofpbuf_put_zeros(b, sizeof *opm);
+ opm->port_no = ofputil_port_to_ofp11(pm->port_no);
+ memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
+ opm->config = htonl(pm->config & OFPPC11_ALL);
+ opm->mask = htonl(pm->mask & OFPPC11_ALL);
+
+ if (pm->advertise) {
+ eth = ofpbuf_put_zeros(b, sizeof *eth);
+ eth->type = htons(OFPPMPT14_ETHERNET);
+ eth->length = htons(sizeof *eth);
+ eth->advertise = netdev_port_features_to_ofp11(pm->advertise);
+ }
break;
+ }
default:
OVS_NOT_REACHED();
}
return b;
}
-struct ofp_prop_header {
- ovs_be16 type;
- ovs_be16 len;
-};
-
static enum ofperr
-ofputil_pull_property(struct ofpbuf *msg, struct ofpbuf *payload,
- uint16_t *typep)
-{
- struct ofp_prop_header *oph;
- unsigned int len;
-
- if (ofpbuf_size(msg) < sizeof *oph) {
- return OFPERR_OFPTFFC_BAD_LEN;
- }
-
- oph = ofpbuf_data(msg);
- len = ntohs(oph->len);
- if (len < sizeof *oph || ROUND_UP(len, 8) > ofpbuf_size(msg)) {
- return OFPERR_OFPTFFC_BAD_LEN;
- }
-
- *typep = ntohs(oph->type);
- if (payload) {
- ofpbuf_use_const(payload, ofpbuf_data(msg), len);
- ofpbuf_pull(payload, sizeof *oph);
- }
- ofpbuf_pull(msg, ROUND_UP(len, 8));
- return 0;
-}
-
-static void PRINTF_FORMAT(2, 3)
-log_property(bool loose, const char *message, ...)
+pull_table_feature_property(struct ofpbuf *msg, struct ofpbuf *payload,
+ uint16_t *typep)
{
- enum vlog_level level = loose ? VLL_DBG : VLL_WARN;
- if (!vlog_should_drop(THIS_MODULE, level, &bad_ofmsg_rl)) {
- va_list args;
+ enum ofperr error;
- va_start(args, message);
- vlog_valist(THIS_MODULE, level, message, args);
- va_end(args);
+ error = ofputil_pull_property(msg, payload, typep);
+ if (payload && !error) {
+ ofpbuf_pull(payload, sizeof(struct ofp_prop_header));
}
+ return error;
}
static enum ofperr
*ids = 0;
while (ofpbuf_size(payload) > 0) {
- enum ofperr error = ofputil_pull_property(payload, NULL, &type);
+ enum ofperr error = pull_table_feature_property(payload, NULL, &type);
if (error) {
return error;
}
enum ofperr error;
uint16_t ofpit;
- error = ofputil_pull_property(payload, NULL, &ofpit);
+ error = pull_table_feature_property(payload, NULL, &ofpit);
if (error) {
return error;
}
for (i = 0; i < ofpbuf_size(payload); i++) {
uint8_t id = ((const uint8_t *) ofpbuf_data(payload))[i];
if (id >= 255) {
- return OFPERR_OFPTFFC_BAD_ARGUMENT;
+ return OFPERR_OFPBPC_BAD_VALUE;
}
bitmap_set1(next_tables, id);
}
oxmp = ofpbuf_try_pull(b, sizeof *oxmp);
if (!oxmp) {
- return OFPERR_OFPTFFC_BAD_LEN;
+ return OFPERR_OFPBPC_BAD_LEN;
}
oxm = ntohl(*oxmp);
*hasmask = NXM_HASMASK(oxm);
if (*hasmask) {
if (NXM_LENGTH(oxm) & 1) {
- return OFPERR_OFPTFFC_BAD_ARGUMENT;
+ return OFPERR_OFPBPC_BAD_VALUE;
}
oxm = NXM_HEADER(NXM_VENDOR(oxm), NXM_FIELD(oxm), NXM_LENGTH(oxm) / 2);
}
}
if (ofpbuf_size(msg) < sizeof *otf) {
- return OFPERR_OFPTFFC_BAD_LEN;
+ return OFPERR_OFPBPC_BAD_LEN;
}
otf = ofpbuf_data(msg);
len = ntohs(otf->length);
if (len < sizeof *otf || len % 8 || len > ofpbuf_size(msg)) {
- return OFPERR_OFPTFFC_BAD_LEN;
+ return OFPERR_OFPBPC_BAD_LEN;
}
ofpbuf_pull(msg, sizeof *otf);
enum ofperr error;
uint16_t type;
- error = ofputil_pull_property(msg, &payload, &type);
+ error = pull_table_feature_property(msg, &payload, &type);
if (error) {
return error;
}
case OFPTFPT13_EXPERIMENTER:
case OFPTFPT13_EXPERIMENTER_MISS:
- log_property(loose,
- "unknown table features experimenter property");
- error = loose ? 0 : OFPERR_OFPTFFC_BAD_TYPE;
+ default:
+ log_property(loose, "unknown table features property %"PRIu16,
+ type);
+ error = loose ? 0 : OFPERR_OFPBPC_BAD_TYPE;
break;
}
if (error) {
"(\'-O OpenFlow13\')");
case OFP13_VERSION:
case OFP14_VERSION:
+ case OFP15_VERSION:
request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
ofp_version, 0);
break;
pm->table_id = otm->table_id;
pm->config = ntohl(otm->config);
+ } else if (raw == OFPRAW_OFPT14_TABLE_MOD) {
+ const struct ofp14_table_mod *otm = ofpbuf_pull(&b, sizeof *otm);
+
+ pm->table_id = otm->table_id;
+ pm->config = ntohl(otm->config);
+ /* We do not understand any properties yet, so we do not bother
+ * parsing them. */
} else {
return OFPERR_OFPBRC_BAD_TYPE;
}
break;
}
case OFP14_VERSION:
- OVS_NOT_REACHED();
+ case OFP15_VERSION: {
+ struct ofp14_table_mod *otm;
+
+ b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
+ otm = ofpbuf_put_zeros(b, sizeof *otm);
+ otm->table_id = pm->table_id;
+ otm->config = htonl(pm->config);
break;
+ }
default:
OVS_NOT_REACHED();
}
case OFP13_VERSION:
case OFP14_VERSION:
+ case OFP15_VERSION:
ofputil_put_ofp13_table_stats(&stats[i], reply);
break;
| NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
flags);
- return OFPERR_NXBRC_FM_BAD_FLAGS;
+ return OFPERR_OFPMOFC_BAD_FLAGS;
}
if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
- case OFP14_VERSION:{
+ case OFP14_VERSION:
+ case OFP15_VERSION: {
struct ofp11_packet_out *opo;
size_t len;
enum ofpraw type;
switch (ofp_version) {
+ case OFP15_VERSION:
case OFP14_VERSION:
case OFP13_VERSION:
case OFP12_VERSION:
ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
struct ofputil_phy_port *pp)
{
+ memset(pp, 0, sizeof *pp);
+
switch (ofp_version) {
case OFP10_VERSION: {
const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
}
case OFP14_VERSION:
- OVS_NOT_REACHED();
- break;
+ case OFP15_VERSION:
+ return ofpbuf_size(b) ? ofputil_pull_ofp14_port(pp, b) : EOF;
default:
OVS_NOT_REACHED();
}
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
- case OFP14_VERSION:{
+ case OFP14_VERSION:
+ case OFP15_VERSION: {
struct ofp11_port_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
ps13->duration_nsec = htonl(ops->duration_nsec);
}
+static void
+ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
+ struct list *replies)
+{
+ struct ofp14_port_stats_prop_ethernet *eth;
+ struct ofp14_port_stats *ps14;
+ struct ofpbuf *reply;
+
+ reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth);
+
+ ps14 = ofpbuf_put_uninit(reply, sizeof *ps14);
+ ps14->length = htons(sizeof *ps14 + sizeof *eth);
+ memset(ps14->pad, 0, sizeof ps14->pad);
+ ps14->port_no = ofputil_port_to_ofp11(ops->port_no);
+ ps14->duration_sec = htonl(ops->duration_sec);
+ ps14->duration_nsec = htonl(ops->duration_nsec);
+ ps14->rx_packets = htonll(ops->stats.rx_packets);
+ ps14->tx_packets = htonll(ops->stats.tx_packets);
+ ps14->rx_bytes = htonll(ops->stats.rx_bytes);
+ ps14->tx_bytes = htonll(ops->stats.tx_bytes);
+ ps14->rx_dropped = htonll(ops->stats.rx_dropped);
+ ps14->tx_dropped = htonll(ops->stats.tx_dropped);
+ ps14->rx_errors = htonll(ops->stats.rx_errors);
+ ps14->tx_errors = htonll(ops->stats.tx_errors);
+
+ eth = ofpbuf_put_uninit(reply, sizeof *eth);
+ eth->type = htons(OFPPSPT14_ETHERNET);
+ eth->length = htons(sizeof *eth);
+ memset(eth->pad, 0, sizeof eth->pad);
+ eth->rx_frame_err = htonll(ops->stats.rx_frame_errors);
+ eth->rx_over_err = htonll(ops->stats.rx_over_errors);
+ eth->rx_crc_err = htonll(ops->stats.rx_crc_errors);
+ eth->collisions = htonll(ops->stats.collisions);
+}
/* Encode a ports stat for 'ops' and append it to 'replies'. */
void
}
case OFP14_VERSION:
- OVS_NOT_REACHED();
+ case OFP15_VERSION:
+ ofputil_append_ofp14_port_stats(ops, replies);
break;
default:
return error;
}
-static size_t
-ofputil_get_port_stats_size(enum ofp_version ofp_version)
+static enum ofperr
+parse_ofp14_port_stats_ethernet_property(const struct ofpbuf *payload,
+ struct ofputil_port_stats *ops)
{
- switch (ofp_version) {
- case OFP10_VERSION:
- return sizeof(struct ofp10_port_stats);
- case OFP11_VERSION:
- case OFP12_VERSION:
- return sizeof(struct ofp11_port_stats);
- case OFP13_VERSION:
- return sizeof(struct ofp13_port_stats);
- case OFP14_VERSION:
- OVS_NOT_REACHED();
- return 0;
- default:
- OVS_NOT_REACHED();
+ const struct ofp14_port_stats_prop_ethernet *eth = ofpbuf_data(payload);
+
+ if (ofpbuf_size(payload) != sizeof *eth) {
+ return OFPERR_OFPBPC_BAD_LEN;
}
+
+ ops->stats.rx_frame_errors = ntohll(eth->rx_frame_err);
+ ops->stats.rx_over_errors = ntohll(eth->rx_over_err);
+ ops->stats.rx_crc_errors = ntohll(eth->rx_crc_err);
+ ops->stats.collisions = ntohll(eth->collisions);
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
+ struct ofpbuf *msg)
+{
+ const struct ofp14_port_stats *ps14;
+ struct ofpbuf properties;
+ enum ofperr error;
+ size_t len;
+
+ ps14 = ofpbuf_try_pull(msg, sizeof *ps14);
+ if (!ps14) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ len = ntohs(ps14->length);
+ if (len < sizeof *ps14 || len - sizeof *ps14 > ofpbuf_size(msg)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ len -= sizeof *ps14;
+ ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
+
+ error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no);
+ if (error) {
+ return error;
+ }
+
+ ops->duration_sec = ntohl(ps14->duration_sec);
+ ops->duration_nsec = ntohl(ps14->duration_nsec);
+ ops->stats.rx_packets = ntohll(ps14->rx_packets);
+ ops->stats.tx_packets = ntohll(ps14->tx_packets);
+ ops->stats.rx_bytes = ntohll(ps14->rx_bytes);
+ ops->stats.tx_bytes = ntohll(ps14->tx_bytes);
+ ops->stats.rx_dropped = ntohll(ps14->rx_dropped);
+ ops->stats.tx_dropped = ntohll(ps14->tx_dropped);
+ ops->stats.rx_errors = ntohll(ps14->rx_errors);
+ ops->stats.tx_errors = ntohll(ps14->tx_errors);
+ ops->stats.rx_frame_errors = UINT64_MAX;
+ ops->stats.rx_over_errors = UINT64_MAX;
+ ops->stats.rx_crc_errors = UINT64_MAX;
+ ops->stats.collisions = UINT64_MAX;
+
+ while (ofpbuf_size(&properties) > 0) {
+ struct ofpbuf payload;
+ enum ofperr error;
+ uint16_t type;
+
+ error = ofputil_pull_property(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPPSPT14_ETHERNET:
+ error = parse_ofp14_port_stats_ethernet_property(&payload, ops);
+ break;
+
+ default:
+ log_property(true, "unknown port stats property %"PRIu16, type);
+ error = 0;
+ break;
+ }
+
+ if (error) {
+ return error;
+ }
+ }
+
+ return 0;
}
/* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY
size_t
ofputil_count_port_stats(const struct ofp_header *oh)
{
+ struct ofputil_port_stats ps;
struct ofpbuf b;
+ size_t n = 0;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
ofpraw_pull_assert(&b);
-
- return ofpbuf_size(&b) / ofputil_get_port_stats_size(oh->version);
+ while (!ofputil_decode_port_stats(&ps, &b)) {
+ n++;
+ }
+ return n;
}
/* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract
if (!ofpbuf_size(msg)) {
return EOF;
+ } else if (raw == OFPRAW_OFPST14_PORT_REPLY) {
+ return ofputil_pull_ofp14_port_stats(ps, msg);
} else if (raw == OFPRAW_OFPST13_PORT_REPLY) {
const struct ofp13_port_stats *ps13;
ofp_port_t *ofp10_port)
{
switch ((enum ofp_version)request->version) {
+ case OFP15_VERSION:
+ case OFP14_VERSION:
case OFP13_VERSION:
case OFP12_VERSION:
case OFP11_VERSION: {
return 0;
}
- case OFP14_VERSION:
- OVS_NOT_REACHED();
- break;
-
default:
OVS_NOT_REACHED();
}
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
- case OFP14_VERSION: {
+ case OFP14_VERSION:
+ case OFP15_VERSION: {
struct ofp11_group_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
return request;
}
+/* Decodes the OpenFlow group description request in 'oh', returning the group
+ * whose description is requested, or OFPG_ALL if stats for all groups was
+ * requested. */
+uint32_t
+ofputil_decode_group_desc_request(const struct ofp_header *oh)
+{
+ struct ofpbuf request;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(&request, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&request);
+ if (raw == OFPRAW_OFPST11_GROUP_DESC_REQUEST) {
+ return OFPG_ALL;
+ } else if (raw == OFPRAW_OFPST15_GROUP_DESC_REQUEST) {
+ ovs_be32 *group_id = ofpbuf_pull(&request, sizeof *group_id);
+ return ntohl(*group_id);
+ } else {
+ OVS_NOT_REACHED();
+ }
+}
+
/* Returns an OpenFlow group description request for OpenFlow version
- * 'ofp_version', that requests stats for group 'group_id'. (Use OFPG_ALL to
- * request stats for all groups.)
+ * 'ofp_version', that requests stats for group 'group_id'. Use OFPG_ALL to
+ * request stats for all groups (OpenFlow 1.4 and earlier always request all
+ * groups).
*
* Group descriptions include the bucket and action configuration for each
* group. */
struct ofpbuf *
-ofputil_encode_group_desc_request(enum ofp_version ofp_version)
+ofputil_encode_group_desc_request(enum ofp_version ofp_version,
+ uint32_t group_id)
{
struct ofpbuf *request;
+ ovs_be32 gid;
switch (ofp_version) {
case OFP10_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
- request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST, ofp_version, 0);
+ request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST,
+ ofp_version, 0);
+ break;
+ case OFP15_VERSION:
+ request = ofpraw_alloc(OFPRAW_OFPST15_GROUP_DESC_REQUEST,
+ ofp_version, 0);
+ gid = htonl(group_id);
+ ofpbuf_put(request, &gid, sizeof gid);
break;
default:
OVS_NOT_REACHED();
}
case OFP13_VERSION:
- case OFP14_VERSION:{
+ case OFP14_VERSION:
+ case OFP15_VERSION: {
struct ofp13_group_stats *gs13;
length = sizeof *gs13 + bucket_counter_size;
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
+ case OFP15_VERSION:
request = ofpraw_alloc(OFPRAW_OFPST12_GROUP_FEATURES_REQUEST,
ofp_version, 0);
break;
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
+ case OFP15_VERSION:
b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0);
start_ogm = ofpbuf_size(b);
ofpbuf_put_zeros(b, sizeof *ogm);
struct ofputil_queue_stats_request *oqsr)
{
switch ((enum ofp_version)request->version) {
+ case OFP15_VERSION:
case OFP14_VERSION:
case OFP13_VERSION:
case OFP12_VERSION:
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
- case OFP14_VERSION: {
+ case OFP14_VERSION:
+ case OFP15_VERSION: {
struct ofp11_queue_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
return request;
}
-static size_t
-ofputil_get_queue_stats_size(enum ofp_version ofp_version)
-{
- switch (ofp_version) {
- case OFP10_VERSION:
- return sizeof(struct ofp10_queue_stats);
- case OFP11_VERSION:
- case OFP12_VERSION:
- return sizeof(struct ofp11_queue_stats);
- case OFP13_VERSION:
- return sizeof(struct ofp13_queue_stats);
- case OFP14_VERSION:
- OVS_NOT_REACHED();
- return 0;
- default:
- OVS_NOT_REACHED();
- }
-}
-
/* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY
* message 'oh'. */
size_t
ofputil_count_queue_stats(const struct ofp_header *oh)
{
+ struct ofputil_queue_stats qs;
struct ofpbuf b;
+ size_t n = 0;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
ofpraw_pull_assert(&b);
-
- return ofpbuf_size(&b) / ofputil_get_queue_stats_size(oh->version);
+ while (!ofputil_decode_queue_stats(&qs, &b)) {
+ n++;
+ }
+ return n;
}
static enum ofperr
return error;
}
+static enum ofperr
+ofputil_pull_ofp14_queue_stats(struct ofputil_queue_stats *oqs,
+ struct ofpbuf *msg)
+{
+ const struct ofp14_queue_stats *qs14;
+ size_t len;
+
+ qs14 = ofpbuf_try_pull(msg, sizeof *qs14);
+ if (!qs14) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ len = ntohs(qs14->length);
+ if (len < sizeof *qs14 || len - sizeof *qs14 > ofpbuf_size(msg)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ ofpbuf_pull(msg, len - sizeof *qs14);
+
+ /* No properties yet defined, so ignore them for now. */
+
+ return ofputil_queue_stats_from_ofp13(oqs, &qs14->qs);
+}
+
/* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract
* ofputil_queue_stats in 'qs'.
*
if (!ofpbuf_size(msg)) {
return EOF;
+ } else if (raw == OFPRAW_OFPST14_QUEUE_REPLY) {
+ return ofputil_pull_ofp14_queue_stats(qs, msg);
} else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) {
const struct ofp13_queue_stats *qs13;
}
}
+static void
+ofputil_queue_stats_to_ofp14(const struct ofputil_queue_stats *oqs,
+ struct ofp14_queue_stats *qs14)
+{
+ qs14->length = htons(sizeof *qs14);
+ memset(qs14->pad, 0, sizeof qs14->pad);
+ ofputil_queue_stats_to_ofp13(oqs, &qs14->qs);
+}
+
+
/* Encode a queue stat for 'oqs' and append it to 'replies'. */
void
ofputil_append_queue_stat(struct list *replies,
}
case OFP14_VERSION:
- OVS_NOT_REACHED();
+ case OFP15_VERSION: {
+ struct ofp14_queue_stats *reply = ofpmp_append(replies, sizeof *reply);
+ ofputil_queue_stats_to_ofp14(oqs, reply);
break;
+ }
default:
OVS_NOT_REACHED();