[ovs-dev] [PATCH v5 3/5] ofp-util: Implement OFPMP_TABLE_FEATURES en/decode

Alexander Wu alexander.wu at huawei.com
Wed Dec 18 09:48:41 UTC 2013


v5:
  Remove ofp-util.def from this patch and merge it ofp-actions patch.
  Fix %zu to PRIuSIZE according to CodingStyle
  Fix different return type in func ovs_instruction_type_from_inst_type
  Remove unused function ofp_print_not_implemented.
  Use OVS_NOT_REACHED now

v4.3:
  Rollback table id 255 to 253 according to OpenFlow spec and current implement
  Update AT.

v4.2:
  Fix last table features id to 255.
  Add more comments.
  Fix wrong print of last table feature.

v4:
  1. Delete duplication code.
  2. Add new acts in *.def.
  3. Update abstract table-features to bitmap.
  4. Make decode_openflow13_props more general.
  5. Update tests. Correct the wriable oxms.

v3:
  1. Update names of functions/macros to make them meaningful.
  2. Fix codingstyle.
  3. Remove useless logic/struct/function.
  4. Make printable messages more friendly.
  5. Add OVS_ACTIONS macro to display all action features.
  6. Modify type of element_size and print error if 0.
  7. Change print of next_tables msg, change enums to OFPUTIL_*.
  8. Make all prints human-readable.
  9. Update printable messages: instruction/action/oxm/next_table.
  10. Update action features, now the actions are correct.

v2:
  Restructure implement of OFPMP_TABLE_FEATURES
  Change decode_*_raw to normalized pull functions

  1. add defines and funcs to decode table features
  2. restructure OFPMP_TABLE_FEATURES decode function
     restructure the function, now them acts like others.
  3. Change big array to defines.
     Change big array to defines.(oxm, table_feature_prop)
     Fix some names, now they're more meaningful.
  4. use macros to restructure implement
  5. Restructure get_* to more effective ones. (table_features, oxm)
  6. remove useless array and prototype
  7. Fix CodingStyle accoring to Simon Horman's suggestions.
  8. Fix print of NEXT_TABLE_MISS.

Simon Horman's suggestions:
  Fix function paras alignment.
  Fix hard coding to marco.
  Fix VLOG calls with rl.
  Fix CodingStyle:
    max chars per line - 79
  Delete useless blank line.
  Restructure implement by macro.

v1:
  ofp-util: Implement the encode/decode Table Features functions

  1. Implement the encode/decode table features msgs function, and
     NOTE that we implement the decode functions *_raw, maybe we
     should change it the ofpbuf_pull?
  2. Add function to print OFPMP_TABLE_FEATURES.
     But now the print is crude and dirty.
     Fix it to bitmap or more desc later.
  3. Implement the at for OFPMP_TABLE_FEATURES.
     (I've tested it via NOX-OF1.3 too.)

Signed-off-by: Alexander Wu <alexander.wu at huawei.com>
---
 lib/ofp-print.c    |  168 ++++++++++++++-
 lib/ofp-util.c     |  623 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/ofp-util.h     |  130 +++++++++++
 tests/ofp-print.at |  172 +++++++++++++++
 tests/ofproto.at   |   25 ++
 5 files changed, 1111 insertions(+), 7 deletions(-)

diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 0f7278c..bc999c8 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -47,6 +47,7 @@
 #include "type-props.h"
 #include "unaligned.h"
 #include "util.h"
+#include "bitmap.h"
 
 static void ofp_print_queue_name(struct ds *string, uint32_t port);
 static void ofp_print_error(struct ds *, enum ofperr);
@@ -2285,12 +2286,6 @@ ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw,
 }
 
 static void
-ofp_print_not_implemented(struct ds *string)
-{
-    ds_put_cstr(string, "NOT IMPLEMENTED YET!\n");
-}
-
-static void
 ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
                 struct list *p_buckets)
 {
@@ -2485,6 +2480,165 @@ ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
     ofp_print_group(s, gm.group_id, gm.type, &gm.buckets);
 }
 
