summaryrefslogtreecommitdiff
path: root/lib/ofp-table.c
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2018-02-09 10:04:26 -0800
committerBen Pfaff <blp@ovn.org>2018-02-13 10:43:13 -0800
commit0d71302e36c4ad290cb3b36363b48756fb438c59 (patch)
tree2b2095496fb8d2c94d891e57047deea4c9f4712a /lib/ofp-table.c
parent31292693fc3a845a5f40102fcbf500cfe3167290 (diff)
downloadopenvswitch-0d71302e36c4ad290cb3b36363b48756fb438c59.tar.gz
ofp-util, ofp-parse: Break up into many separate modules.
ofp-util had been far too large and monolithic for a long time. This commit breaks it up into units that make some logical sense. It also moves the pieces of ofp-parse that were specific to each unit into the relevant unit. Most of this commit is just moving code around. Signed-off-by: Ben Pfaff <blp@ovn.org> Reviewed-by: Yifeng Sun <pkusunyifeng@gmail.com>
Diffstat (limited to 'lib/ofp-table.c')
-rw-r--r--lib/ofp-table.c1613
1 files changed, 1613 insertions, 0 deletions
diff --git a/lib/ofp-table.c b/lib/ofp-table.c
new file mode 100644
index 000000000..558e4bcd9
--- /dev/null
+++ b/lib/ofp-table.c
@@ -0,0 +1,1613 @@
+/*
+ * Copyright (c) 2008-2017 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "openvswitch/ofp-table.h"
+#include "bitmap.h"
+#include "nx-match.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/json.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-msgs.h"
+#include "openvswitch/ofp-prop.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
+#include "util.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_table);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss,
+ enum ofputil_table_eviction,
+ enum ofputil_table_vacancy,
+ enum ofp_version);
+static enum ofputil_table_vacancy ofputil_decode_table_vacancy(
+ ovs_be32 config, enum ofp_version);
+static enum ofputil_table_eviction ofputil_decode_table_eviction(
+ ovs_be32 config, enum ofp_version);
+
+/* ofputil_table_map. */
+
+void
+ofputil_table_map_init(struct ofputil_table_map *map)
+{
+ namemap_init(&map->map);
+}
+
+void
+ofputil_table_map_put(struct ofputil_table_map *map,
+ uint8_t table_id, const char *name)
+{
+ namemap_put(&map->map, table_id, name);
+}
+
+const char *
+ofputil_table_map_get_name(const struct ofputil_table_map *map,
+ uint8_t table_id)
+{
+ struct namemap_node *node
+ = map ? namemap_find_by_number(&map->map, table_id) : NULL;
+ return node && !node->duplicate ? node->name : NULL;
+}
+
+uint8_t
+ofputil_table_map_get_number(const struct ofputil_table_map *map,
+ const char *name)
+{
+ struct namemap_node *node
+ = map ? namemap_find_by_name(&map->map, name) : NULL;
+ return node && !node->duplicate ? node->number : UINT8_MAX;
+}
+
+void
+ofputil_table_map_destroy(struct ofputil_table_map *map)
+{
+ namemap_destroy(&map->map);
+}
+
+/* Table numbers. */
+
+/* Stores the table number represented by 's' into '*tablep'. 's' may be an
+ * integer or, if 'table_map' is nonnull, a name (quoted or unquoted).
+ *
+ * Returns true if successful, false if 's' is not a valid OpenFlow table
+ * number or name. The caller should issue an error message in this case,
+ * because this function usually does not. (This gives the caller an
+ * opportunity to look up the table name another way, e.g. by contacting the
+ * switch and listing the names of all its tables). */
+bool
+ofputil_table_from_string(const char *s,
+ const struct ofputil_table_map *table_map,
+ uint8_t *tablep)
+{
+ *tablep = 0;
+ if (*s == '-') {
+ VLOG_WARN("Negative value %s is not a valid table number.", s);
+ return false;
+ }
+
+ unsigned int table;
+ if (str_to_uint(s, 10, &table)) {
+ if (table > 255) {
+ VLOG_WARN("table %u is outside the supported range 0 through 255",
+ table);
+ return false;
+ }
+ *tablep = table;
+ return true;
+ } else {
+ if (s[0] != '"') {
+ table = ofputil_table_map_get_number(table_map, s);
+ } else {
+ size_t length = strlen(s);
+ char *name = NULL;
+ if (length > 1
+ && s[length - 1] == '"'
+ && json_string_unescape(s + 1, length - 2, &name)) {
+ table = ofputil_table_map_get_number(table_map, name);
+ }
+ free(name);
+ }
+ if (table != UINT8_MAX) {
+ *tablep = table;
+ return true;
+ }
+
+ return false;
+ }
+}
+
+/* Appends to 's' a string representation of the OpenFlow table number 'table',
+ * either the table number or a name drawn from 'table_map'. */
+void
+ofputil_format_table(uint8_t table, const struct ofputil_table_map *table_map,
+ struct ds *s)
+{
+ const char *table_name = ofputil_table_map_get_name(table_map, table);
+ if (table_name) {
+ namemap_put_name(table_name, s);
+ } else {
+ ds_put_format(s, "%"PRIu8, table);
+ }
+}
+
+/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
+ * representation of OpenFlow table number 'table', either the table's number
+ * or a name drawn from 'table_map'. */
+void
+ofputil_table_to_string(uint8_t table,
+ const struct ofputil_table_map *table_map,
+ char *namebuf, size_t bufsize)
+{
+ const char *table_name = ofputil_table_map_get_name(table_map, table);
+ if (table_name) {
+ struct ds s = DS_EMPTY_INITIALIZER;
+ namemap_put_name(table_name, &s);
+ ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
+ ds_destroy(&s);
+ return;
+ }
+
+ snprintf(namebuf, bufsize, "%"PRIu8, table);
+}
+
+/* Table features. */
+
+static enum ofperr
+pull_table_feature_property(struct ofpbuf *msg, struct ofpbuf *payload,
+ uint64_t *typep)
+{
+ enum ofperr error;
+
+ error = ofpprop_pull(msg, payload, typep);
+ if (payload && !error) {
+ ofpbuf_pull(payload, (char *)payload->msg - (char *)payload->header);
+ }
+ return error;
+}
+
+static enum ofperr
+parse_action_bitmap(struct ofpbuf *payload, enum ofp_version ofp_version,
+ uint64_t *ofpacts)
+{
+ uint32_t types = 0;
+
+ while (payload->size > 0) {
+ enum ofperr error;
+ uint64_t type;
+
+ error = ofpprop_pull__(payload, NULL, 1, 0x10000, &type);
+ if (error) {
+ return error;
+ }
+ if (type < CHAR_BIT * sizeof types) {
+ types |= 1u << type;
+ }
+ }
+
+ *ofpacts = ofpact_bitmap_from_openflow(htonl(types), ofp_version);
+ return 0;
+}
+
+static enum ofperr
+parse_instruction_ids(struct ofpbuf *payload, bool loose, uint32_t *insts)
+{
+ *insts = 0;
+ while (payload->size > 0) {
+ enum ovs_instruction_type inst;
+ enum ofperr error;
+ uint64_t ofpit;
+
+ /* OF1.3 and OF1.4 aren't clear about padding in the instruction IDs.
+ * It seems clear that they aren't padded to 8 bytes, though, because
+ * both standards say that "non-experimenter instructions are 4 bytes"
+ * and do not mention any padding before the first instruction ID.
+ * (There wouldn't be any point in padding to 8 bytes if the IDs were
+ * aligned on an odd 4-byte boundary.)
+ *
+ * Anyway, we just assume they're all glommed together on byte
+ * boundaries. */
+ error = ofpprop_pull__(payload, NULL, 1, 0x10000, &ofpit);
+ if (error) {
+ return error;
+ }
+
+ error = ovs_instruction_type_from_inst_type(&inst, ofpit);
+ if (!error) {
+ *insts |= 1u << inst;
+ } else if (!loose) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+static enum ofperr
+parse_table_features_next_table(struct ofpbuf *payload,
+ unsigned long int *next_tables)
+{
+ size_t i;
+
+ memset(next_tables, 0, bitmap_n_bytes(255));
+ for (i = 0; i < payload->size; i++) {
+ uint8_t id = ((const uint8_t *) payload->data)[i];
+ if (id >= 255) {
+ return OFPERR_OFPBPC_BAD_VALUE;
+ }
+ bitmap_set1(next_tables, id);
+ }
+ return 0;
+}
+
+static enum ofperr
+parse_oxms(struct ofpbuf *payload, bool loose,
+ struct mf_bitmap *exactp, struct mf_bitmap *maskedp)
+{
+ struct mf_bitmap exact = MF_BITMAP_INITIALIZER;
+ struct mf_bitmap masked = MF_BITMAP_INITIALIZER;
+
+ while (payload->size > 0) {
+ const struct mf_field *field;
+ enum ofperr error;
+ bool hasmask;
+
+ error = nx_pull_header(payload, NULL, &field, &hasmask);
+ if (!error) {
+ bitmap_set1(hasmask ? masked.bm : exact.bm, field->id);
+ } else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) {
+ return error;
+ }
+ }
+ if (exactp) {
+ *exactp = exact;
+ } else if (!bitmap_is_all_zeros(exact.bm, MFF_N_IDS)) {
+ return OFPERR_OFPBMC_BAD_MASK;
+ }
+ if (maskedp) {
+ *maskedp = masked;
+ } else if (!bitmap_is_all_zeros(masked.bm, MFF_N_IDS)) {
+ return OFPERR_OFPBMC_BAD_MASK;
+ }
+ return 0;
+}
+
+/* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract
+ * ofputil_table_features in 'tf'.
+ *
+ * If 'loose' is true, this function ignores properties and values that it does
+ * not understand, as a controller would want to do when interpreting
+ * capabilities provided by a switch. If 'loose' is false, this function
+ * treats unknown properties and values as an error, as a switch would want to
+ * do when interpreting a configuration request made by a controller.
+ *
+ * A single OpenFlow message can specify features for multiple tables. Calling
+ * this function multiple times for a single 'msg' iterates through the tables
+ * in the message. The caller must initially leave 'msg''s layer pointers null
+ * and not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise
+ * a positive "enum ofperr" value. */
+int
+ofputil_decode_table_features(struct ofpbuf *msg,
+ struct ofputil_table_features *tf, bool loose)
+{
+ memset(tf, 0, sizeof *tf);
+
+ if (!msg->header) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ const struct ofp_header *oh = msg->header;
+ struct ofp13_table_features *otf = msg->data;
+ if (msg->size < sizeof *otf) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ unsigned int len = ntohs(otf->length);
+ if (len < sizeof *otf || len % 8 || len > msg->size) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ tf->table_id = otf->table_id;
+ if (tf->table_id == OFPTT_ALL) {
+ return OFPERR_OFPTFFC_BAD_TABLE;
+ }
+
+ ovs_strlcpy_arrays(tf->name, otf->name);
+ tf->metadata_match = otf->metadata_match;
+ tf->metadata_write = otf->metadata_write;
+ tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
+ if (oh->version >= OFP14_VERSION) {
+ uint32_t caps = ntohl(otf->capabilities);
+ tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0;
+ tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0;
+ } else {
+ tf->supports_eviction = -1;
+ tf->supports_vacancy_events = -1;
+ }
+ tf->max_entries = ntohl(otf->max_entries);
+
+ struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
+ len);
+ ofpbuf_pull(&properties, sizeof *otf);
+ while (properties.size > 0) {
+ struct ofpbuf payload;
+ enum ofperr error;
+ uint64_t type;
+
+ error = pull_table_feature_property(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch ((enum ofp13_table_feature_prop_type) type) {
+ case OFPTFPT13_INSTRUCTIONS:
+ error = parse_instruction_ids(&payload, loose,
+ &tf->nonmiss.instructions);
+ break;
+ case OFPTFPT13_INSTRUCTIONS_MISS:
+ error = parse_instruction_ids(&payload, loose,
+ &tf->miss.instructions);
+ break;
+
+ case OFPTFPT13_NEXT_TABLES:
+ error = parse_table_features_next_table(&payload,
+ tf->nonmiss.next);
+ break;
+ case OFPTFPT13_NEXT_TABLES_MISS:
+ error = parse_table_features_next_table(&payload, tf->miss.next);
+ break;
+
+ case OFPTFPT13_WRITE_ACTIONS:
+ error = parse_action_bitmap(&payload, oh->version,
+ &tf->nonmiss.write.ofpacts);
+ break;
+ case OFPTFPT13_WRITE_ACTIONS_MISS:
+ error = parse_action_bitmap(&payload, oh->version,
+ &tf->miss.write.ofpacts);
+ break;
+
+ case OFPTFPT13_APPLY_ACTIONS:
+ error = parse_action_bitmap(&payload, oh->version,
+ &tf->nonmiss.apply.ofpacts);
+ break;
+ case OFPTFPT13_APPLY_ACTIONS_MISS:
+ error = parse_action_bitmap(&payload, oh->version,
+ &tf->miss.apply.ofpacts);
+ break;
+
+ case OFPTFPT13_MATCH:
+ error = parse_oxms(&payload, loose, &tf->match, &tf->mask);
+ break;
+ case OFPTFPT13_WILDCARDS:
+ error = parse_oxms(&payload, loose, &tf->wildcard, NULL);
+ break;
+
+ case OFPTFPT13_WRITE_SETFIELD:
+ error = parse_oxms(&payload, loose,
+ &tf->nonmiss.write.set_fields, NULL);
+ break;
+ case OFPTFPT13_WRITE_SETFIELD_MISS:
+ error = parse_oxms(&payload, loose,
+ &tf->miss.write.set_fields, NULL);
+ break;
+ case OFPTFPT13_APPLY_SETFIELD:
+ error = parse_oxms(&payload, loose,
+ &tf->nonmiss.apply.set_fields, NULL);
+ break;
+ case OFPTFPT13_APPLY_SETFIELD_MISS:
+ error = parse_oxms(&payload, loose,
+ &tf->miss.apply.set_fields, NULL);
+ break;
+
+ case OFPTFPT13_EXPERIMENTER:
+ case OFPTFPT13_EXPERIMENTER_MISS:
+ default:
+ error = OFPPROP_UNKNOWN(loose, "table features", type);
+ break;
+ }
+ if (error) {
+ return error;
+ }
+ }
+
+ /* Fix inconsistencies:
+ *
+ * - Turn on 'match' bits that are set in 'mask', because maskable
+ * fields are matchable.
+ *
+ * - Turn on 'wildcard' bits that are set in 'mask', because a field
+ * that is arbitrarily maskable can be wildcarded entirely.
+ *
+ * - Turn off 'wildcard' bits that are not in 'match', because a field
+ * must be matchable for it to be meaningfully wildcarded. */
+ bitmap_or(tf->match.bm, tf->mask.bm, MFF_N_IDS);
+ bitmap_or(tf->wildcard.bm, tf->mask.bm, MFF_N_IDS);
+ bitmap_and(tf->wildcard.bm, tf->match.bm, MFF_N_IDS);
+
+ return 0;
+}
+
+/* Encodes and returns a request to obtain the table features of a switch.
+ * The message is encoded for OpenFlow version 'ofp_version'. */
+struct ofpbuf *
+ofputil_encode_table_features_request(enum ofp_version ofp_version)
+{
+ struct ofpbuf *request = NULL;
+
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ ovs_fatal(0, "dump-table-features needs OpenFlow 1.3 or later "
+ "(\'-O OpenFlow13\')");
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ case OFP16_VERSION:
+ request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
+ ofp_version, 0);
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ return request;
+}
+
+static void
+put_fields_property(struct ofpbuf *reply,
+ const struct mf_bitmap *fields,
+ const struct mf_bitmap *masks,
+ enum ofp13_table_feature_prop_type property,
+ enum ofp_version version)
+{
+ size_t start_ofs;
+ int field;
+
+ start_ofs = ofpprop_start(reply, property);
+ BITMAP_FOR_EACH_1 (field, MFF_N_IDS, fields->bm) {
+ nx_put_header(reply, field, version,
+ masks && bitmap_is_set(masks->bm, field));
+ }
+ ofpprop_end(reply, start_ofs);
+}
+
+static void
+put_table_action_features(struct ofpbuf *reply,
+ const struct ofputil_table_action_features *taf,
+ enum ofp13_table_feature_prop_type actions_type,
+ enum ofp13_table_feature_prop_type set_fields_type,
+ int miss_offset, enum ofp_version version)
+{
+ ofpprop_put_bitmap(reply, actions_type + miss_offset,
+ ntohl(ofpact_bitmap_to_openflow(taf->ofpacts,
+ version)));
+ put_fields_property(reply, &taf->set_fields, NULL,
+ set_fields_type + miss_offset, version);
+}
+
+static void
+put_table_instruction_features(
+ struct ofpbuf *reply, const struct ofputil_table_instruction_features *tif,
+ int miss_offset, enum ofp_version version)
+{
+ size_t start_ofs;
+ uint8_t table_id;
+
+ ofpprop_put_bitmap(reply, OFPTFPT13_INSTRUCTIONS + miss_offset,
+ ntohl(ovsinst_bitmap_to_openflow(tif->instructions,
+ version)));
+
+ start_ofs = ofpprop_start(reply, OFPTFPT13_NEXT_TABLES + miss_offset);
+ BITMAP_FOR_EACH_1 (table_id, 255, tif->next) {
+ ofpbuf_put(reply, &table_id, 1);
+ }
+ ofpprop_end(reply, start_ofs);
+
+ put_table_action_features(reply, &tif->write,
+ OFPTFPT13_WRITE_ACTIONS,
+ OFPTFPT13_WRITE_SETFIELD, miss_offset, version);
+ put_table_action_features(reply, &tif->apply,
+ OFPTFPT13_APPLY_ACTIONS,
+ OFPTFPT13_APPLY_SETFIELD, miss_offset, version);
+}
+
+void
+ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
+ struct ovs_list *replies)
+{
+ struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
+ enum ofp_version version = ofpmp_version(replies);
+ size_t start_ofs = reply->size;
+ struct ofp13_table_features *otf;
+
+ otf = ofpbuf_put_zeros(reply, sizeof *otf);
+ otf->table_id = tf->table_id;
+ ovs_strlcpy_arrays(otf->name, tf->name);
+ otf->metadata_match = tf->metadata_match;
+ otf->metadata_write = tf->metadata_write;
+ if (version >= OFP14_VERSION) {
+ if (tf->supports_eviction) {
+ otf->capabilities |= htonl(OFPTC14_EVICTION);
+ }
+ if (tf->supports_vacancy_events) {
+ otf->capabilities |= htonl(OFPTC14_VACANCY_EVENTS);
+ }
+ }
+ otf->max_entries = htonl(tf->max_entries);
+
+ put_table_instruction_features(reply, &tf->nonmiss, 0, version);
+ put_table_instruction_features(reply, &tf->miss, 1, version);
+
+ put_fields_property(reply, &tf->match, &tf->mask,
+ OFPTFPT13_MATCH, version);
+ put_fields_property(reply, &tf->wildcard, NULL,
+ OFPTFPT13_WILDCARDS, version);
+
+ 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_vacancy_property(struct ofpbuf *property,
+ struct ofputil_table_desc *td)
+{
+ struct ofp14_table_mod_prop_vacancy *otv = property->data;
+
+ if (property->size != sizeof *otv) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ td->table_vacancy.vacancy_down = otv->vacancy_down;
+ td->table_vacancy.vacancy_up = otv->vacancy_up;
+ td->table_vacancy.vacancy = otv->vacancy;
+ 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)
+{
+ memset(td, 0, sizeof *td);
+
+ if (!msg->header) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ struct ofp14_table_desc *otd = ofpbuf_try_pull(msg, sizeof *otd);
+ if (!otd) {
+ VLOG_WARN_RL(&rl, "OFP14_TABLE_DESC reply has %"PRIu32" "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ td->table_id = otd->table_id;
+ size_t length = ntohs(otd->length);
+ if (length < sizeof *otd || length - sizeof *otd > msg->size) {
+ VLOG_WARN_RL(&rl, "OFP14_TABLE_DESC reply claims invalid "
+ "length %"PRIuSIZE, length);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ length -= sizeof *otd;
+
+ td->eviction = ofputil_decode_table_eviction(otd->config, version);
+ td->vacancy = ofputil_decode_table_vacancy(otd->config, version);
+ td->eviction_flags = UINT32_MAX;
+
+ struct ofpbuf properties = ofpbuf_const_initializer(
+ ofpbuf_pull(msg, length), length);
+ while (properties.size > 0) {
+ struct ofpbuf payload;
+ enum ofperr error;
+ uint64_t type;
+
+ error = ofpprop_pull(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPTMPT14_EVICTION:
+ error = ofpprop_parse_u32(&payload, &td->eviction_flags);
+ break;
+
+ case OFPTMPT14_VACANCY:
+ error = parse_table_desc_vacancy_property(&payload, td);
+ break;
+
+ default:
+ error = OFPPROP_UNKNOWN(true, "table_desc", type);
+ 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(ovs_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) {
+ ofpprop_put_u32(reply, OFPTMPT14_EVICTION, td->eviction_flags);
+ }
+ if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+ struct ofp14_table_mod_prop_vacancy *otv;
+
+ otv = ofpprop_put_zeros(reply, OFPTMPT14_VACANCY, sizeof *otv);
+ otv->vacancy_down = td->table_vacancy.vacancy_down;
+ otv->vacancy_up = td->table_vacancy.vacancy_up;
+ otv->vacancy = td->table_vacancy.vacancy;
+ }
+
+ 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, td->vacancy,
+ version);
+ ofpmp_postappend(replies, start_otd);
+}
+
+/* This function parses Vacancy property, and decodes the
+ * ofp14_table_mod_prop_vacancy in ofputil_table_mod.
+ * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is
+ * greater than vacancy_up and also when current vacancy has non-zero
+ * value. Returns 0 on success. */
+static enum ofperr
+parse_table_mod_vacancy_property(struct ofpbuf *property,
+ struct ofputil_table_mod *tm)
+{
+ struct ofp14_table_mod_prop_vacancy *otv = property->data;
+
+ if (property->size != sizeof *otv) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+ tm->table_vacancy.vacancy_down = otv->vacancy_down;
+ tm->table_vacancy.vacancy_up = otv->vacancy_up;
+ if (tm->table_vacancy.vacancy_down > tm->table_vacancy.vacancy_up) {
+ OFPPROP_LOG(&rl, false,
+ "Value of vacancy_down is greater than vacancy_up");
+ return OFPERR_OFPBPC_BAD_VALUE;
+ }
+ if (tm->table_vacancy.vacancy_down > 100 ||
+ tm->table_vacancy.vacancy_up > 100) {
+ OFPPROP_LOG(&rl, false, "Vacancy threshold percentage "
+ "should not be greater than 100");
+ return OFPERR_OFPBPC_BAD_VALUE;
+ }
+ tm->table_vacancy.vacancy = otv->vacancy;
+ if (tm->table_vacancy.vacancy) {
+ OFPPROP_LOG(&rl, false,
+ "Vacancy value should be zero for table-mod messages");
+ return OFPERR_OFPBPC_BAD_VALUE;
+ }
+ 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 vacancy configuration that it specifies.
+ *
+ * Only OpenFlow 1.4 and later specify table vacancy configuration this way,
+ * so for other 'version' this function always returns
+ * OFPUTIL_TABLE_VACANCY_DEFAULT. */
+static enum ofputil_table_vacancy
+ofputil_decode_table_vacancy(ovs_be32 config, enum ofp_version version)
+{
+ return (version < OFP14_VERSION ? OFPUTIL_TABLE_VACANCY_DEFAULT
+ : config & htonl(OFPTC14_VACANCY_EVENTS) ? OFPUTIL_TABLE_VACANCY_ON
+ : OFPUTIL_TABLE_VACANCY_OFF);
+}
+
+/* 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 ofputil_table_vacancy vacancy,
+ enum ofp_version version)
+{
+ uint32_t config = 0;
+ /* Search for "OFPTC_* Table Configuration" in the documentation 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:
+ case OFP16_VERSION:
+ /* OpenFlow 1.4 introduced OFPTC14_EVICTION and
+ * OFPTC14_VACANCY_EVENTS. */
+ if (eviction == OFPUTIL_TABLE_EVICTION_ON) {
+ config |= OFPTC14_EVICTION;
+ }
+ if (vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+ config |= OFPTC14_VACANCY_EVENTS;
+ }
+ return htonl(config);
+ }
+
+ 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(&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)
+{
+ memset(pm, 0, sizeof *pm);
+ pm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
+ pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
+ pm->eviction_flags = UINT32_MAX;
+ pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT;
+
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+ enum ofpraw 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);
+ pm->vacancy = ofputil_decode_table_vacancy(otm->config, oh->version);
+ while (b.size > 0) {
+ struct ofpbuf property;
+ enum ofperr error;
+ uint64_t type;
+
+ error = ofpprop_pull(&b, &property, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case OFPTMPT14_EVICTION:
+ error = ofpprop_parse_u32(&property, &pm->eviction);
+ break;
+
+ case OFPTMPT14_VACANCY:
+ error = parse_table_mod_vacancy_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,
+ tm->vacancy, ofp_version);
+ break;
+ }
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ case OFP16_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 = tm->table_id;
+ otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+ tm->vacancy, ofp_version);
+
+ if (tm->eviction_flags != UINT32_MAX) {
+ ofpprop_put_u32(b, OFPTMPT14_EVICTION, tm->eviction_flags);
+ }
+ if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+ struct ofp14_table_mod_prop_vacancy *otv;
+
+ otv = ofpprop_put_zeros(b, OFPTMPT14_VACANCY, sizeof *otv);
+ otv->vacancy_down = tm->table_vacancy.vacancy_down;
+ otv->vacancy_up = tm->table_vacancy.vacancy_up;
+ }
+ break;
+ }
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ return b;
+}
+
+/* Convert 'setting' (as described for the "mod-table" command
+ * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and
+ * 'tm->table_vacancy->vacancy_down' threshold values.
+ * For the two threshold values, value of vacancy_up is always greater
+ * than value of vacancy_down.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * OVS_WARN_UNUSED_RESULT
+parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting)
+{
+ char *save_ptr = NULL;
+ char *vac_up, *vac_down;
+ char *value = xstrdup(setting);
+ char *ret_msg;
+ int vacancy_up, vacancy_down;
+
+ strtok_r(value, ":", &save_ptr);
+ vac_down = strtok_r(NULL, ",", &save_ptr);
+ if (!vac_down) {
+ ret_msg = xasprintf("Vacancy down value missing");
+ goto exit;
+ }
+ if (!str_to_int(vac_down, 0, &vacancy_down) ||
+ vacancy_down < 0 || vacancy_down > 100) {
+ ret_msg = xasprintf("Invalid vacancy down value \"%s\"", vac_down);
+ goto exit;
+ }
+ vac_up = strtok_r(NULL, ",", &save_ptr);
+ if (!vac_up) {
+ ret_msg = xasprintf("Vacancy up value missing");
+ goto exit;
+ }
+ if (!str_to_int(vac_up, 0, &vacancy_up) ||
+ vacancy_up < 0 || vacancy_up > 100) {
+ ret_msg = xasprintf("Invalid vacancy up value \"%s\"", vac_up);
+ goto exit;
+ }
+ if (vacancy_down > vacancy_up) {
+ ret_msg = xasprintf("Invalid vacancy range, vacancy up should be "
+ "greater than vacancy down (%s)",
+ ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE));
+ goto exit;
+ }
+
+ free(value);
+ tm->table_vacancy.vacancy_down = vacancy_down;
+ tm->table_vacancy.vacancy_up = vacancy_up;
+ return NULL;
+
+exit:
+ free(value);
+ return ret_msg;
+}
+
+/* Convert 'table_id' and 'setting' (as described for the "mod-table" command
+ * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a
+ * switch.
+ *
+ * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into
+ * '*usable_versions'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+char * OVS_WARN_UNUSED_RESULT
+parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
+ const char *setting,
+ const struct ofputil_table_map *table_map,
+ uint32_t *usable_versions)
+{
+ *usable_versions = 0;
+ if (!strcasecmp(table_id, "all")) {
+ tm->table_id = OFPTT_ALL;
+ } else if (!ofputil_table_from_string(table_id, table_map,
+ &tm->table_id)) {
+ return xasprintf("unknown table \"%s\"", table_id);
+ }
+
+ tm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
+ tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
+ tm->eviction_flags = UINT32_MAX;
+ tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT;
+ tm->table_vacancy.vacancy_down = 0;
+ tm->table_vacancy.vacancy_up = 0;
+ tm->table_vacancy.vacancy = 0;
+ /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod.
+ * Only OpenFlow 1.4+ can configure eviction and vacancy events
+ * via table_mod.
+ */
+ if (!strcmp(setting, "controller")) {
+ tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER;
+ *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+ } else if (!strcmp(setting, "continue")) {
+ tm->miss = OFPUTIL_TABLE_MISS_CONTINUE;
+ *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+ } else if (!strcmp(setting, "drop")) {
+ tm->miss = OFPUTIL_TABLE_MISS_DROP;
+ *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+ } else if (!strcmp(setting, "evict")) {
+ tm->eviction = OFPUTIL_TABLE_EVICTION_ON;
+ *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
+ } else if (!strcmp(setting, "noevict")) {
+ tm->eviction = OFPUTIL_TABLE_EVICTION_OFF;
+ *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
+ } else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) {
+ tm->vacancy = OFPUTIL_TABLE_VACANCY_ON;
+ *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
+ char *error = parse_ofp_table_vacancy(tm, setting);
+ if (error) {
+ return error;
+ }
+ } else if (!strcmp(setting, "novacancy")) {
+ tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF;
+ *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
+ } else {
+ return xasprintf("invalid table_mod setting %s", setting);
+ }
+
+ if (tm->table_id == 0xfe
+ && tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) {
+ return xstrdup("last table's flow miss handling can not be continue");
+ }
+
+ return NULL;
+}
+
+/* Table stats. */
+
+/* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be
+ * matched and a field that must be wildcarded. This function returns a bitmap
+ * that contains both kinds of fields. */
+static struct mf_bitmap
+wild_or_nonmatchable_fields(const struct ofputil_table_features *features)
+{
+ struct mf_bitmap wc = features->match;
+ bitmap_not(wc.bm, MFF_N_IDS);
+ bitmap_or(wc.bm, features->wildcard.bm, MFF_N_IDS);
+ return wc;
+}
+
+struct ofp10_wc_map {
+ enum ofp10_flow_wildcards wc10;
+ enum mf_field_id mf;
+};
+
+static const struct ofp10_wc_map ofp10_wc_map[] = {
+ { OFPFW10_IN_PORT, MFF_IN_PORT },
+ { OFPFW10_DL_VLAN, MFF_VLAN_VID },
+ { OFPFW10_DL_SRC, MFF_ETH_SRC },
+ { OFPFW10_DL_DST, MFF_ETH_DST},
+ { OFPFW10_DL_TYPE, MFF_ETH_TYPE },
+ { OFPFW10_NW_PROTO, MFF_IP_PROTO },
+ { OFPFW10_TP_SRC, MFF_TCP_SRC },
+ { OFPFW10_TP_DST, MFF_TCP_DST },
+ { OFPFW10_NW_SRC_MASK, MFF_IPV4_SRC },
+ { OFPFW10_NW_DST_MASK, MFF_IPV4_DST },
+ { OFPFW10_DL_VLAN_PCP, MFF_VLAN_PCP },
+ { OFPFW10_NW_TOS, MFF_IP_DSCP },
+};
+
+static ovs_be32
+mf_bitmap_to_of10(const struct mf_bitmap *fields)
+{
+ const struct ofp10_wc_map *p;
+ uint32_t wc10 = 0;
+
+ for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) {
+ if (bitmap_is_set(fields->bm, p->mf)) {
+ wc10 |= p->wc10;
+ }
+ }
+ return htonl(wc10);
+}
+
+static struct mf_bitmap
+mf_bitmap_from_of10(ovs_be32 wc10_)
+{
+ struct mf_bitmap fields = MF_BITMAP_INITIALIZER;
+ const struct ofp10_wc_map *p;
+ uint32_t wc10 = ntohl(wc10_);
+
+ for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) {
+ if (wc10 & p->wc10) {
+ bitmap_set1(fields.bm, p->mf);
+ }
+ }
+ return fields;
+}
+
+static void
+ofputil_put_ofp10_table_stats(const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features,
+ struct ofpbuf *buf)
+{
+ struct mf_bitmap wc = wild_or_nonmatchable_fields(features);
+ struct ofp10_table_stats *out;
+
+ out = ofpbuf_put_zeros(buf, sizeof *out);
+ out->table_id = features->table_id;
+ ovs_strlcpy_arrays(out->name, features->name);
+ out->wildcards = mf_bitmap_to_of10(&wc);
+ out->max_entries = htonl(features->max_entries);
+ out->active_count = htonl(stats->active_count);
+ put_32aligned_be64(&out->lookup_count, htonll(stats->lookup_count));
+ put_32aligned_be64(&out->matched_count, htonll(stats->matched_count));
+}
+
+struct ofp11_wc_map {
+ enum ofp11_flow_match_fields wc11;
+ enum mf_field_id mf;
+};
+
+static const struct ofp11_wc_map ofp11_wc_map[] = {
+ { OFPFMF11_IN_PORT, MFF_IN_PORT },
+ { OFPFMF11_DL_VLAN, MFF_VLAN_VID },
+ { OFPFMF11_DL_VLAN_PCP, MFF_VLAN_PCP },
+ { OFPFMF11_DL_TYPE, MFF_ETH_TYPE },
+ { OFPFMF11_NW_TOS, MFF_IP_DSCP },
+ { OFPFMF11_NW_PROTO, MFF_IP_PROTO },
+ { OFPFMF11_TP_SRC, MFF_TCP_SRC },
+ { OFPFMF11_TP_DST, MFF_TCP_DST },
+ { OFPFMF11_MPLS_LABEL, MFF_MPLS_LABEL },
+ { OFPFMF11_MPLS_TC, MFF_MPLS_TC },
+ /* I don't know what OFPFMF11_TYPE means. */
+ { OFPFMF11_DL_SRC, MFF_ETH_SRC },
+ { OFPFMF11_DL_DST, MFF_ETH_DST },
+ { OFPFMF11_NW_SRC, MFF_IPV4_SRC },
+ { OFPFMF11_NW_DST, MFF_IPV4_DST },
+ { OFPFMF11_METADATA, MFF_METADATA },
+};
+
+static ovs_be32
+mf_bitmap_to_of11(const struct mf_bitmap *fields)
+{
+ const struct ofp11_wc_map *p;
+ uint32_t wc11 = 0;
+
+ for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) {
+ if (bitmap_is_set(fields->bm, p->mf)) {
+ wc11 |= p->wc11;
+ }
+ }
+ return htonl(wc11);
+}
+
+static struct mf_bitmap
+mf_bitmap_from_of11(ovs_be32 wc11_)
+{
+ struct mf_bitmap fields = MF_BITMAP_INITIALIZER;
+ const struct ofp11_wc_map *p;
+ uint32_t wc11 = ntohl(wc11_);
+
+ for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) {
+ if (wc11 & p->wc11) {
+ bitmap_set1(fields.bm, p->mf);
+ }
+ }
+ return fields;
+}
+
+static void
+ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features,
+ struct ofpbuf *buf)
+{
+ struct mf_bitmap wc = wild_or_nonmatchable_fields(features);
+ struct ofp11_table_stats *out;
+
+ out = ofpbuf_put_zeros(buf, sizeof *out);
+ out->table_id = features->table_id;
+ ovs_strlcpy_arrays(out->name, features->name);
+ out->wildcards = mf_bitmap_to_of11(&wc);
+ out->match = mf_bitmap_to_of11(&features->match);
+ out->instructions = ovsinst_bitmap_to_openflow(
+ features->nonmiss.instructions, OFP11_VERSION);
+ out->write_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.write.ofpacts, OFP11_VERSION);
+ out->apply_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.apply.ofpacts, OFP11_VERSION);
+ out->config = htonl(features->miss_config);
+ out->max_entries = htonl(features->max_entries);
+ out->active_count = htonl(stats->active_count);
+ out->lookup_count = htonll(stats->lookup_count);
+ out->matched_count = htonll(stats->matched_count);
+}
+
+static void
+ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features,
+ struct ofpbuf *buf)
+{
+ struct ofp12_table_stats *out;
+
+ out = ofpbuf_put_zeros(buf, sizeof *out);
+ out->table_id = features->table_id;
+ ovs_strlcpy_arrays(out->name, features->name);
+ out->match = oxm_bitmap_from_mf_bitmap(&features->match, OFP12_VERSION);
+ out->wildcards = oxm_bitmap_from_mf_bitmap(&features->wildcard,
+ OFP12_VERSION);
+ out->write_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.write.ofpacts, OFP12_VERSION);
+ out->apply_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.apply.ofpacts, OFP12_VERSION);
+ out->write_setfields = oxm_bitmap_from_mf_bitmap(
+ &features->nonmiss.write.set_fields, OFP12_VERSION);
+ out->apply_setfields = oxm_bitmap_from_mf_bitmap(
+ &features->nonmiss.apply.set_fields, OFP12_VERSION);
+ out->metadata_match = features->metadata_match;
+ out->metadata_write = features->metadata_write;
+ out->instructions = ovsinst_bitmap_to_openflow(
+ features->nonmiss.instructions, OFP12_VERSION);
+ out->config = ofputil_encode_table_config(features->miss_config,
+ OFPUTIL_TABLE_EVICTION_DEFAULT,
+ OFPUTIL_TABLE_VACANCY_DEFAULT,
+ OFP12_VERSION);
+ out->max_entries = htonl(features->max_entries);
+ out->active_count = htonl(stats->active_count);
+ out->lookup_count = htonll(stats->lookup_count);
+ out->matched_count = htonll(stats->matched_count);
+}
+
+static void
+ofputil_put_ofp13_table_stats(const struct ofputil_table_stats *stats,
+ struct ofpbuf *buf)
+{
+ struct ofp13_table_stats *out;
+
+ out = ofpbuf_put_zeros(buf, sizeof *out);
+ out->table_id = stats->table_id;
+ out->active_count = htonl(stats->active_count);
+ out->lookup_count = htonll(stats->lookup_count);
+ out->matched_count = htonll(stats->matched_count);
+}
+
+struct ofpbuf *
+ofputil_encode_table_stats_reply(const struct ofp_header *request)
+{
+ return ofpraw_alloc_stats_reply(request, 0);
+}
+
+void
+ofputil_append_table_stats_reply(struct ofpbuf *reply,
+ const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features)
+{
+ struct ofp_header *oh = reply->header;
+
+ ovs_assert(stats->table_id == features->table_id);
+
+ switch ((enum ofp_version) oh->version) {
+ case OFP10_VERSION:
+ ofputil_put_ofp10_table_stats(stats, features, reply);
+ break;
+
+ case OFP11_VERSION:
+ ofputil_put_ofp11_table_stats(stats, features, reply);
+ break;
+
+ case OFP12_VERSION:
+ ofputil_put_ofp12_table_stats(stats, features, reply);
+ break;
+
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ case OFP16_VERSION:
+ ofputil_put_ofp13_table_stats(stats, reply);
+ break;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+static int
+ofputil_decode_ofp10_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp10_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+ ovs_strlcpy_arrays(features->name, ots->name);
+ features->max_entries = ntohl(ots->max_entries);
+ features->match = features->wildcard = mf_bitmap_from_of10(ots->wildcards);
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(get_32aligned_be64(&ots->lookup_count));
+ stats->matched_count = ntohll(get_32aligned_be64(&ots->matched_count));
+
+ return 0;
+}
+
+static int
+ofputil_decode_ofp11_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp11_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+ ovs_strlcpy_arrays(features->name, ots->name);
+ features->max_entries = ntohl(ots->max_entries);
+ features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
+ ots->instructions, OFP11_VERSION);
+ features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow(
+ ots->write_actions, OFP11_VERSION);
+ features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
+ ots->write_actions, OFP11_VERSION);
+ features->miss = features->nonmiss;
+ features->miss_config = ofputil_decode_table_miss(ots->config,
+ OFP11_VERSION);
+ features->match = mf_bitmap_from_of11(ots->match);
+ features->wildcard = mf_bitmap_from_of11(ots->wildcards);
+ bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(ots->lookup_count);
+ stats->matched_count = ntohll(ots->matched_count);
+
+ return 0;
+}
+
+static int
+ofputil_decode_ofp12_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp12_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+ ovs_strlcpy_arrays(features->name, ots->name);
+ features->metadata_match = ots->metadata_match;
+ features->metadata_write = ots->metadata_write;
+ features->miss_config = ofputil_decode_table_miss(ots->config,
+ OFP12_VERSION);
+ features->max_entries = ntohl(ots->max_entries);
+
+ features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
+ ots->instructions, OFP12_VERSION);
+ features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow(
+ ots->write_actions, OFP12_VERSION);
+ features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
+ ots->apply_actions, OFP12_VERSION);
+ features->nonmiss.write.set_fields = oxm_bitmap_to_mf_bitmap(
+ ots->write_setfields, OFP12_VERSION);
+ features->nonmiss.apply.set_fields = oxm_bitmap_to_mf_bitmap(
+ ots->apply_setfields, OFP12_VERSION);
+ features->miss = features->nonmiss;
+
+ features->match = oxm_bitmap_to_mf_bitmap(ots->match, OFP12_VERSION);
+ features->wildcard = oxm_bitmap_to_mf_bitmap(ots->wildcards,
+ OFP12_VERSION);
+ bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(ots->lookup_count);
+ stats->matched_count = ntohll(ots->matched_count);
+
+ return 0;
+}
+
+static int
+ofputil_decode_ofp13_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp13_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(ots->lookup_count);
+ stats->matched_count = ntohll(ots->matched_count);
+
+ return 0;
+}
+
+int
+ofputil_decode_table_stats_reply(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ const struct ofp_header *oh;
+
+ if (!msg->header) {
+ ofpraw_pull_assert(msg);
+ }
+ oh = msg->header;
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ memset(stats, 0, sizeof *stats);
+ memset(features, 0, sizeof *features);
+ features->supports_eviction = -1;
+ features->supports_vacancy_events = -1;
+
+ switch ((enum ofp_version) oh->version) {
+ case OFP10_VERSION:
+ return ofputil_decode_ofp10_table_stats(msg, stats, features);
+
+ case OFP11_VERSION:
+ return ofputil_decode_ofp11_table_stats(msg, stats, features);
+
+ case OFP12_VERSION:
+ return ofputil_decode_ofp12_table_stats(msg, stats, features);
+
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ case OFP16_VERSION:
+ return ofputil_decode_ofp13_table_stats(msg, stats, features);
+
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+static void
+ofputil_put_ofp14_table_desc(const struct ofputil_table_desc *td,
+ struct ofpbuf *b, enum ofp_version version)
+{
+ struct ofp14_table_desc *otd;
+ struct ofp14_table_mod_prop_vacancy *otv;
+ size_t start_otd;
+
+ start_otd = b->size;
+ ofpbuf_put_zeros(b, sizeof *otd);
+
+ ofpprop_put_u32(b, OFPTMPT14_EVICTION, td->eviction_flags);
+
+ otv = ofpbuf_put_zeros(b, sizeof *otv);
+ otv->type = htons(OFPTMPT14_VACANCY);
+ otv->length = htons(sizeof *otv);
+ otv->vacancy_down = td->table_vacancy.vacancy_down;
+ otv->vacancy_up = td->table_vacancy.vacancy_up;
+ otv->vacancy = td->table_vacancy.vacancy;
+
+ otd = ofpbuf_at_assert(b, start_otd, sizeof *otd);
+ otd->length = htons(b->size - start_otd);
+ otd->table_id = td->table_id;
+ otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT,
+ td->eviction, td->vacancy,
+ version);
+}
+
+/* Converts the abstract form of a "table status" message in '*ts' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in
+ * a buffer owned by the caller. */
+struct ofpbuf *
+ofputil_encode_table_status(const struct ofputil_table_status *ts,
+ enum ofputil_protocol protocol)
+{
+ enum ofp_version version;
+ struct ofpbuf *b;
+
+ version = ofputil_protocol_to_ofp_version(protocol);
+ if (version >= OFP14_VERSION) {
+ enum ofpraw raw;
+ struct ofp14_table_status *ots;
+
+ raw = OFPRAW_OFPT14_TABLE_STATUS;
+ b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
+ ots = ofpbuf_put_zeros(b, sizeof *ots);
+ ots->reason = ts->reason;
+ ofputil_put_ofp14_table_desc(&ts->desc, b, version);
+ ofpmsg_update_length(b);
+ return b;
+ } else {
+ return NULL;
+ }
+}
+
+/* Decodes the OpenFlow "table status" message in '*ots' into an abstract form
+ * in '*ts'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_table_status(const struct ofp_header *oh,
+ struct ofputil_table_status *ts)
+{
+ const struct ofp14_table_status *ots;
+ struct ofpbuf b;
+ enum ofperr error;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ ots = ofpbuf_pull(&b, sizeof *ots);
+
+ if (raw == OFPRAW_OFPT14_TABLE_STATUS) {
+ if (ots->reason != OFPTR_VACANCY_DOWN
+ && ots->reason != OFPTR_VACANCY_UP) {
+ return OFPERR_OFPBPC_BAD_VALUE;
+ }
+ ts->reason = ots->reason;
+
+ error = ofputil_decode_table_desc(&b, &ts->desc, oh->version);
+ return error;
+ } else {
+ return OFPERR_OFPBRC_BAD_VERSION;
+ }
+
+ return 0;
+}