@@ -167,4 +167,172 @@ struct ofp15_group_desc_stats {
};
OFP_ASSERT(sizeof(struct ofp15_group_desc_stats) == 16);
+/* Table Features request commands */
+enum ofp15_table_features_command {
+ OFPTFC_REPLACE = 0, /* Replace full pipeline. */
+ OFPTFC_MODIFY = 1, /* Modify flow tables capabilities. */
+ OFPTFC_ENABLE = 2, /* Enable flow tables in the pipeline. */
+ OFPTFC_DISABLE = 3, /* Disable flow tables in pipeline. */
+};
+
+/* Flags of features supported by the table. */
+enum ofp15_table_feature_flag {
+ OFPTFF_INGRESS_TABLE = 1 << 0, /* Can be configured as ingress table. */
+ OFPTFF_EGRESS_TABLE = 1 << 1, /* Can be configured as egress table. */
+ OFPTFF_FIRST_EGRESS = 1 << 4, /* Is the first egress table. */
+};
+
+/* Common header for all Table Feature Properties */
+struct ofp15_table_feature_prop_header {
+ ovs_be16 type; /* One of OFPTFPT_*. */
+ ovs_be16 length; /* Length in bytes of this property. */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_header) == 4);
+
+/* Body for ofp_multipart_request of type OFPMP_TABLE_FEATURES./
+ * Body of reply to OFPMP_TABLE_FEATURES request. */
+struct ofp15_table_features {
+ ovs_be16 length; /* Length is padded to 64 bits. */
+ uint8_t table_id; /* Identifier of table. Lower numbered tables
+ * are consulted first. */
+ uint8_t command; /* One of OFPTFC_*. */
+ ovs_be32 features; /* Bitmap of OFPTFF_* values. */
+ char name[OFP_MAX_TABLE_NAME_LEN];
+ ovs_be64 metadata_match; /* Bits of metadata table can match. */
+ ovs_be64 metadata_write; /* Bits of metadata table can write. */
+
+ /* In OF1.3 this field was named 'config' and it was useless because OF1.3
+ * did not define any OFPTC_* bits.
+ *
+ * OF1.4 renamed this field to 'capabilities' and added OFPTC14_EVICTION
+ * and OFPTC14_VACANCY_EVENTS. */
+ ovs_be32 capabilities; /* Bitmap of OFPTC_* values */
+
+ ovs_be32 max_entries; /* Max number of entries supported. */
+
+ /* Table Feature Property list */
+ /* struct ofp15_table_feature_prop_header properties[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_features) == 64);
+
+/* Table Feature property types.
+ * Low order bit cleared indicates a property for a regular Flow Entry.
+ * Low order bit set indicates a property for the Table-Miss Flow Entry. */
+enum ofp15_table_feature_prop_type {
+ OFPTFPT15_INSTRUCTIONS = 0, /* Instructions property. */
+ OFPTFPT15_INSTRUCTIONS_MISS = 1, /* Instructions for table-miss. */
+ OFPTFPT15_NEXT_TABLES = 2, /* Next Table property. */
+ OFPTFPT15_NEXT_TABLES_MISS = 3, /* Next Table for table-miss. */
+ OFPTFPT15_WRITE_ACTIONS = 4, /* Write Actions property. */
+ OFPTFPT15_WRITE_ACTIONS_MISS = 5, /* Write Actions for table-miss. */
+ OFPTFPT15_APPLY_ACTIONS = 6, /* Apply Actions property. */
+ OFPTFPT15_APPLY_ACTIONS_MISS = 7, /* Apply Actions for table-miss. */
+ OFPTFPT15_MATCH = 8, /* Match property. */
+ OFPTFPT15_WILDCARDS = 10, /* Wildcards property. */
+ OFPTFPT15_WRITE_SETFIELD = 12, /* Write Set-Field property. */
+ OFPTFPT15_WRITE_SETFIELD_MISS = 13, /* Write Set-Field for table-miss. */
+ OFPTFPT15_APPLY_SETFIELD = 14, /* Apply Set-Field property. */
+ OFPTFPT15_APPLY_SETFIELD_MISS = 15, /* Apply Set-Field for table-miss. */
+ OFPTFPT15_TABLE_SYNC_FROM = 16, /* Table synchronisation property. */
+ OFPTFPT15_WRITE_COPYFIELD = 18, /* Write Copy-Field property. */
+ OFPTFPT15_WRITE_COPYFIELD_MISS = 19, /* Write Copy-Field for table-miss. */
+ OFPTFPT15_APPLY_COPYFIELD = 20, /* Apply Copy-Field property. */
+ OFPTFPT15_APPLY_COPYFIELD_MISS = 21, /* Apply Copy-Field for table-miss. */
+ OFPTFPT15_PACKET_TYPES = 22, /* Packet types property. */
+ OFPTFPT15_EXPERIMENTER = 0xFFFE, /* Experimenter property. */
+ OFPTFPT15_EXPERIMENTER_MISS = 0xFFFF, /* Experimenter for table-miss. */
+};
+
+/* Instructions property */
+struct ofp15_table_feature_prop_instructions {
+ ovs_be16 type; /* One of OFPTFPT15_INSTRUCTIONS,
+ * OFPTFPT15_INSTRUCTIONS_MISS. */
+ ovs_be16 length; /* Length in bytes of this property. */
+ /* Followed by:
+ * - Exactly (length - 4) bytes containing the instruction ids, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+ /* struct ofp11_instruction instruction_ids[0]; List of instructions
+ without any data */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_instructions) == 4);
+
+/* Next Tables property */
+struct ofp15_table_feature_prop_next_tables {
+ ovs_be16 type; /* One of OFPTFPT15_NEXT_TABLES,
+ * OFPTFPT15_NEXT_TABLES_MISS.
+ * OFPTFPT15_TABLE_SYNC_FROM. */
+ ovs_be16 length; /* Length in bytes of this property. */
+ /* Followed by:
+ * - Exactly (length - 4) bytes containing the table_ids, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+ /* uint8_t next_table_ids[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_next_tables) == 4);
+
+/* Actions property */
+struct ofp15_table_feature_prop_actions {
+ ovs_be16 type; /* One of OFPTFPT15_WRITE_ACTIONS,
+ * OFPTFPT15_WRITE_ACTIONS_MISS,
+ * OFPTFPT15_APPLY_ACTIONS,
+ * OFPTFPT15_APPLY_ACTIONS_MISS. */
+ ovs_be16 length; /* Length in bytes of this property. */
+ /* Followed by:
+ * - Exactly (length - 4) bytes containing the action_ids, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+ /* struct ofp_action_header action_ids[0]; List of actions
+ without any data */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_actions) == 4);
+
+/* Match, Wildcard or Set-Field property */
+struct ofp15_table_feature_prop_oxm {
+ ovs_be16 type; /* One of OFPTFPT15_MATCH, OFPTFPT15_WILDCARDS,
+ * OFPTFPT15_WRITE_SETFIELD,
+ * OFPTFPT15_WRITE_SETFIELD_MISS,
+ * OFPTFPT15_APPLY_SETFIELD,
+ * OFPTFPT15_APPLY_SETFIELD_MISS.
+ * OFPTFPT15_WRITE_COPYFIELD,
+ * OFPTFPT15_WRITE_COPYFIELD_MISS,
+ * OFPTFPT15_APPLY_COPYFIELD,
+ * OFPTFPT15_APPLY_COPYFIELD_MISS. */
+ ovs_be16 length; /* Length in bytes of this property. */
+ /* Followed by:
+ * - Exactly (length - 4) bytes containing the oxm_ids, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+ /* ovs_be32 oxm_ids[0]; Array of OXM headers */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_oxm) == 4);
+
+/* Packet types property */
+struct ofp15_table_feature_prop_oxm_values {
+ ovs_be16 type; /* OFPTFPT15_PACKET_TYPES. */
+ ovs_be16 length; /* Length in bytes of this property. */
+ /* Followed by:
+ * - Exactly (length - 4) bytes containing the oxm values, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+ /*uint32_t oxm_values[0]; Array of OXM values */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_oxm_values) == 4);
+
+/* Experimenter table feature property */
+struct ofp15_table_feature_prop_experimenter {
+ ovs_be16 type; /* One of OFPTFPT15_EXPERIMENTER,
+ * OFPTFPT15_EXPERIMENTER_MISS. */
+ ovs_be16 length; /* Length in bytes of this property. */
+ ovs_be32 experimenter; /* Experimenter ID which takes the same form
+ * as in struct ofp_experimenter_header. */
+ ovs_be32 exp_type; /* Experimenter defined. */
+ /* Followed by:
+ * - Exactly (length - 12) bytes containing the experimenter data, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+ /* ovs_be32 experimenter_data[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_experimenter) == 12);
+
#endif /* openflow/openflow-1.5.h */
@@ -369,12 +369,18 @@ enum ofpraw {
/* OFPST 1.3+ (11): struct ofp13_meter_features. */
OFPRAW_OFPST13_METER_FEATURES_REPLY,
- /* OFPST 1.3+ (12): void. */
+ /* OFPST 1.3-1.4 (12): void. */
OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
- /* OFPST 1.3+ (12): struct ofp13_table_features, uint8_t[8][]. */
+ /* OFPST 1.5+ (12): struct ofp15_table_features[]. */
+ OFPRAW_OFPST15_TABLE_FEATURES_REQUEST,
+
+ /* OFPST 1.3-1.4 (12): struct ofp13_table_features, uint8_t[8][]. */
OFPRAW_OFPST13_TABLE_FEATURES_REPLY,
+ /* OFPST 1.5+ (12): struct ofp15_table_features, uint8_t[8][]. */
+ OFPRAW_OFPST15_TABLE_FEATURES_REPLY,
+
/* OFPST 1.4+ (15): void. */
OFPRAW_OFPST14_TABLE_DESC_REQUEST,
@@ -622,9 +628,11 @@ enum ofptype {
OFPTYPE_METER_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_METER_FEATURES_REPLY. */
- OFPTYPE_TABLE_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST. */
+ OFPTYPE_TABLE_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST.
+ * OFPRAW_OFPST15_TABLE_FEATURES_REQUEST. */
- OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */
+ OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY.
+ * OFPRAW_OFPST15_TABLE_FEATURES_REPLY. */
OFPTYPE_TABLE_DESC_REQUEST, /* OFPRAW_OFPST14_TABLE_DESC_REQUEST. */
@@ -1652,3 +1652,27 @@ parse_ofp_geneve_table_mod_str(struct ofputil_geneve_table_mod *gtm,
return NULL;
}
+
+/* Convert 'table_id' and table feature flag 'OFPTFF_FIRST_EGRESS' into 'tf'
+ * for sending a set_table_features command to a switch.
+ *
+ * Stores a bitmap of the OpenFlow versions that are usable for 'tf' 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_features(struct ofputil_table_features *tf, const char *table_id,
+ uint32_t *usable_versions)
+{
+ char *error = str_to_u8(table_id, "table_id", &tf->table_id);
+ if (error) {
+ return error;
+ }
+
+ *usable_versions = (1u << OFP15_VERSION);
+
+ tf->features = OFPTFF_FIRST_EGRESS;
+
+ return NULL;
+}
@@ -36,6 +36,7 @@ struct ofputil_meter_mod;
struct ofputil_table_mod;
struct ofputil_geneve_table_mod;
struct simap;
+struct ofputil_table_features;
enum ofputil_protocol;
char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
@@ -52,6 +53,11 @@ char *parse_ofp_table_mod(struct ofputil_table_mod *,
uint32_t *usable_versions)
OVS_WARN_UNUSED_RESULT;
+char *parse_ofp_table_features(struct ofputil_table_features *tf,
+ const char *table_id,
+ uint32_t *usable_versions)
+ OVS_WARN_UNUSED_RESULT;
+
char *parse_ofp_flow_mod_file(const char *file_name, int command,
struct ofputil_flow_mod **fms, size_t *n_fms,
enum ofputil_protocol *usable_protocols)
@@ -2792,6 +2792,12 @@ ofp_print_table_features(struct ds *s,
}
+ if(features->features >= 0) {
+ ds_put_format(s, " features: %s\n",
+ (features->features & OFPTFF_FIRST_EGRESS) ?
+ "first egress table" : "none");
+ }
+
if (features->max_entries) {
ds_put_format(s, " max_entries=%"PRIu32"\n", features->max_entries);
}
@@ -4454,6 +4454,12 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
return b;
}
+
+/* Variable to store the table-id of the first egress table. If it is set to
+ * 0 it means that egress processing is not enabled.
+ */
+int first_egress_table = 0;
+
/* Table features. */
@@ -4494,6 +4500,47 @@ parse_action_bitmap(struct ofpbuf *payload, enum ofp_version ofp_version,
}
static enum ofperr
+parse15_action_bitmap(struct ofpbuf *payload, enum ofp_version ofp_version,
+ uint8_t table_id, uint64_t *ofpacts)
+{
+ uint32_t types = 0;
+
+ while (payload->size > 0) {
+ uint16_t type;
+ enum ofperr error;
+
+ error = ofputil_pull_property__(payload, NULL, 1, &type);
+ if (error) {
+ return error;
+ }
+
+ /* If first egress table is set then flow tables used for egress
+ * processing must forbid the use of output actior group action in
+ * write-action instrcution and must advertise the same in flow table
+ * features as mentioned in the specification.
+ *
+ * OFPAT_* values, i.e. 0 for OFPACT_OUTPUT and 22 for OFPACT_GROUP is
+ * used for comparison as ofpact_type cannot be compared directly with
+ * 'type', which is of type ofpat.
+ */
+ if (type < CHAR_BIT * sizeof types) {
+ if (first_egress_table && first_egress_table <= table_id) {
+ if (type == 0 || type == 22) {
+ continue;
+ } else {
+ types |= 1u << type;
+ }
+ } else {
+ 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;
@@ -4575,25 +4622,10 @@ parse_oxms(struct ofpbuf *payload, bool loose,
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)
+static int
+ofputil_decode_ofpst13_table_features(struct ofpbuf *msg,
+ struct ofputil_table_features *tf,
+ bool loose)
{
const struct ofp_header *oh;
struct ofp13_table_features *otf;
@@ -4601,10 +4633,6 @@ ofputil_decode_table_features(struct ofpbuf *msg,
unsigned int len;
memset(tf, 0, sizeof *tf);
-
- if (!msg->header) {
- ofpraw_pull_assert(msg);
- }
oh = msg->header;
if (!msg->size) {
@@ -4641,6 +4669,7 @@ ofputil_decode_table_features(struct ofpbuf *msg,
tf->supports_vacancy_events = -1;
}
tf->max_entries = ntohl(otf->max_entries);
+ tf->features = -1;
while (properties.size > 0) {
struct ofpbuf payload;
@@ -4742,10 +4771,242 @@ ofputil_decode_table_features(struct ofpbuf *msg,
return 0;
}
+static int
+ofputil_decode_ofpst15_table_features(struct ofpbuf *msg,
+ struct ofputil_table_features *tf,
+ bool loose, enum ofpraw raw)
+{
+ const struct ofp_header *oh;
+ struct ofp15_table_features *otf;
+ struct ofpbuf properties;
+ unsigned int len;
+ uint32_t features;
+ uint32_t caps;
+
+ memset(tf, 0, sizeof *tf);
+ oh = msg->header;
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ if (msg->size < sizeof *otf) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ otf = msg->data;
+ len = ntohs(otf->length);
+ if (len < sizeof *otf || len % 8 || len > msg->size) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+ ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
+ ofpbuf_pull(&properties, sizeof *otf);
+
+ tf->table_id = otf->table_id;
+ if (tf->table_id == OFPTT_ALL) {
+ return OFPERR_OFPTFFC_BAD_TABLE;
+ }
+
+ if (raw == OFPRAW_OFPST15_TABLE_FEATURES_REQUEST) {
+
+ /* Return an error if TABLE_FEATURES_REQUEST attempts to set table 0
+ * as first egress table. */
+ if (tf->table_id == 0) {
+ return OFPERR_OFPTFFC_BAD_TABLE;
+ }
+
+ /* Return an error if TABLE_FEATURES_REQUEST contain properties. */
+ if (properties.size > 0) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+ }
+
+ ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
+ tf->metadata_match = otf->metadata_match;
+ tf->metadata_write = otf->metadata_write;
+ tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
+
+ caps = ntohl(otf->capabilities);
+ tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0;
+ tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0;
+
+ tf->max_entries = ntohl(otf->max_entries);
+
+ /* Return an error if any flag other than OFPTFF_FIRST_EGRESS is set. */
+ features = ntohl(otf->features);
+ if ((features & OFPTFF_FIRST_EGRESS) != 0) {
+ tf->features = OFPTFF_FIRST_EGRESS;
+ first_egress_table = tf->table_id;
+ } else if ((features & OFPTFF_INGRESS_TABLE) != 0 ||
+ (features & OFPTFF_EGRESS_TABLE) != 0 ) {
+ return OFPERR_OFPBFC_BAD_FLAGS;
+ }
+
+ while (properties.size > 0) {
+ struct ofpbuf payload;
+ enum ofperr error;
+ uint16_t type;
+
+ error = pull_table_feature_property(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch ((enum ofp15_table_feature_prop_type) type) {
+ case OFPTFPT15_INSTRUCTIONS:
+ error = parse_instruction_ids(&payload, loose,
+ &tf->nonmiss.instructions);
+ break;
+
+ case OFPTFPT15_INSTRUCTIONS_MISS:
+ error = parse_instruction_ids(&payload, loose,
+ &tf->miss.instructions);
+ break;
+
+ case OFPTFPT15_NEXT_TABLES:
+ error = parse_table_features_next_table(&payload,
+ tf->nonmiss.next);
+ break;
+
+ case OFPTFPT15_NEXT_TABLES_MISS:
+ error = parse_table_features_next_table(&payload, tf->miss.next);
+ break;
+
+ case OFPTFPT15_WRITE_ACTIONS:
+ error = parse15_action_bitmap(&payload, oh->version, tf->table_id,
+ &tf->nonmiss.write.ofpacts);
+ break;
+
+ case OFPTFPT15_WRITE_ACTIONS_MISS:
+ error = parse15_action_bitmap(&payload, oh->version, tf->table_id,
+ &tf->miss.write.ofpacts);
+ break;
+
+ case OFPTFPT15_APPLY_ACTIONS:
+ error = parse_action_bitmap(&payload, oh->version,
+ &tf->nonmiss.apply.ofpacts);
+ break;
+
+ case OFPTFPT15_APPLY_ACTIONS_MISS:
+ error = parse_action_bitmap(&payload, oh->version,
+ &tf->miss.apply.ofpacts);
+ break;
+
+ case OFPTFPT15_MATCH:
+ error = parse_oxms(&payload, loose, &tf->match, &tf->mask);
+ break;
+
+ case OFPTFPT15_WILDCARDS:
+ error = parse_oxms(&payload, loose, &tf->wildcard, NULL);
+ break;
+
+ case OFPTFPT15_WRITE_SETFIELD:
+ error = parse_oxms(&payload, loose,
+ &tf->nonmiss.write.set_fields, NULL);
+ break;
+
+ case OFPTFPT15_WRITE_SETFIELD_MISS:
+ error = parse_oxms(&payload, loose,
+ &tf->miss.write.set_fields, NULL);
+ break;
+
+ case OFPTFPT15_APPLY_SETFIELD:
+ error = parse_oxms(&payload, loose,
+ &tf->nonmiss.apply.set_fields, NULL);
+ break;
+
+ case OFPTFPT15_APPLY_SETFIELD_MISS:
+ error = parse_oxms(&payload, loose,
+ &tf->miss.apply.set_fields, NULL);
+ break;
+
+ case OFPTFPT15_TABLE_SYNC_FROM:
+ case OFPTFPT15_WRITE_COPYFIELD:
+ case OFPTFPT15_WRITE_COPYFIELD_MISS:
+ case OFPTFPT15_APPLY_COPYFIELD:
+ case OFPTFPT15_APPLY_COPYFIELD_MISS:
+ case OFPTFPT15_PACKET_TYPES:
+ log_property(loose, "unsupported table features property %"PRIu16,
+ type);
+ break;
+
+ case OFPTFPT15_EXPERIMENTER:
+ case OFPTFPT15_EXPERIMENTER_MISS:
+ default:
+ log_property(loose, "unknown table features property %"PRIu16,
+ type);
+ error = loose ? 0 : OFPERR_OFPBPC_BAD_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;
+}
+
+/* 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)
+{
+ enum ofpraw raw;
+ enum ofperr error;
+ error = (msg->header ? ofpraw_decode(&raw, msg->header)
+ : ofpraw_pull(&raw, msg));
+ if (error) {
+ return error;
+ }
+
+ switch ((int)raw) {
+ case OFPRAW_OFPST13_TABLE_FEATURES_REQUEST:
+ case OFPRAW_OFPST13_TABLE_FEATURES_REPLY:
+ return ofputil_decode_ofpst13_table_features(msg, tf, loose);
+
+ case OFPRAW_OFPST15_TABLE_FEATURES_REQUEST:
+ case OFPRAW_OFPST15_TABLE_FEATURES_REPLY:
+ return ofputil_decode_ofpst15_table_features(msg, tf, loose, raw);
+
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
/* 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)
+ofputil_encode_table_features_request(const struct ofputil_table_features *tf,
+ enum ofp_version ofp_version)
{
struct ofpbuf *request = NULL;
@@ -4757,10 +5018,22 @@ ofputil_encode_table_features_request(enum ofp_version ofp_version)
"(\'-O OpenFlow13\')");
case OFP13_VERSION:
case OFP14_VERSION:
- case OFP15_VERSION:
request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
ofp_version, 0);
break;
+ case OFP15_VERSION: {
+ struct ofp15_table_features *otf;
+
+ request = ofpraw_alloc(OFPRAW_OFPST15_TABLE_FEATURES_REQUEST,
+ ofp_version, 0);
+ if (tf != NULL && (tf->features & OFPTFF_FIRST_EGRESS)) {
+ otf = ofpbuf_put_zeros(request, sizeof *otf);
+ otf->table_id = tf->table_id;
+ otf->features = htonl(tf->features);
+ otf->length = htons(sizeof *otf);
+ }
+ }
+ break;
default:
OVS_NOT_REACHED();
}
@@ -4769,11 +5042,29 @@ ofputil_encode_table_features_request(enum ofp_version ofp_version)
}
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)
+put_ofpst13_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 = start_property(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));
+ }
+ end_property(reply, start_ofs);
+}
+
+static void
+put_ofpst15_fields_property(struct ofpbuf *reply,
+ const struct mf_bitmap *fields,
+ const struct mf_bitmap *masks,
+ enum ofp15_table_feature_prop_type property,
+ enum ofp_version version)
{
size_t start_ofs;
int field;
@@ -4787,11 +5078,30 @@ put_fields_property(struct ofpbuf *reply,
}
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)
+put_ofpst13_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)
+{
+ size_t start_ofs;
+
+ start_ofs = start_property(reply, actions_type + miss_offset);
+ put_bitmap_properties(reply,
+ ntohl(ofpact_bitmap_to_openflow(taf->ofpacts,
+ version)));
+ end_property(reply, start_ofs);
+
+ put_ofpst13_fields_property(reply, &taf->set_fields, NULL,
+ set_fields_type + miss_offset, version);
+}
+
+static void
+put_ofpst15_table_action_features(
+ struct ofpbuf *reply, const struct ofputil_table_action_features *taf,
+ enum ofp15_table_feature_prop_type actions_type,
+ enum ofp15_table_feature_prop_type set_fields_type,
+ int miss_offset, enum ofp_version version)
{
size_t start_ofs;
@@ -4801,12 +5111,12 @@ put_table_action_features(struct ofpbuf *reply,
version)));
end_property(reply, start_ofs);
- put_fields_property(reply, &taf->set_fields, NULL,
- set_fields_type + miss_offset, version);
+ put_ofpst15_fields_property(reply, &taf->set_fields, NULL,
+ set_fields_type + miss_offset, version);
}
static void
-put_table_instruction_features(
+put_ofpst13_table_instruction_features(
struct ofpbuf *reply, const struct ofputil_table_instruction_features *tif,
int miss_offset, enum ofp_version version)
{
@@ -4825,17 +5135,49 @@ put_table_instruction_features(
}
end_property(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);
+ put_ofpst13_table_action_features(reply, &tif->write,
+ OFPTFPT13_WRITE_ACTIONS,
+ OFPTFPT13_WRITE_SETFIELD,
+ miss_offset, version);
+ put_ofpst13_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)
+static void
+put_ofpst15_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;
+
+ start_ofs = start_property(reply, OFPTFPT15_INSTRUCTIONS + miss_offset);
+ put_bitmap_properties(reply,
+ ntohl(ovsinst_bitmap_to_openflow(tif->instructions,
+ version)));
+ end_property(reply, start_ofs);
+
+ start_ofs = start_property(reply, OFPTFPT15_NEXT_TABLES + miss_offset);
+ BITMAP_FOR_EACH_1 (table_id, 255, tif->next) {
+ ofpbuf_put(reply, &table_id, 1);
+ }
+ end_property(reply, start_ofs);
+
+ put_ofpst15_table_action_features(reply, &tif->write,
+ OFPTFPT15_WRITE_ACTIONS,
+ OFPTFPT15_WRITE_SETFIELD,
+ miss_offset, version);
+ put_ofpst15_table_action_features(reply, &tif->apply,
+ OFPTFPT15_APPLY_ACTIONS,
+ OFPTFPT15_APPLY_SETFIELD,
+ miss_offset, version);
+}
+
+static void
+ofputil_ofpst13_table_features_reply(const struct ofputil_table_features *tf,
+ struct ovs_list *replies)
{
struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
enum ofp_version version = ofpmp_version(replies);
@@ -4857,19 +5199,70 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
}
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_ofpst13_table_instruction_features(reply, &tf->nonmiss, 0, version);
+ put_ofpst13_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);
+ put_ofpst13_fields_property(reply, &tf->match, &tf->mask,
+ OFPTFPT13_MATCH, version);
+ put_ofpst13_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 void
+ofputil_ofpst15_table_features_reply(const struct ofputil_table_features *tf,
+ struct ovs_list *replies)
+{
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ enum ofp_version version = ofpmp_version(replies);
+ size_t start_ofs = reply->size;
+ struct ofp15_table_features *otf;
+
+ otf = ofpbuf_put_zeros(reply, sizeof *otf);
+ otf->table_id = tf->table_id;
+ ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
+ otf->metadata_match = tf->metadata_match;
+ otf->metadata_write = tf->metadata_write;
+ 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);
+
+ if ((tf->features & OFPTFF_FIRST_EGRESS) != 0) {
+ otf->features = htonl(OFPTFF_FIRST_EGRESS);
+ }
+ put_ofpst15_table_instruction_features(reply, &tf->nonmiss, 0, version);
+ put_ofpst15_table_instruction_features(reply, &tf->miss, 1, version);
+
+ put_ofpst15_fields_property(reply, &tf->match, &tf->mask,
+ OFPTFPT15_MATCH, version);
+ put_ofpst15_fields_property(reply, &tf->wildcard, NULL,
+ OFPTFPT15_WILDCARDS, version);
+
+ otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf);
+ otf->length = htons(reply->size - start_ofs);
+ ofpmp_postappend(replies, start_ofs);
+}
+
+void
+ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
+ struct ovs_list *replies)
+{
+ enum ofp_version version = ofpmp_version(replies);
+
+ if (version < OFP15_VERSION) {
+ ofputil_ofpst13_table_features_reply(tf, replies);
+ } else {
+ ofputil_ofpst15_table_features_reply(tf, replies);
+ }
+}
+
static enum ofperr
parse_table_desc_eviction_property(struct ofpbuf *property,
struct ofputil_table_desc *td)
@@ -5770,6 +6163,7 @@ ofputil_decode_table_stats_reply(struct ofpbuf *msg,
memset(features, 0, sizeof *features);
features->supports_eviction = -1;
features->supports_vacancy_events = -1;
+ features->features = -1;
switch ((enum ofp_version) oh->version) {
case OFP10_VERSION:
@@ -682,6 +682,15 @@ struct ofputil_table_features {
int supports_eviction; /* OF1.4+ only. */
int supports_vacancy_events; /* OF1.4+ only. */
+ /* The features field is a bitmap of OFPTFF_* values that defines how the
+ * flow table can be used and what are its basic features.
+ *
+ * 'features' is relevant only for Openflow 1.5 and later only. For 1.5,
+ * it will be OFPTFF_FIRST_EGRESS if first egress table is set, otherwise
+ * 0. For other versions, they are decoded as -1 and ignored for encoding.
+ */
+ int features; /* OF1.5+ only. */
+
/* Table features related to instructions. There are two instances:
*
* - 'miss' reports features available in the table miss flow.
@@ -738,7 +747,9 @@ int ofputil_decode_table_desc(struct ofpbuf *,
struct ofputil_table_desc *,
enum ofp_version);
-struct ofpbuf *ofputil_encode_table_features_request(enum ofp_version);
+struct ofpbuf *
+ofputil_encode_table_features_request(const struct ofputil_table_features *,
+ enum ofp_version);
struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version);
@@ -258,6 +258,11 @@ struct oftable {
atomic_ulong n_matched;
atomic_ulong n_missed;
+
+ /* This flag indicates that this flow table is the first egress table, when
+ * a packet is output to a port, egress processing will start with this flow
+ * table. */
+ bool is_first_egress;
};
/* Assigns TABLE to each oftable, in turn, in OFPROTO.
@@ -3163,6 +3163,9 @@ query_tables(struct ofproto *ofproto,
f->match = match;
f->mask = mask;
f->wildcard = match;
+ if (ofproto->tables[i].is_first_egress) {
+ f->features = OFPTFF_FIRST_EGRESS;
+ }
}
if (statsp) {
@@ -3553,12 +3556,34 @@ handle_table_features_request(struct ofconn *ofconn,
struct ofputil_table_features *features;
struct ovs_list replies;
struct ofpbuf msg;
+ enum ofperr error = 0;
size_t i;
ofpbuf_use_const(&msg, request, ntohs(request->length));
ofpraw_pull_assert(&msg);
- if (msg.size || ofpmp_more(request)) {
- return OFPERR_OFPTFFC_EPERM;
+
+ if (request->version < OFP15_VERSION) {
+ if (msg.size || ofpmp_more(request)) {
+ return OFPERR_OFPTFFC_EPERM;
+ }
+ } else {
+ if (msg.size) {
+ struct ofputil_table_features tf;
+ error = ofputil_decode_table_features(&msg, &tf, false);
+ if (error) {
+ return error;
+ }
+ if ((tf.features & OFPTFF_FIRST_EGRESS) != 0) {
+ for (i = 0; i < ofproto->n_tables; i++) {
+ if (ofproto->tables[i].is_first_egress) {
+ ofproto->tables[i].is_first_egress = false;
+ break;
+ }
+ }
+ ofproto->tables[tf.table_id].is_first_egress = true;
+ }
+ return 0;
+ }
}
query_tables(ofproto, &features, NULL);
@@ -2475,6 +2475,228 @@ f5 f6 f7 f8 f9 fa fb fc fd 00 00 00 00 00 00 00 \
])
AT_CLEANUP
+AT_SETUP([OFPST_TABLE_FEATURES request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 13 09 40 00 00 00 d5 00 0c 00 01 00 00 00 00 \
+09 30 00 00 00 00 00 00 74 61 62 6c 65 30 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff \
+ff ff ff ff ff ff ff ff 00 00 00 03 00 0f 42 40 \
+00 00 00 2c 00 01 00 08 00 00 00 00 00 02 00 08 \
+00 00 00 00 00 03 00 08 00 00 00 00 00 04 00 08 \
+00 00 00 00 00 05 00 08 00 00 00 00 00 00 00 00 \
+00 01 00 2c 00 01 00 08 00 00 00 00 00 02 00 08 \
+00 00 00 00 00 03 00 08 00 00 00 00 00 04 00 08 \
+00 00 00 00 00 05 00 08 00 00 00 00 00 00 00 00 \
+00 02 01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c \
+0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c \
+1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c \
+2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c \
+3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c \
+4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c \
+5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c \
+6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c \
+7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c \
+8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c \
+9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac \
+ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc \
+bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc \
+cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc \
+dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec \
+ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc \
+fd 00 00 00 00 00 00 00 00 03 01 01 01 02 03 04 \
+05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 \
+15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 \
+25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 \
+35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 \
+45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 \
+55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 \
+65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 \
+75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 \
+85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 \
+95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 \
+a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 \
+b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 \
+c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 \
+d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 \
+e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 \
+f5 f6 f7 f8 f9 fa fb fc fd 00 00 00 00 00 00 00 \
+00 04 00 84 00 00 00 08 00 00 00 00 00 0b 00 08 \
+00 00 00 00 00 0c 00 08 00 00 00 00 00 0f 00 08 \
+00 00 00 00 00 10 00 08 00 00 00 00 00 11 00 08 \
+00 00 00 00 00 12 00 08 00 00 00 00 00 13 00 08 \
+00 00 00 00 00 14 00 08 00 00 00 00 00 15 00 08 \
+00 00 00 00 00 16 00 08 00 00 00 00 00 17 00 08 \
+00 00 00 00 00 18 00 08 00 00 00 00 00 19 00 08 \
+00 00 00 00 00 1a 00 08 00 00 00 00 00 1b 00 08 \
+00 00 00 00 00 00 00 00 00 05 00 84 00 00 00 08 \
+00 00 00 00 00 0b 00 08 00 00 00 00 00 0c 00 08 \
+00 00 00 00 00 0f 00 08 00 00 00 00 00 10 00 08 \
+00 00 00 00 00 11 00 08 00 00 00 00 00 12 00 08 \
+00 00 00 00 00 13 00 08 00 00 00 00 00 14 00 08 \
+00 00 00 00 00 15 00 08 00 00 00 00 00 16 00 08 \
+00 00 00 00 00 17 00 08 00 00 00 00 00 18 00 08 \
+00 00 00 00 00 19 00 08 00 00 00 00 00 1a 00 08 \
+00 00 00 00 00 1b 00 08 00 00 00 00 00 00 00 00 \
+00 06 00 84 00 00 00 08 00 00 00 00 00 0b 00 08 \
+00 00 00 00 00 0c 00 08 00 00 00 00 00 0f 00 08 \
+00 00 00 00 00 10 00 08 00 00 00 00 00 11 00 08 \
+00 00 00 00 00 12 00 08 00 00 00 00 00 13 00 08 \
+00 00 00 00 00 14 00 08 00 00 00 00 00 15 00 08 \
+00 00 00 00 00 16 00 08 00 00 00 00 00 17 00 08 \
+00 00 00 00 00 18 00 08 00 00 00 00 00 19 00 08 \
+00 00 00 00 00 1a 00 08 00 00 00 00 00 1b 00 08 \
+00 00 00 00 00 00 00 00 00 07 00 84 00 00 00 08 \
+00 00 00 00 00 0b 00 08 00 00 00 00 00 0c 00 08 \
+00 00 00 00 00 0f 00 08 00 00 00 00 00 10 00 08 \
+00 00 00 00 00 11 00 08 00 00 00 00 00 12 00 08 \
+00 00 00 00 00 13 00 08 00 00 00 00 00 14 00 08 \
+00 00 00 00 00 15 00 08 00 00 00 00 00 16 00 08 \
+00 00 00 00 00 17 00 08 00 00 00 00 00 18 00 08 \
+00 00 00 00 00 19 00 08 00 00 00 00 00 1a 00 08 \
+00 00 00 00 00 1b 00 08 00 00 00 00 00 00 00 00 \
+00 08 00 dc 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 80 00 0a 02 00 00 08 02 \
+80 00 0c 02 80 00 0e 01 80 00 44 04 80 00 46 01 \
+80 00 48 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 80 00 38 04 80 00 14 01 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 00 01 34 01 \
+80 00 2a 02 80 00 2c 04 80 00 2e 04 80 00 30 06 \
+80 00 32 06 80 00 1a 02 80 00 1c 02 00 01 44 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+80 00 26 01 80 00 28 01 80 00 3a 01 80 00 3c 01 \
+80 00 3e 10 80 00 40 06 80 00 42 06 00 00 00 00 \
+00 0a 00 dc 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 80 00 0a 02 00 00 08 02 \
+80 00 0c 02 80 00 0e 01 80 00 44 04 80 00 46 01 \
+80 00 48 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 80 00 38 04 80 00 14 01 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 00 01 34 01 \
+80 00 2a 02 80 00 2c 04 80 00 2e 04 80 00 30 06 \
+80 00 32 06 80 00 1a 02 80 00 1c 02 00 01 44 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+80 00 26 01 80 00 28 01 80 00 3a 01 80 00 3c 01 \
+80 00 3e 10 80 00 40 06 80 00 42 06 00 00 00 00 \
+00 0c 00 a8 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 00 00 08 02 80 00 0c 02 \
+80 00 0e 01 80 00 44 04 80 00 46 01 80 00 16 04 \
+80 00 18 04 80 00 34 10 80 00 36 10 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 80 00 2a 02 \
+80 00 2c 04 80 00 2e 04 80 00 30 06 80 00 32 06 \
+80 00 1a 02 80 00 1c 02 80 00 1e 02 80 00 20 02 \
+80 00 22 02 80 00 24 02 00 0d 00 a8 80 00 4c 08 \
+00 01 3e 04 00 01 40 04 80 00 04 08 00 00 00 02 \
+80 00 00 04 00 01 42 04 00 01 00 04 00 01 02 04 \
+00 01 04 04 00 01 06 04 00 01 08 04 00 01 0a 04 \
+00 01 0c 04 00 01 0e 04 80 00 08 06 80 00 06 06 \
+00 00 08 02 80 00 0c 02 80 00 0e 01 80 00 44 04 \
+80 00 46 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 00 00 0a 01 80 00 10 01 80 00 12 01 \
+00 01 3a 01 80 00 2a 02 80 00 2c 04 80 00 2e 04 \
+80 00 30 06 80 00 32 06 80 00 1a 02 80 00 1c 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+00 0e 00 a8 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 00 00 08 02 80 00 0c 02 \
+80 00 0e 01 80 00 44 04 80 00 46 01 80 00 16 04 \
+80 00 18 04 80 00 34 10 80 00 36 10 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 80 00 2a 02 \
+80 00 2c 04 80 00 2e 04 80 00 30 06 80 00 32 06 \
+80 00 1a 02 80 00 1c 02 80 00 1e 02 80 00 20 02 \
+80 00 22 02 80 00 24 02 00 0f 00 a8 80 00 4c 08 \
+00 01 3e 04 00 01 40 04 80 00 04 08 00 00 00 02 \
+80 00 00 04 00 01 42 04 00 01 00 04 00 01 02 04 \
+00 01 04 04 00 01 06 04 00 01 08 04 00 01 0a 04 \
+00 01 0c 04 00 01 0e 04 80 00 08 06 80 00 06 06 \
+00 00 08 02 80 00 0c 02 80 00 0e 01 80 00 44 04 \
+80 00 46 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 00 00 0a 01 80 00 10 01 80 00 12 01 \
+00 01 3a 01 80 00 2a 02 80 00 2c 04 80 00 2e 04 \
+80 00 30 06 80 00 32 06 80 00 1a 02 80 00 1c 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+"], [0], [OFPST_TABLE_FEATURES reply (OF1.5) (xid=0xd5):
+ table 0 ("table0"):
+ metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+ eviction: not supported
+ vacancy events: not supported
+ features: none
+ max_entries=1000000
+ instructions (table miss and others):
+ next tables: 1-253
+ instructions: apply_actions,clear_actions,write_actions,write_metadata,goto_table
+ Write-Actions and Apply-Actions features:
+ actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+ supported on Set-Field: tun_id tun_src tun_dst metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst
+ matching:
+ tun_id: exact match or wildcard
+ tun_src: exact match or wildcard
+ tun_dst: exact match or wildcard
+ metadata: exact match or wildcard
+ in_port: exact match or wildcard
+ in_port_oxm: exact match or wildcard
+ pkt_mark: exact match or wildcard
+ reg0: exact match or wildcard
+ reg1: exact match or wildcard
+ reg2: exact match or wildcard
+ reg3: exact match or wildcard
+ reg4: exact match or wildcard
+ reg5: exact match or wildcard
+ reg6: exact match or wildcard
+ reg7: exact match or wildcard
+ eth_src: exact match or wildcard
+ eth_dst: exact match or wildcard
+ eth_type: exact match or wildcard
+ vlan_tci: exact match or wildcard
+ vlan_vid: exact match or wildcard
+ vlan_pcp: exact match or wildcard
+ mpls_label: exact match or wildcard
+ mpls_tc: exact match or wildcard
+ mpls_bos: exact match or wildcard
+ ip_src: exact match or wildcard
+ ip_dst: exact match or wildcard
+ ipv6_src: exact match or wildcard
+ ipv6_dst: exact match or wildcard
+ ipv6_label: exact match or wildcard
+ nw_proto: exact match or wildcard
+ nw_tos: exact match or wildcard
+ ip_dscp: exact match or wildcard
+ nw_ecn: exact match or wildcard
+ nw_ttl: exact match or wildcard
+ ip_frag: exact match or wildcard
+ arp_op: exact match or wildcard
+ arp_spa: exact match or wildcard
+ arp_tpa: exact match or wildcard
+ arp_sha: exact match or wildcard
+ arp_tha: exact match or wildcard
+ tcp_src: exact match or wildcard
+ tcp_dst: exact match or wildcard
+ tcp_flags: exact match or wildcard
+ udp_src: exact match or wildcard
+ udp_dst: exact match or wildcard
+ sctp_src: exact match or wildcard
+ sctp_dst: exact match or wildcard
+ icmp_type: exact match or wildcard
+ icmp_code: exact match or wildcard
+ icmpv6_type: exact match or wildcard
+ icmpv6_code: exact match or wildcard
+ nd_target: exact match or wildcard
+ nd_sll: exact match or wildcard
+ nd_tll: exact match or wildcard
+])
+AT_CLEANUP
+
AT_SETUP([OFPT_BARRIER_REQUEST - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print '01 12 00 08 00 00 00 01'], [0], [dnl
@@ -1721,6 +1721,233 @@ AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout])
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto - table features (OpenFlow 1.5)])
+OVS_VSWITCHD_START
+head_table () {
+ printf ' table 0 ("%s"):
+ metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+ eviction: not supported
+ vacancy events: not supported
+ features: none
+ max_entries=1000000
+ instructions (table miss and others):
+ next tables: 1-253
+ instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
+ Write-Actions and Apply-Actions features:
+ actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+ supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metad
ata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll
+ matching:
+ dp_hash: arbitrary mask
+ recirc_id: exact match or wildcard
+ conj_id: exact match or wildcard
+ tun_id: arbitrary mask
+ tun_src: arbitrary mask
+ tun_dst: arbitrary mask
+ tun_flags: arbitrary mask
+ tun_gbp_id: arbitrary mask
+ tun_gbp_flags: arbitrary mask
+ tun_metadata0: arbitrary mask
+ tun_metadata1: arbitrary mask
+ tun_metadata2: arbitrary mask
+ tun_metadata3: arbitrary mask
+ tun_metadata4: arbitrary mask
+ tun_metadata5: arbitrary mask
+ tun_metadata6: arbitrary mask
+ tun_metadata7: arbitrary mask
+ tun_metadata8: arbitrary mask
+ tun_metadata9: arbitrary mask
+ tun_metadata10: arbitrary mask
+ tun_metadata11: arbitrary mask
+ tun_metadata12: arbitrary mask
+ tun_metadata13: arbitrary mask
+ tun_metadata14: arbitrary mask
+ tun_metadata15: arbitrary mask
+ tun_metadata16: arbitrary mask
+ tun_metadata17: arbitrary mask
+ tun_metadata18: arbitrary mask
+ tun_metadata19: arbitrary mask
+ tun_metadata20: arbitrary mask
+ tun_metadata21: arbitrary mask
+ tun_metadata22: arbitrary mask
+ tun_metadata23: arbitrary mask
+ tun_metadata24: arbitrary mask
+ tun_metadata25: arbitrary mask
+ tun_metadata26: arbitrary mask
+ tun_metadata27: arbitrary mask
+ tun_metadata28: arbitrary mask
+ tun_metadata29: arbitrary mask
+ tun_metadata30: arbitrary mask
+ tun_metadata31: arbitrary mask
+ tun_metadata32: arbitrary mask
+ tun_metadata33: arbitrary mask
+ tun_metadata34: arbitrary mask
+ tun_metadata35: arbitrary mask
+ tun_metadata36: arbitrary mask
+ tun_metadata37: arbitrary mask
+ tun_metadata38: arbitrary mask
+ tun_metadata39: arbitrary mask
+ tun_metadata40: arbitrary mask
+ tun_metadata41: arbitrary mask
+ tun_metadata42: arbitrary mask
+ tun_metadata43: arbitrary mask
+ tun_metadata44: arbitrary mask
+ tun_metadata45: arbitrary mask
+ tun_metadata46: arbitrary mask
+ tun_metadata47: arbitrary mask
+ tun_metadata48: arbitrary mask
+ tun_metadata49: arbitrary mask
+ tun_metadata50: arbitrary mask
+ tun_metadata51: arbitrary mask
+ tun_metadata52: arbitrary mask
+ tun_metadata53: arbitrary mask
+ tun_metadata54: arbitrary mask
+ tun_metadata55: arbitrary mask
+ tun_metadata56: arbitrary mask
+ tun_metadata57: arbitrary mask
+ tun_metadata58: arbitrary mask
+ tun_metadata59: arbitrary mask
+ tun_metadata60: arbitrary mask
+ tun_metadata61: arbitrary mask
+ tun_metadata62: arbitrary mask
+ tun_metadata63: arbitrary mask
+ metadata: arbitrary mask
+ in_port: exact match or wildcard
+ in_port_oxm: exact match or wildcard
+ actset_output: exact match or wildcard
+ pkt_mark: arbitrary mask
+ reg0: arbitrary mask
+ reg1: arbitrary mask
+ reg2: arbitrary mask
+ reg3: arbitrary mask
+ reg4: arbitrary mask
+ reg5: arbitrary mask
+ reg6: arbitrary mask
+ reg7: arbitrary mask
+ xreg0: arbitrary mask
+ xreg1: arbitrary mask
+ xreg2: arbitrary mask
+ xreg3: arbitrary mask
+ eth_src: arbitrary mask
+ eth_dst: arbitrary mask
+ eth_type: exact match or wildcard
+ vlan_tci: arbitrary mask
+ vlan_vid: arbitrary mask
+ vlan_pcp: exact match or wildcard
+ mpls_label: exact match or wildcard
+ mpls_tc: exact match or wildcard
+ mpls_bos: exact match or wildcard
+ ip_src: arbitrary mask
+ ip_dst: arbitrary mask
+ ipv6_src: arbitrary mask
+ ipv6_dst: arbitrary mask
+ ipv6_label: arbitrary mask
+ nw_proto: exact match or wildcard
+ nw_tos: exact match or wildcard
+ ip_dscp: exact match or wildcard
+ nw_ecn: exact match or wildcard
+ nw_ttl: exact match or wildcard
+ ip_frag: arbitrary mask
+ arp_op: exact match or wildcard
+ arp_spa: arbitrary mask
+ arp_tpa: arbitrary mask
+ arp_sha: arbitrary mask
+ arp_tha: arbitrary mask
+ tcp_src: arbitrary mask
+ tcp_dst: arbitrary mask
+ tcp_flags: arbitrary mask
+ udp_src: arbitrary mask
+ udp_dst: arbitrary mask
+ sctp_src: arbitrary mask
+ sctp_dst: arbitrary mask
+ icmp_type: exact match or wildcard
+ icmp_code: exact match or wildcard
+ icmpv6_type: exact match or wildcard
+ icmpv6_code: exact match or wildcard
+ nd_target: arbitrary mask
+ nd_sll: arbitrary mask
+ nd_tll: arbitrary mask
+
+' $1
+}
+ditto() {
+ printf ' table %d ("%s"):
+ metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+ eviction: not supported
+ vacancy events: not supported
+ features: none
+ max_entries=%d
+ instructions (table miss and others):
+ next tables: %d-253
+ (same instructions)
+ (same actions)
+ (same matching)
+
+' $1 $2 $3 `expr $1 + 1`
+}
+tail_tables() {
+echo ' table 252 ("table252"):
+ metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+ eviction: not supported
+ vacancy events: not supported
+ features: none
+ max_entries=1000000
+ instructions (table miss and others):
+ next tables: 253
+ (same instructions)
+ (same actions)
+ (same matching)
+
+ table 253 ("table253"):
+ metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+ eviction: not supported
+ vacancy events: not supported
+ features: none
+ max_entries=1000000
+ instructions (table miss and others):
+ instructions: meter,apply_actions,clear_actions,write_actions,write_metadata
+ (same actions)
+ (same matching)
+'
+}
+first_egress_table() {
+echo ' table 251 ("table251"):
+ metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+ eviction: not supported
+ vacancy events: not supported
+ features: first egress table
+ max_entries=1000000
+ instructions (table miss and others):
+ next tables: 252-253
+ (same instructions)
+ Write-Actions features:
+ actions: set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+ supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metad
ata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll
+ Apply-Actions features:
+ actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+ supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metad
ata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll
+ (same matching)
+'
+}
+(head_table classifier
+ for i in `seq 1 251`; do
+ ditto $i table$i 1000000
+ done
+ tail_tables) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0], [0], [expout])
+# Set first egress table.
+ovs-ofctl -O Openflow15 set-first-egress-table br0 251
+
+# Check that the configuration was updated.
+(head_table classifier
+ for i in `seq 1 250`; do
+ ditto $i table$i 1000000
+ done
+ first_egress_table
+ tail_tables) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0], [0], [expout])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+ 1654,30 40%
AT_SETUP([ofproto - table description (OpenFlow 1.4)])
OVS_VSWITCHD_START
(x=0
@@ -338,6 +338,7 @@ usage(void)
" dump-desc SWITCH print switch description\n"
" dump-tables SWITCH print table stats\n"
" dump-table-features SWITCH print table features\n"
+ " set-first-egress-table SWITCH TABLE set first egress table\n"
" dump-table-desc SWITCH print table description (OF1.4+)\n"
" mod-port SWITCH IFACE ACT modify port behavior\n"
" mod-table SWITCH MOD modify flow table behavior\n"
@@ -728,9 +729,9 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
{
struct ofpbuf *request;
struct vconn *vconn;
-
+ struct ofputil_table_features *tf = NULL;
open_vconn(ctx->argv[1], &vconn);
- request = ofputil_encode_table_features_request(vconn_get_version(vconn));
+ request = ofputil_encode_table_features_request(tf, vconn_get_version(vconn));
/* The following is similar to dump_trivial_stats_transaction(), but it
* maintains the previous 'ofputil_table_features' from one stats reply
@@ -803,6 +804,35 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
}
static void
+ofctl_set_first_egress_table(struct ovs_cmdl_context *ctx)
+{
+ uint32_t usable_versions;
+ struct ofputil_table_features tf;
+ struct vconn *vconn;
+ char *error;
+
+ error = parse_ofp_table_features(&tf, ctx->argv[2], &usable_versions);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ uint32_t allowed_versions = get_allowed_ofp_versions();
+ if (!(allowed_versions & usable_versions)) {
+ struct ds versions = DS_EMPTY_INITIALIZER;
+ ofputil_format_version_bitmap_names(&versions, allowed_versions);
+ ovs_fatal(0, "set_first_egress_table '%s' requires one of the OpenFlow "
+ "versions %s but none is enabled (use -O)",
+ ctx->argv[2], ds_cstr(&versions));
+ }
+ mask_allowed_ofp_versions(usable_versions);
+
+ open_vconn(ctx->argv[1], &vconn);
+ transact_noreply(vconn, ofputil_encode_table_features_request(&tf,
+ vconn_get_version(vconn)));
+ vconn_close(vconn);
+}
+
+static void
ofctl_dump_table_desc(struct ovs_cmdl_context *ctx)
{
struct ofpbuf *request;
@@ -3694,6 +3724,8 @@ static const struct ovs_cmdl_command all_commands[] = {
1, 1, ofctl_dump_tables },
{ "dump-table-features", "switch",
1, 1, ofctl_dump_table_features },
+ { "set-first-egress-table", "switch table",
+ 2, 2, ofctl_set_first_egress_table },
{ "dump-table-desc", "switch",
1, 1, ofctl_dump_table_desc },
{ "dump-flows", "switch",