+/* Appends a string representation of 'prop' to 's'. */
+static void
+table_feature_prop_format(enum ovs_tfprop_type type,
+                          const struct bitmap *bitmap,
+                          struct ds *s)
+{
+    size_t i = 0;
+    enum ofperr error;
+
+    ds_put_format(s, "%s: ", table_feature_prop_get_name(
+                               table_feature_prop_encode_type(type)));
+
+    switch (type) {
+    case OVSTFPROP_OFPTFPT13_INSTRUCTIONS:
+    case OVSTFPROP_OFPTFPT13_INSTRUCTIONS_MISS: {
+        enum ovs_instruction_type instruction_type;
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            error = ovs_instruction_type_from_inst_type(&instruction_type, i);
+            if (error) {
+                ofp_print_error(s, error);
+                continue;
+            }
+            ds_put_format(s, "%s,",
+                ovs_instruction_name_from_type(instruction_type));
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_NEXT_TABLES:
+    case OVSTFPROP_OFPTFPT13_NEXT_TABLES_MISS: {
+        uint32_t table_start = OFPTT_MAX;
+        uint32_t table_end = 0;
+
+        /* Currently we're sure the table is continuous. So it's enough to
+         * print the start and end of the next tables only. */
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            if (i < table_start) {
+                table_start = i;
+            }
+            if (i > table_end) {
+                table_end = i;
+            }
+        }
+
+        if (table_start < table_end) {
+            ds_put_format(s, "%"PRIu32" to %"PRIu32"", table_start, table_end);
+        } else if (table_start == table_end) {
+            ds_put_format(s, "%"PRIu32, table_start);
+        } else if (bitmap_count1(bitmap->map, bitmap->end) == 0) {
+            ds_put_format(s, "There is no next-table.");
+        } else {
+            ofp_print_error(s, OFPERR_OFPTFFC_BAD_ARGUMENT);
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_WRITE_ACTIONS:
+    case OVSTFPROP_OFPTFPT13_WRITE_ACTIONS_MISS:
+    case OVSTFPROP_OFPTFPT13_APPLY_ACTIONS:
+    case OVSTFPROP_OFPTFPT13_APPLY_ACTIONS_MISS: {
+        enum ofp13_action_type action_type;
+
+        BITMAP_FOR_EACH_1(action_type, bitmap->end, bitmap->map) {
+            const char *action_name = NULL;
+            action_name = ofputil_action_name_from_code(
+                ofputil_action_code_from_ofp13_action(action_type));
+            ds_put_format(s, "%s,", action_name);
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_MATCH:
+    case OVSTFPROP_OFPTFPT13_WILDCARDS: {
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            const struct mf_field *mf = mf_from_id(i);
+            ds_put_format(s, "%s", mf->name);
+            if (mf->maskable) {
+                ds_put_format(s, ":%d", 1);
+            }
+            ds_put_format(s, ",");
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_WRITE_SETFIELD:
+    case OVSTFPROP_OFPTFPT13_WRITE_SETFIELD_MISS:
+    case OVSTFPROP_OFPTFPT13_APPLY_SETFIELD:
+    case OVSTFPROP_OFPTFPT13_APPLY_SETFIELD_MISS: {
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            const struct mf_field *mf = mf_from_id(i);
+            ds_put_format(s, "%s", mf->name);
+            ds_put_format(s, ",");
+        }
+        break;
+    }
+    default:
+        ds_put_format(s, "unknown(%u)", table_feature_prop_encode_type(type));
+        break;
+    }
+}
+
+static void
+ofp_print_table_features_stats_single(struct ds *s,
+                                      const struct ofputil_table_features *tf)
+{
+    int i;
+
+    ds_put_format(s, "\n  %"PRIu8":", tf->table_id);
+    ds_put_format(s, " name:%s", tf->name);
+    ds_put_format(s, " metadata_match:%"PRIx64, tf->metadata_match);
+    ds_put_format(s, " metadata_write:%"PRIx64, tf->metadata_write);
+    ds_put_format(s, " config:%"PRIx32, tf->config);
+    ds_put_format(s, " max_entries:%"PRIu32, tf->max_entries);
+    ds_put_format(s, "\n    Properties:");
+
+    for (i = 0; i < N_OVS_TFPROPS; i++) {
+        if (!tf->bitmaps[i].map) {
+            continue;
+        }
+
+        ds_put_format(s, "\n      ");
+        table_feature_prop_format(i, &tf->bitmaps[i], s);
+    }
+    ds_put_format(s, "\n");
+}
+
+static void
+ofp_print_table_features_stats(struct ds *s, const struct ofp_header *oh)
+{
+    int i;
+    int j;
+    enum ofperr error;
+    uint32_t flag;
+
+    struct ofputil_table_features tfs[OFPTT_MAX];
+    int tfs_num;
+    const int temp_bitmap_size = OFPTT_ALL;
+
+    memset(tfs, 0, sizeof(tfs));
+
+    for (i = 0; i < OFPTT_MAX; i++) {
+        for (j = 0; j < N_OVS_TFPROPS; j++) {
+            tfs[i].bitmaps[j].map = bitmap_allocate(temp_bitmap_size);
+            tfs[i].bitmaps[j].end = temp_bitmap_size;
+        }
+    }
+
+    error = ofputil_pull_table_features(oh, &tfs_num, tfs, &flag);
+    if (error) {
+        ofp_print_error(s, error);
+    }
+
+    for (i = 0; i < tfs_num; i++) {
+        ofp_print_table_features_stats_single(s, &tfs[i]);
+    }
+
+    for (i = 0; i < OFPTT_MAX; i++) {
+        for (j = 0; j < N_OVS_TFPROPS; j++) {
+            bitmap_free(tfs[i].bitmaps[j].map);
+        }
+    }
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
                 struct ds *string, int verbosity)
@@ -2525,7 +2679,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
 
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
-        ofp_print_not_implemented(string);
+        ofp_print_table_features_stats(string, oh);
         break;
 
     case OFPTYPE_HELLO:
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index a0a372f..d0dcb90 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -42,6 +42,7 @@
 #include "unaligned.h"
 #include "type-props.h"
 #include "vlog.h"
+#include "bitmap.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_util);
 
@@ -4156,6 +4157,608 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
     return b;
 }
 
+static enum ofperr
+tfprop_count_n_elem(uint32_t *n_elem, uint16_t length, uint16_t elem_size)
+{
+    int n = 0;
+    if (length % elem_size)
+        return OFPERR_OFPTFFC_BAD_LEN;
+
+    n = length / elem_size;
+    *n_elem = n;
+    return 0;
+}
+
+enum ofp13_table_feature_prop_type
+table_feature_prop_encode_type(enum ovs_tfprop_type type)
+{
+    switch(type) {
+
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME)  \
+    case OVSTFPROP_##ENUM:                      \
+        return ENUM;
+OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+
+    default:
+        return -1;
+    }
+}
+
+static enum ovs_tfprop_type
+table_feature_prop_decode_type(enum ofp13_table_feature_prop_type type)
+{
+    switch (type) {
+
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME)  \
+    case ENUM:                                  \
+        return OVSTFPROP_##ENUM;
+OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+
+    case OFPTFPT13_EXPERIMENTER:
+    case OFPTFPT13_EXPERIMENTER_MISS:
+    default:
+        return -1;
+    }
+}
+
+char *table_feature_prop_get_name(enum ofp13_table_feature_prop_type type)
+{
+    switch (type) {
+
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME)  \
+    case ENUM:                                  \
+        return NAME;
+OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+
+    case OFPTFPT13_EXPERIMENTER:
+    case OFPTFPT13_EXPERIMENTER_MISS:
+    default:
+        return NULL;
+    }
+}
+
+uint16_t table_feature_prop_get_size(enum ofp13_table_feature_prop_type type)
+{
+    switch (type) {
+
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME)  \
+    case ENUM:                                  \
+        return sizeof(STRUCT);
+OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+
+    case OFPTFPT13_EXPERIMENTER:
+    case OFPTFPT13_EXPERIMENTER_MISS:
+    default:
+        return 0;
+    }
+}
+
+#define PROP_ALIGN 8
+#define MULTIPART_HDR_LEN 8 /* TYPE:2 FLAG:2 PADDING:4 */
+#define MULTIPART_ALIGN 8
+
+static int
+prop_is_valid(const struct ofp_prop_header *prop, size_t n_prop)
+{
+    uint16_t len = ntohs(prop->length);
+    return (len >= sizeof *prop
+            && (ROUND_UP(len, PROP_ALIGN) / PROP_ALIGN) <= n_prop);
+}
+
+static inline struct ofp_prop_header *
+prop_next(const struct ofp_prop_header *prop)
+{
+    return ((struct ofp_prop_header *) (void *)
+            ((uint8_t *) prop + ROUND_UP(ntohs(prop->length), PROP_ALIGN)));
+}
+
+/* This macro is careful to check for props with bad lengths. */
+#define PROP_FOR_EACH(ITER, LEFT, PROPS, N_PROPS)                     \
+    for ((ITER) = (PROPS), (LEFT) = (N_PROPS);                        \
+         (LEFT) > 0 && prop_is_valid(ITER, LEFT);                     \
+         ((LEFT) -= ROUND_UP(ntohs((ITER)->length), PROP_ALIGN)       \
+                     / PROP_ALIGN,                                    \
+          (ITER) = prop_next(ITER)))
+
+#define OPROP_DATA(OPROP) ((void *)(OPROP + 1))
+static enum ofperr
+trans_openflow13_tfprop(const struct ofp_prop_header *oprop,
+                        struct ofputil_table_features *tf)
+{
+    uint32_t data_len;
+
+    enum ofperr error = 0;
+    uint32_t element_size = 0;
+    uint32_t n_elem = 0;
+    int i;
+
+    enum ovs_tfprop_type prop_type
+        = table_feature_prop_decode_type(ntohs(oprop->type));
+    uint16_t prop_len = ntohs(oprop->length);
+    data_len = prop_len - sizeof *oprop;
+    element_size = table_feature_prop_get_size(ntohs(oprop->type));
+    if (element_size == 0) {
+        return OFPERR_OFPTFFC_BAD_TYPE;
+    } else if (data_len % element_size) {
+        return OFPERR_OFPTFFC_BAD_LEN;
+    }
+    error = tfprop_count_n_elem(&n_elem, data_len, element_size);
+    if (error) {
+        return error;
+    }
+
+    switch (prop_type) {
+    case OVSTFPROP_OFPTFPT13_INSTRUCTIONS:
+    case OVSTFPROP_OFPTFPT13_INSTRUCTIONS_MISS: {
+        struct ofp11_instruction *oinst = OPROP_DATA(oprop);
+        for (i = 0; i < n_elem; i++) {
+            bitmap_set1(tf->bitmaps[prop_type].map, ntohs(oinst[i].type));
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_NEXT_TABLES:
+    case OVSTFPROP_OFPTFPT13_NEXT_TABLES_MISS: {
+        uint8_t *ontable = OPROP_DATA(oprop);
+        for (i = 0; i < n_elem; i++) {
+            bitmap_set1(tf->bitmaps[prop_type].map, ontable[i]);
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_WRITE_ACTIONS:
+    case OVSTFPROP_OFPTFPT13_WRITE_ACTIONS_MISS:
+    case OVSTFPROP_OFPTFPT13_APPLY_ACTIONS:
+    case OVSTFPROP_OFPTFPT13_APPLY_ACTIONS_MISS: {
+        const struct ofp_action_header *oact = OPROP_DATA(oprop);
+        for (i = 0; i < n_elem; i++) {
+            bitmap_set1(tf->bitmaps[prop_type].map, ntohs(oact[i].type));
+        }
+        break;
+    }
+    case OVSTFPROP_OFPTFPT13_MATCH:
+    case OVSTFPROP_OFPTFPT13_WILDCARDS:
+    case OVSTFPROP_OFPTFPT13_WRITE_SETFIELD:
+    case OVSTFPROP_OFPTFPT13_WRITE_SETFIELD_MISS:
+    case OVSTFPROP_OFPTFPT13_APPLY_SETFIELD:
+    case OVSTFPROP_OFPTFPT13_APPLY_SETFIELD_MISS: {
+        const struct mf_field *mf;
+        uint32_t header;
+        const ovs_be32 *be32 = OPROP_DATA(oprop);
+        for (i = 0; i < n_elem; i++) {
+            header = ntohl(be32[i]);
+            mf = mf_from_nxm_header(header);
+            if (!mf) {
+                VLOG_INFO_RL(&bad_ofmsg_rl, "Bad oxm header %"PRIx32" with "
+                    "offset %d in table tf properties.", header, i);
+                continue;
+            }
+            bitmap_set1(tf->bitmaps[prop_type].map, mf->id);
+        }
+        break;
+    }
+    default:
+        return OFPERR_OFPTFFC_BAD_TYPE;
+    }
+    return 0;
+}
+
+static enum ofperr
+table_feature_check_len(const struct ofp13_table_features *feature)
+{
+    uint16_t len = ntohs(feature->length);
+
+    if (len < sizeof *feature || len % 8) {
+        return OFPERR_OFPTFFC_BAD_LEN;
+    }
+    return 0;
+}
+
+static void
+table_feature_get_id(const struct ofp13_table_features *feature,
+                     uint8_t *table_id)
+{
+    *table_id = feature->table_id;
+}
+
+static int
+table_feature_is_valid(const struct ofp13_table_features *feature,
+                       size_t n_feature)
+{
+    uint16_t len = ntohs(feature->length);
+    return (!(len % 8)
+            && len >= (sizeof *feature)
+            && (len / sizeof *feature) <= n_feature);
+}
+
+static inline struct ofp13_table_features *
+table_feature_next(const struct ofp13_table_features *feature)
+{
+    return ((struct ofp13_table_features *) (void *)
+            ((uint8_t *) feature + ntohs(feature->length)));
+}
+
+/* This macro is careful to check for props with bad lengths. */
+#define TABLE_FEATURE_FOR_EACH(ITER, LEFT, FEATURES, N_FEATURES)        \
+    for ((ITER) = (FEATURES), (LEFT) = (N_FEATURES);                    \
+         (LEFT) > 0 && table_feature_is_valid(ITER, LEFT);              \
+         ((LEFT) -= (ntohs((ITER)->length)                              \
+                     / MULTIPART_ALIGN),                                \
+          (ITER) = table_feature_next(ITER)))
+
+static enum ofperr
+decode_openflow13_table_features(const struct ofp13_table_features features[],
+                                 size_t n_features,
+                                 const struct ofp13_table_features *out[])
+{
+    const struct ofp13_table_features *feature;
+    size_t left;
+
+    TABLE_FEATURE_FOR_EACH (feature, left, features, n_features) {
+        uint8_t table_id;
+        enum ofperr error;
+
+        error = table_feature_check_len(feature);
+        if (error) {
+            return error;
+        }
+
+        table_feature_get_id(feature, &table_id);
+
+        if (out[table_id]) {
+            return OFPERR_OFPTFFC_BAD_TABLE;
+        }
+        out[table_id] = feature;
+    }
+
+    if (left) {
+        VLOG_WARN("Bad table features format at offset %"PRIuSIZE".",
+                     (n_features - left) * sizeof *feature);
+        return OFPERR_OFPTFFC_BAD_LEN;
+    }
+    return 0;
+}
+
+static enum ofperr
+decode_openflow13_table_feature_prop(const struct ofp_prop_header *prop,
+                                     uint16_t *type)
+{
+    uint16_t len = ntohs(prop->length);
+    uint16_t data_len = len > sizeof *prop ? len - sizeof *prop : 0;
+
+    switch (prop->type) {
+    case CONSTANT_HTONS(OFPTFPT13_EXPERIMENTER):
+    case CONSTANT_HTONS(OFPTFPT13_EXPERIMENTER_MISS):
+        return OFPERR_OFPTFFC_BAD_ARGUMENT;
+
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME)      \
+    case CONSTANT_HTONS(ENUM):                      \
+        if (!(data_len % sizeof(STRUCT))) {         \
+            *type = OVSTFPROP_##ENUM;               \
+            return 0;                               \
+        } else {                                    \
+            return OFPERR_OFPBIC_BAD_LEN;           \
+        }
+OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+
+    default:
+        return OFPERR_OFPTFFC_BAD_TYPE;
+    }
+}
+
+enum ofperr
+decode_openflow13_props(const struct ofp_prop_header props[],
+                        size_t n_props,
+                        const struct ofp_prop_header *out[],
+                        enum ofperr (*decode_openflow13_prop)(
+                            const struct ofp_prop_header *, uint16_t *))
+{
+    const struct ofp_prop_header *prop;
+    size_t left;
+
+    PROP_FOR_EACH (prop, left, props, n_props) {
+        uint16_t type;
+        enum ofperr error;
+
+        error = decode_openflow13_prop(prop, &type);
+        if (error) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "Decode property failed:%d.", error);
+            return error;
+        }
+
+        if (out[type]) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "Duplicate property:%"PRIu16".", type);
+            return OFPERR_OFPBRC_BAD_TYPE;
+        }
+        out[type] = prop;
+    }
+
+    if (left) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "Bad property format at offset %"PRIuSIZE
+            ".", (n_props - left) * PROP_ALIGN);
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    return 0;
+}
+
+static enum ofperr
+translate_table_features(struct ofputil_table_features *tf,
+                         const struct ofp13_table_features *otf)
+{
+    enum ofperr error;
+    const struct ofp_prop_header *props;
+    uint16_t props_len;
+    const struct ofp_prop_header *ps[N_OVS_TFPROPS];
+    int i;
+
+    memset(ps, 0, N_OVS_TFPROPS * sizeof *ps);
+
+    tf->length = ntohs(otf->length);
+    tf->table_id = otf->table_id;
+    ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
+    tf->metadata_match = ntohll(otf->metadata_match);
+    tf->metadata_write = ntohll(otf->metadata_write);
+    tf->config = ntohl(otf->config);
+    tf->max_entries = ntohl(otf->max_entries);
+
+    props = (const struct ofp_prop_header*)(otf + 1);
+    props_len = ntohs(otf->length) - sizeof(*otf);
+    error = decode_openflow13_props(props, props_len / PROP_ALIGN, ps,
+                decode_openflow13_table_feature_prop);
+    if (error) {
+        return error;
+    }
+
+    for (i = 0; i < N_OVS_TFPROPS; i++) {
+        if (ps[i] == NULL) {
+            continue;
+        }
+
+        error = trans_openflow13_tfprop(ps[i], tf);
+        if (error) {
+            return error;
+        }
+    }
+
+    return 0;
+}
+
+enum ofperr
+ofputil_pull_table_features(const struct ofp_header *oh,
+                            int *tfs_num,
+                            struct ofputil_table_features tfs[],
+                            uint32_t *flag)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    struct ofpbuf msg;
+    enum ofpraw raw;
+
+    const struct ofp13_table_features *features;
+    const struct ofp13_table_features *fs[OFPTT_ALL];
+
+    uint32_t features_len;
+    int i;
+    int tfs_cursor = 0;
+    int error = 0;
+
+    memset(fs, 0, sizeof fs);
+
+    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&msg);
+    if (raw != OFPRAW_OFPST13_TABLE_FEATURES_REQUEST
+        && raw != OFPRAW_OFPST13_TABLE_FEATURES_REPLY) {
+        VLOG_DBG_RL(&rl, "Bad openflow msg type(%u).\n", raw);
+        return OFPERR_OFPBRC_BAD_TYPE;
+    }
+
+    if (msg.size == 0) /* stands for GET table_features request */ {
+        *flag = OVS_TF_GET;
+        return 0;
+    }
+
+    /* Else if request contains body, then it's a SET table_features request */
+    *flag = OVS_TF_SET;
+
+    features_len = (ntohs(oh->length) - sizeof(*oh) - MULTIPART_HDR_LEN);
+    features = ofpbuf_try_pull(&msg, features_len);
+    if (features == NULL) {
+        VLOG_WARN_RL(&rl, "Table features length %u is longer than space "
+            "in message length %"PRIuSIZE".", features_len, msg.size);
+        return OFPERR_OFPTFFC_BAD_LEN;
+    }
+
+    error = decode_openflow13_table_features(
+        features, features_len / MULTIPART_ALIGN, fs);
+    if (error) {
+        return error;
+    }
+
+    for (i = 0; i < OFPTT_MAX; i++) {
+        if (fs[i] == NULL) {
+            continue;
+        }
+
+        translate_table_features(&tfs[tfs_cursor++], fs[i]);
+    }
+
+    *tfs_num = tfs_cursor;
+    return error;
+}
+
+static enum ofperr
+encode_openflow13_tfprop(struct ofpbuf *reply,
+                         enum ovs_tfprop_type type,
+                         const struct bitmap *bitmap)
+{
+    size_t i;
+    uint32_t prop_type;
+    uint16_t element_size;
+    uint32_t data_len;
+    uint32_t padding_len;
+    uint32_t prop_len;
+    void *data;
+
+    uint32_t bit_count;
+    struct ofp13_table_feature_prop_header *oprop;
+
+    bit_count = bitmap_count1(bitmap->map, bitmap->end);
+    if (bit_count == 0) {
+        return 0;
+    }
+
+    oprop = ofpbuf_put_zeros(reply, sizeof *oprop);
+
+    prop_type = table_feature_prop_encode_type(type);
+    element_size = table_feature_prop_get_size(prop_type);
+    if (element_size == 0) {
+        return OFPERR_OFPTFFC_BAD_TYPE;
+    }
+
+    data_len = bit_count * element_size;
+    prop_len = data_len + sizeof *oprop;
+    padding_len = ROUND_UP(prop_len, PROP_ALIGN) - prop_len;
+
+    oprop->type = htons(prop_type);
+    oprop->length = htons(prop_len);
+
+    data = ofpbuf_put_zeros(reply, data_len + padding_len);
+
+    switch (prop_type) {
+    case OFPTFPT13_INSTRUCTIONS:
+    case OFPTFPT13_INSTRUCTIONS_MISS: {
+        struct ofp11_instruction *oinst = data;
+        int cursor = 0;
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            oinst[cursor].type = htons(i);
+            oinst[cursor].len = htons(8);
+            ++cursor;
+        }
+        break;
+    }
+    case OFPTFPT13_NEXT_TABLES:
+    case OFPTFPT13_NEXT_TABLES_MISS: {
+        uint8_t *ontable = data;
+        int cursor = 0;
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            ontable[cursor] = i + 1;
+            ++cursor;
+        }
+        break;
+    }
+    case OFPTFPT13_WRITE_ACTIONS:
+    case OFPTFPT13_WRITE_ACTIONS_MISS:
+    case OFPTFPT13_APPLY_ACTIONS:
+    case OFPTFPT13_APPLY_ACTIONS_MISS: {
+        struct ofp_action_header *oact = data;
+        int cursor = 0;
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            oact[cursor].type = htons(i);
+            oact[cursor].len = htons(8);
+            ++cursor;
+        }
+        break;
+    }
+    case OFPTFPT13_MATCH:
+    case OFPTFPT13_WILDCARDS:
+    case OFPTFPT13_WRITE_SETFIELD:
+    case OFPTFPT13_WRITE_SETFIELD_MISS:
+    case OFPTFPT13_APPLY_SETFIELD:
+    case OFPTFPT13_APPLY_SETFIELD_MISS: {
+        ovs_be32 *be32 = data;
+        int cursor = 0;
+        uint32_t i;
+        uint32_t oxm_header;
+        BITMAP_FOR_EACH_1(i, bitmap->end, bitmap->map) {
+            oxm_header = mf_from_id(i)->oxm_header;
+            if (!oxm_header) {
+                VLOG_WARN_RL(&bad_ofmsg_rl, "Bad OXM bit value %"PRIu32, i);
+                continue;
+            }
+
+            be32[cursor] = htonl(oxm_header);
+            ++cursor;
+        }
+        break;
+    }
+    case OFPTFPT13_EXPERIMENTER:
+    case OFPTFPT13_EXPERIMENTER_MISS:
+    default:
+        return OFPERR_OFPTFFC_BAD_ARGUMENT;
+    }
+
+    return 0;
+}
+
+static enum ofperr
+ofputil_encode_table_features_props(struct ofpbuf *reply,
+                                    const struct ofputil_table_features *tf)
+{
+    int i;
+
+    for (i = 0; i < N_OVS_TFPROPS; i++) {
+        encode_openflow13_tfprop(reply, i, &tf->bitmaps[i]);
+    }
+
+    return 0;
+}
+
+void
+ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
+                                    struct list *replies)
+{
+    struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+    size_t start_otf = reply->size;
+    enum ofpraw raw;
+
+    ofpraw_decode_partial(&raw, reply->data, reply->size);
+    if (raw == OFPRAW_OFPST13_TABLE_FEATURES_REPLY) {
+        struct ofp13_table_features *otf;
+
+        ofpbuf_put_zeros(reply, sizeof *otf);
+        ofputil_encode_table_features_props(reply, tf);
+
+        otf = ofpbuf_at_assert(reply, start_otf, sizeof *otf);
+        otf->length = htons(reply->size - start_otf);
+        otf->table_id = tf->table_id;
+        ovs_strlcpy(otf->name, tf->name, OFP_MAX_TABLE_NAME_LEN);
+        otf->metadata_match = htonll(tf->metadata_match);
+        otf->metadata_write = htonll(tf->metadata_write);
+        otf->config = htonl(tf->config);
+        otf->max_entries = htonl(tf->max_entries);
+    } else {
+        OVS_NOT_REACHED();
+    }
+    ofpmp_postappend(replies, start_otf);
+}
+
+/* Returns an OpenFlow group features request 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:
+        ovs_fatal(0, "dump-table-features needs OpenFlow 1.2 or later "
+                     "(\'-O OpenFlow12\')");
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
+                               ofp_version, 0);
+        break;
+    }
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    return request;
+}
+
 /* ofputil_table_mod */
 
 /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
