+ otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf);
+ otf->length = htons(reply->size - start_ofs);
+ ofpmp_postappend(replies, start_ofs);
+}
+
+static enum ofperr
+parse_table_desc_eviction_property(struct ofpbuf *property,
+ struct ofputil_table_desc *td)
+{
+ struct ofp14_table_mod_prop_eviction *ote = property->data;
+
+ if (property->size != sizeof *ote) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ td->eviction_flags = ntohl(ote->flags);
+ return 0;
+}
+
+/* Decodes the next OpenFlow "table desc" message (of possibly several) from
+ * 'msg' into an abstract form in '*td'. Returns 0 if successful, EOF if the
+ * last "table desc" in 'msg' was already decoded, otherwise an OFPERR_*
+ * value. */
+int
+ofputil_decode_table_desc(struct ofpbuf *msg,
+ struct ofputil_table_desc *td,
+ enum ofp_version version)
+{
+ struct ofp14_table_desc *otd;
+ struct ofpbuf properties;
+ size_t length;
+
+ memset(td, 0, sizeof *td);
+
+ if (!msg->header) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ otd = ofpbuf_try_pull(msg, sizeof *otd);
+ if (!otd) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply has %"PRIu32" "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ td->table_id = otd->table_id;
+ length = ntohs(otd->length);
+ if (length < sizeof *otd || length - sizeof *otd > msg->size) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply claims invalid "
+ "length %"PRIuSIZE, length);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ length -= sizeof *otd;
+ ofpbuf_use_const(&properties, ofpbuf_pull(msg, length), length);
+
+ td->eviction = ofputil_decode_table_eviction(otd->config, version);
+ td->eviction_flags = UINT32_MAX;
+
+ while (properties.size > 0) {
+ struct ofpbuf payload;
+ enum ofperr error;
+ uint16_t type;
+
+ error = ofputil_pull_property(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPTMPT14_EVICTION:
+ error = parse_table_desc_eviction_property(&payload, td);
+ break;
+
+ default:
+ log_property(true, "unknown table_desc property %"PRIu16, type);
+ error = 0;
+ break;
+ }
+
+ if (error) {
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+/* Encodes and returns a request to obtain description of tables of a switch.
+ * The message is encoded for OpenFlow version 'ofp_version'. */
+struct ofpbuf *
+ofputil_encode_table_desc_request(enum ofp_version ofp_version)
+{
+ struct ofpbuf *request = NULL;
+
+ if (ofp_version >= OFP14_VERSION) {
+ request = ofpraw_alloc(OFPRAW_OFPST14_TABLE_DESC_REQUEST,
+ ofp_version, 0);
+ } else {
+ ovs_fatal(0, "dump-table-desc needs OpenFlow 1.4 or later "
+ "(\'-O OpenFlow14\')");
+ }
+
+ return request;
+}
+
+/* Function to append Table desc information in a reply list. */
+void
+ofputil_append_table_desc_reply(const struct ofputil_table_desc *td,
+ struct ovs_list *replies,
+ enum ofp_version version)
+{
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ size_t start_otd;
+ struct ofp14_table_desc *otd;
+
+ start_otd = reply->size;
+ ofpbuf_put_zeros(reply, sizeof *otd);
+ if (td->eviction_flags != UINT32_MAX) {
+ struct ofp14_table_mod_prop_eviction *ote;
+
+ ote = ofpbuf_put_zeros(reply, sizeof *ote);
+ ote->type = htons(OFPTMPT14_EVICTION);
+ ote->length = htons(sizeof *ote);
+ ote->flags = htonl(td->eviction_flags);
+ }
+
+ otd = ofpbuf_at_assert(reply, start_otd, sizeof *otd);
+ otd->length = htons(reply->size - start_otd);
+ otd->table_id = td->table_id;
+ otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT,
+ td->eviction, version);
+ ofpmp_postappend(replies, start_otd);
+}
+
+static enum ofperr
+parse_table_mod_eviction_property(struct ofpbuf *property,
+ struct ofputil_table_mod *tm)
+{
+ struct ofp14_table_mod_prop_eviction *ote = property->data;
+
+ if (property->size != sizeof *ote) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ tm->eviction_flags = ntohl(ote->flags);
+ return 0;
+}
+
+/* Given 'config', taken from an OpenFlow 'version' message that specifies
+ * table configuration (a table mod, table stats, or table features message),
+ * returns the table eviction configuration that it specifies.
+ *
+ * Only OpenFlow 1.4 and later specify table eviction configuration this way,
+ * so for other 'version' values this function always returns
+ * OFPUTIL_TABLE_EVICTION_DEFAULT. */
+static enum ofputil_table_eviction
+ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version)
+{
+ return (version < OFP14_VERSION ? OFPUTIL_TABLE_EVICTION_DEFAULT
+ : config & htonl(OFPTC14_EVICTION) ? OFPUTIL_TABLE_EVICTION_ON
+ : OFPUTIL_TABLE_EVICTION_OFF);
+}
+
+/* Returns a bitmap of OFPTC* values suitable for 'config' fields in various
+ * OpenFlow messages of the given 'version', based on the provided 'miss' and
+ * 'eviction' values. */
+static ovs_be32
+ofputil_encode_table_config(enum ofputil_table_miss miss,
+ enum ofputil_table_eviction eviction,
+ enum ofp_version version)
+{
+ /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more
+ * information on the crazy evolution of this field. */
+ switch (version) {
+ case OFP10_VERSION:
+ /* OpenFlow 1.0 didn't have such a field, any value ought to do. */
+ return htonl(0);
+
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ /* OpenFlow 1.1 and 1.2 define only OFPTC11_TABLE_MISS_*. */
+ switch (miss) {
+ case OFPUTIL_TABLE_MISS_DEFAULT:
+ /* Really this shouldn't be used for encoding (the caller should
+ * provide a specific value) but I can't imagine that defaulting to
+ * the fall-through case here will hurt. */
+ case OFPUTIL_TABLE_MISS_CONTROLLER:
+ default:
+ return htonl(OFPTC11_TABLE_MISS_CONTROLLER);
+ case OFPUTIL_TABLE_MISS_CONTINUE:
+ return htonl(OFPTC11_TABLE_MISS_CONTINUE);
+ case OFPUTIL_TABLE_MISS_DROP:
+ return htonl(OFPTC11_TABLE_MISS_DROP);
+ }
+ OVS_NOT_REACHED();
+
+ case OFP13_VERSION:
+ /* OpenFlow 1.3 removed OFPTC11_TABLE_MISS_* and didn't define any new
+ * flags, so this is correct. */
+ return htonl(0);
+
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ /* OpenFlow 1.4 introduced OFPTC14_EVICTION and OFPTC14_VACANCY_EVENTS
+ * and we don't support the latter yet. */
+ return htonl(eviction == OFPUTIL_TABLE_EVICTION_ON
+ ? OFPTC14_EVICTION : 0);
+ }
+
+ OVS_NOT_REACHED();
+}
+
+/* Given 'config', taken from an OpenFlow 'version' message that specifies
+ * table configuration (a table mod, table stats, or table features message),
+ * returns the table miss configuration that it specifies.
+ *
+ * Only OpenFlow 1.1 and 1.2 specify table miss configurations this way, so for
+ * other 'version' values this function always returns
+ * OFPUTIL_TABLE_MISS_DEFAULT. */
+static enum ofputil_table_miss
+ofputil_decode_table_miss(ovs_be32 config_, enum ofp_version version)
+{
+ uint32_t config = ntohl(config_);
+
+ if (version == OFP11_VERSION || version == OFP12_VERSION) {
+ switch (config & OFPTC11_TABLE_MISS_MASK) {
+ case OFPTC11_TABLE_MISS_CONTROLLER:
+ return OFPUTIL_TABLE_MISS_CONTROLLER;
+
+ case OFPTC11_TABLE_MISS_CONTINUE:
+ return OFPUTIL_TABLE_MISS_CONTINUE;
+
+ case OFPTC11_TABLE_MISS_DROP:
+ return OFPUTIL_TABLE_MISS_DROP;
+
+ default:
+ VLOG_WARN_RL(&bad_ofmsg_rl, "bad table miss config %d", config);
+ return OFPUTIL_TABLE_MISS_CONTROLLER;
+ }
+ } else {
+ return OFPUTIL_TABLE_MISS_DEFAULT;
+ }
+}
+
+/* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
+ * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_table_mod(const struct ofp_header *oh,
+ struct ofputil_table_mod *pm)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ memset(pm, 0, sizeof *pm);
+ pm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
+ pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
+ pm->eviction_flags = UINT32_MAX;
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT11_TABLE_MOD) {
+ const struct ofp11_table_mod *otm = b.data;
+
+ pm->table_id = otm->table_id;
+ pm->miss = ofputil_decode_table_miss(otm->config, oh->version);
+ } 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->miss = ofputil_decode_table_miss(otm->config, oh->version);
+ pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version);
+ while (b.size > 0) {
+ struct ofpbuf property;
+ enum ofperr error;
+ uint16_t type;
+
+ error = ofputil_pull_property(&b, &property, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPTMPT14_EVICTION:
+ error = parse_table_mod_eviction_property(&property, pm);
+ break;
+
+ default:
+ error = OFPERR_OFPBRC_BAD_TYPE;
+ break;
+ }
+
+ if (error) {
+ return error;
+ }
+ }
+ } else {
+ return OFPERR_OFPBRC_BAD_TYPE;
+ }
+
+ return 0;
+}
+
+/* Converts the abstract form of a "table mod" message in '*tm' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in a
+ * buffer owned by the caller. */
+struct ofpbuf *
+ofputil_encode_table_mod(const struct ofputil_table_mod *tm,
+ enum ofputil_protocol protocol)
+{
+ enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+ struct ofpbuf *b;
+
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ ovs_fatal(0, "table mod needs OpenFlow 1.1 or later "
+ "(\'-O OpenFlow11\')");
+ break;
+ }
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ case OFP13_VERSION: {
+ struct ofp11_table_mod *otm;
+
+ b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0);
+ otm = ofpbuf_put_zeros(b, sizeof *otm);
+ otm->table_id = tm->table_id;
+ otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+ ofp_version);
+ break;
+ }
+ case OFP14_VERSION:
+ case OFP15_VERSION: {
+ struct ofp14_table_mod *otm;
+ struct ofp14_table_mod_prop_eviction *ote;
+
+ b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
+ otm = ofpbuf_put_zeros(b, sizeof *otm);
+ otm->table_id = tm->table_id;
+ otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+ ofp_version);
+
+ if (tm->eviction_flags != UINT32_MAX) {
+ ote = ofpbuf_put_zeros(b, sizeof *ote);
+ ote->type = htons(OFPTMPT14_EVICTION);
+ ote->length = htons(sizeof *ote);
+ ote->flags = htonl(tm->eviction_flags);
+ }
+ break;
+ }
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ return b;
+}
+\f
+/* ofputil_role_request */
+
+/* Decodes the OpenFlow "role request" or "role reply" message in '*oh' into
+ * an abstract form in '*rr'. Returns 0 if successful, otherwise an
+ * OFPERR_* value. */
+enum ofperr
+ofputil_decode_role_message(const struct ofp_header *oh,
+ struct ofputil_role_request *rr)
+{
+ struct ofpbuf b;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT12_ROLE_REQUEST ||
+ raw == OFPRAW_OFPT12_ROLE_REPLY) {
+ const struct ofp12_role_request *orr = b.msg;
+
+ if (orr->role != htonl(OFPCR12_ROLE_NOCHANGE) &&
+ orr->role != htonl(OFPCR12_ROLE_EQUAL) &&
+ orr->role != htonl(OFPCR12_ROLE_MASTER) &&
+ orr->role != htonl(OFPCR12_ROLE_SLAVE)) {
+ return OFPERR_OFPRRFC_BAD_ROLE;
+ }
+
+ rr->role = ntohl(orr->role);
+ if (raw == OFPRAW_OFPT12_ROLE_REQUEST
+ ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE)
+ : orr->generation_id == OVS_BE64_MAX) {
+ rr->have_generation_id = false;
+ rr->generation_id = 0;
+ } else {
+ rr->have_generation_id = true;
+ rr->generation_id = ntohll(orr->generation_id);
+ }
+ } else if (raw == OFPRAW_NXT_ROLE_REQUEST ||
+ raw == OFPRAW_NXT_ROLE_REPLY) {
+ const struct nx_role_request *nrr = b.msg;
+
+ BUILD_ASSERT(NX_ROLE_OTHER + 1 == OFPCR12_ROLE_EQUAL);
+ BUILD_ASSERT(NX_ROLE_MASTER + 1 == OFPCR12_ROLE_MASTER);
+ BUILD_ASSERT(NX_ROLE_SLAVE + 1 == OFPCR12_ROLE_SLAVE);
+
+ if (nrr->role != htonl(NX_ROLE_OTHER) &&
+ nrr->role != htonl(NX_ROLE_MASTER) &&
+ nrr->role != htonl(NX_ROLE_SLAVE)) {
+ return OFPERR_OFPRRFC_BAD_ROLE;
+ }
+
+ rr->role = ntohl(nrr->role) + 1;
+ rr->have_generation_id = false;
+ rr->generation_id = 0;
+ } else {
+ OVS_NOT_REACHED();