@@ -5178,6 +5781,7 @@ static const char *const names[OFPUTIL_N_ACTIONS] = {
     NULL,
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)             NAME,
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)   NAME,
 #include "ofp-util.def"
 };
@@ -5207,6 +5811,21 @@ ofputil_action_name_from_code(enum ofputil_action_code code)
         : "Unknown action";
 }
 
+enum ofputil_action_code
+ofputil_action_code_from_ofp13_action(enum ofp13_action_type type)
+{
+    switch (type) {
+
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)  \
+    case ENUM:                                          \
+        return OFPUTIL_##ENUM;
+#include "ofp-util.def"
+
+    default:
+        return OFPUTIL_ACTION_INVALID;
+    }
+}
+
 /* Appends an action of the type specified by 'code' to 'buf' and returns the
  * action.  Initializes the parts of 'action' that identify it as having type
  * <ENUM> and length 'sizeof *action' and zeros the rest.  For actions that
@@ -5217,6 +5836,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
 {
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
         OVS_NOT_REACHED();
 
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)                  \
@@ -5248,6 +5869,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
     }
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
     OFPAT10_ACTION(ENUM, STRUCT, NAME)
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+    OFPAT10_ACTION(ENUM, STRUCT, NAME)
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
     void                                                        \
     ofputil_init_##ENUM(struct STRUCT *s)                       \
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index bf02b9f..bc75701 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -599,6 +599,129 @@ enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
 struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
                                        enum ofputil_protocol);
 
+enum ovs_table_features_flag {
+    OVS_TF_GET = (1 << 0),
+    OVS_TF_SET = (1 << 1)
+};
+
+#define OVS_TABLE_FEATURE_PROPS                         \
+    DEFINE_TFPROP(OFPTFPT13_INSTRUCTIONS,               \
+         struct ofp11_instruction, instruction_array,   \
+         "Instruction")                                 \
+    DEFINE_TFPROP(OFPTFPT13_INSTRUCTIONS_MISS,          \
+         struct ofp11_instruction, instruction_array,   \
+         "Instruction miss")                            \
+    DEFINE_TFPROP(OFPTFPT13_NEXT_TABLES,                \
+         uint8_t, next_table_array,                     \
+         "Next tables")                                 \
+    DEFINE_TFPROP(OFPTFPT13_NEXT_TABLES_MISS,           \
+         uint8_t, next_table_array,                     \
+         "Next tables miss")                            \
+    DEFINE_TFPROP(OFPTFPT13_WRITE_ACTIONS,              \
+         struct ofp_action_header, action_array,        \
+         "Write actions")                               \
+    DEFINE_TFPROP(OFPTFPT13_WRITE_ACTIONS_MISS,         \
+         struct ofp_action_header, action_array,        \
+         "Write actions miss")                          \
+    DEFINE_TFPROP(OFPTFPT13_APPLY_ACTIONS,              \
+         struct ofp_action_header, action_array,        \
+         "Apply actions")                               \
+    DEFINE_TFPROP(OFPTFPT13_APPLY_ACTIONS_MISS,         \
+         struct ofp_action_header, action_array,        \
+         "Apply actions miss")                          \
+    DEFINE_TFPROP(OFPTFPT13_MATCH,                      \
+         ovs_be32, oxm_array,                           \
+         "Match")                                       \
+    DEFINE_TFPROP(OFPTFPT13_WILDCARDS,                  \
+         ovs_be32, oxm_array,                           \
+         "Wildcards")                                   \
+    DEFINE_TFPROP(OFPTFPT13_WRITE_SETFIELD,             \
+         ovs_be32, oxm_array_writable,                  \
+         "Write setfield")                              \
+    DEFINE_TFPROP(OFPTFPT13_WRITE_SETFIELD_MISS,        \
+         ovs_be32, oxm_array_writable,                  \
+         "Write setfield miss")                         \
+    DEFINE_TFPROP(OFPTFPT13_APPLY_SETFIELD,             \
+         ovs_be32, oxm_array_writable,                  \
+         "Apply setfield")                              \
+    DEFINE_TFPROP(OFPTFPT13_APPLY_SETFIELD_MISS,        \
+         ovs_be32, oxm_array_writable,                  \
+         "Apply setfield miss")
+
+enum {
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME) + 1
+    N_OVS_TFPROPS = OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+};
+
+enum ovs_tfprop_type {
+#define DEFINE_TFPROP(ENUM, STRUCT, TAG, NAME) OVSTFPROP_##ENUM,
+    OVS_TABLE_FEATURE_PROPS
+#undef DEFINE_TFPROP
+};
+
+struct bitmap {
+    uint32_t end;
+    unsigned long *map;
+};
+
+/* Common property header of network byte order. */
+struct ofp_prop_header {
+    ovs_be16 type;
+    ovs_be16 length;
+};
+
+/* Common property header of host byte order. */
+struct prop_header {
+    uint16_t type;
+    uint16_t length;
+};
+
+/* Abstract function to decode props.
+ * Implement the decode_openflow13_prop callback to use it. */
+enum ofperr
+decode_openflow13_props(const struct ofp_prop_header props[],
+                        size_t n_props,
+                        const struct ofp_prop_header *out[],
+                        enum ofperr (*decode_openflow13_prop)(
+                            const struct ofp_prop_header *, uint16_t *));
+
+/* Abstract ofp13_table_features */
+struct ofputil_table_features {
+    uint16_t length;          /* Length is padded to 64 bits. */
+    uint8_t table_id;         /* Identifier of table. Lower numbered tables
+                                 are consulted first. */
+    char name[OFP_MAX_TABLE_NAME_LEN];
+    uint64_t metadata_match;  /* Bits of metadata table can match. */
+    uint64_t metadata_write;  /* Bits of metadata table can write. */
+    uint32_t config;          /* Bitmap of OFPTC_* values */
+    uint32_t max_entries;     /* Max number of entries supported. */
+
+    /* The bitmaps stores table features OFPTFPT13_* properties.
+     * Here are valid bits:
+     *   Instructions: OFPIT11_GOTO_TABLE ~ OFPIT13_METER
+     *   Next tables: 0 ~ 254
+     *   Actions: OFPAT13_OUTPUT ~ OFPAT13_POP_PBB
+     *   OXMs(_writable): MFF_TUN_ID ~ MFF_N_IDS
+     */
+    struct bitmap bitmaps[N_OVS_TFPROPS];
+};
+
+enum ofp13_table_feature_prop_type table_feature_prop_encode_type(
+                                            enum ovs_tfprop_type type);
+enum ofperr ofputil_pull_table_features(const struct ofp_header *oh,
+                                        int *n,
+                                        struct ofputil_table_features tfs[],
+                                        uint32_t *flag);
+struct ofpbuf *ofputil_encode_table_features_request(
+                            enum ofp_version ofp_version);
+void ofputil_append_table_features_reply(
+                            const struct ofputil_table_features *tf,
+                            struct list *replies);
+
+uint16_t table_feature_prop_get_size(enum ofp13_table_feature_prop_type type);
+char *table_feature_prop_get_name(enum ofp13_table_feature_prop_type type);
+
 /* Meter band configuration for all supported band types. */
 struct ofputil_meter_band {
     uint16_t type;
@@ -848,6 +971,7 @@ enum OVS_PACKED_ENUM ofputil_action_code {
     OFPUTIL_ACTION_INVALID,
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)             OFPUTIL_##ENUM,
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)   OFPUTIL_##ENUM,
 #include "ofp-util.def"
 };
@@ -856,6 +980,7 @@ enum OVS_PACKED_ENUM ofputil_action_code {
 enum {
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)             + 1
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)   + 1
     OFPUTIL_N_ACTIONS = 1
 #include "ofp-util.def"
@@ -863,6 +988,8 @@ enum {
 
 int ofputil_action_code_from_name(const char *);
 const char * ofputil_action_name_from_code(enum ofputil_action_code code);
+enum ofputil_action_code ofputil_action_code_from_ofp13_action(
+    enum ofp13_action_type type);
 
 void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 
@@ -886,6 +1013,9 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)  \
     void ofputil_init_##ENUM(struct STRUCT *);          \
     struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)  \
+    void ofputil_init_##ENUM(struct STRUCT *);          \
+    struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)    \
     void ofputil_init_##ENUM(struct STRUCT *);          \
     struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 2f09c1b..685c97a 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -1978,6 +1978,178 @@ meter:2 flow_count:2 packet_in_count:512 byte_in_count:12288 duration:391.170094
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_TABLE_FEATURES request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 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], [dnl
+OFPST_TABLE_FEATURES reply (OF1.3) (xid=0xd5):
+  0: name:table0 metadata_match:ffffffffffffffff metadata_write:ffffffffffffffff config:3 max_entries:1000000
+    Properties:
+      Instruction: goto_table,write_metadata,write_actions,apply_actions,clear_actions,
+      Instruction miss: goto_table,write_metadata,write_actions,apply_actions,clear_actions,
+      Next tables: 1 to 253
+      Next tables miss: 1 to 253
+      Write actions: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Write actions miss: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Apply actions: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Apply actions miss: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Match: tun_id:1,tun_src:1,tun_dst:1,metadata:1,in_port,in_port_oxm,pkt_mark:1,reg0:1,reg1:1,reg2:1,reg3:1,reg4:1,reg5:1,reg6:1,reg7:1,eth_src:1,eth_dst:1,eth_type,vlan_tci:1,vlan_vid:1,vlan_pcp,mpls_label,mpls_tc,mpls_bos,ip_src:1,ip_dst:1,ipv6_src:1,ipv6_dst:1,ipv6_label:1,nw_proto,nw_tos,ip_dscp,nw_ecn,nw_ttl,ip_frag:1,arp_op,arp_spa:1,arp_tpa:1,arp_sha:1,arp_tha:1,tcp_src:1,tcp_dst:1,tcp_flags:1,udp_src:1,udp_dst:1,sctp_src:1,sctp_dst:1,icmp_type,icmp_code,icmpv6_type,icmpv6_code,nd_target:1,nd_sll:1,nd_tll:1,
+      Wildcards: tun_id:1,tun_src:1,tun_dst:1,metadata:1,in_port,in_port_oxm,pkt_mark:1,reg0:1,reg1:1,reg2:1,reg3:1,reg4:1,reg5:1,reg6:1,reg7:1,eth_src:1,eth_dst:1,eth_type,vlan_tci:1,vlan_vid:1,vlan_pcp,mpls_label,mpls_tc,mpls_bos,ip_src:1,ip_dst:1,ipv6_src:1,ipv6_dst:1,ipv6_label:1,nw_proto,nw_tos,ip_dscp,nw_ecn,nw_ttl,ip_frag:1,arp_op,arp_spa:1,arp_tpa:1,arp_sha:1,arp_tha:1,tcp_src:1,tcp_dst:1,tcp_flags:1,udp_src:1,udp_dst:1,sctp_src:1,sctp_dst:1,icmp_type,icmp_code,icmpv6_type,icmpv6_code,nd_target:1,nd_sll:1,nd_tll:1,
+      Write setfield: 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,
+      Write setfield miss: 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,
+      Apply setfield: 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,
+      Apply setfield miss: 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,
+])
+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
diff --git a/tests/ofproto.at b/tests/ofproto.at
index c410d29..ca0229a 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -358,6 +358,31 @@ OFPST_GROUP reply (OF1.1):
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - table feature - (OpenFlow 1.3)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-table-features br0], [0], [stdout])
+AT_CHECK([[grep -B 1 -C 15 -w table0 stdout]], [0], [dnl
+OFPST_TABLE_FEATURES reply (OF1.3) (xid=0x2):
+  0: name:table0 metadata_match:ffffffffffffffff metadata_write:ffffffffffffffff config:3 max_entries:1000000
+    Properties:
+      Instruction: goto_table,write_metadata,write_actions,apply_actions,clear_actions,
+      Instruction miss: goto_table,write_metadata,write_actions,apply_actions,clear_actions,
+      Next tables: 1 to 253
+      Next tables miss: 1 to 253
+      Write actions: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Write actions miss: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Apply actions: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Apply actions miss: output,copy_ttl_out,copy_ttl_in,set_mpls_ttl,dec_mpls_ttl,push_vlan,pop_vlan,push_mpls,pop_mpls,set_queue,group,set_nw_ttl,dec_nw_ttl,set_field,push_pbb,pop_pbb,
+      Match: tun_id:1,tun_src:1,tun_dst:1,metadata:1,in_port,in_port_oxm,pkt_mark:1,reg0:1,reg1:1,reg2:1,reg3:1,reg4:1,reg5:1,reg6:1,reg7:1,eth_src:1,eth_dst:1,eth_type,vlan_tci:1,vlan_vid:1,vlan_pcp,mpls_label,mpls_tc,mpls_bos,ip_src:1,ip_dst:1,ipv6_src:1,ipv6_dst:1,ipv6_label:1,nw_proto,nw_tos,ip_dscp,nw_ecn,nw_ttl,ip_frag:1,arp_op,arp_spa:1,arp_tpa:1,arp_sha:1,arp_tha:1,tcp_src:1,tcp_dst:1,tcp_flags:1,udp_src:1,udp_dst:1,sctp_src:1,sctp_dst:1,icmp_type,icmp_code,icmpv6_type,icmpv6_code,nd_target:1,nd_sll:1,nd_tll:1,
+      Wildcards: tun_id:1,tun_src:1,tun_dst:1,metadata:1,in_port,in_port_oxm,pkt_mark:1,reg0:1,reg1:1,reg2:1,reg3:1,reg4:1,reg5:1,reg6:1,reg7:1,eth_src:1,eth_dst:1,eth_type,vlan_tci:1,vlan_vid:1,vlan_pcp,mpls_label,mpls_tc,mpls_bos,ip_src:1,ip_dst:1,ipv6_src:1,ipv6_dst:1,ipv6_label:1,nw_proto,nw_tos,ip_dscp,nw_ecn,nw_ttl,ip_frag:1,arp_op,arp_spa:1,arp_tpa:1,arp_sha:1,arp_tha:1,tcp_src:1,tcp_dst:1,tcp_flags:1,udp_src:1,udp_dst:1,sctp_src:1,sctp_dst:1,icmp_type,icmp_code,icmpv6_type,icmpv6_code,nd_target:1,nd_sll:1,nd_tll:1,
+      Write setfield: 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,
+      Write setfield miss: 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,
+      Apply setfield: 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,
+      Apply setfield miss: 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,
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - mod-port (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 for command_config_state in \
-- 
1.7.3.1.msysgit.0





More information about the dev mailing list