summaryrefslogtreecommitdiff
path: root/lib/ofp-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ofp-util.c')
-rw-r--r--lib/ofp-util.c514
1 files changed, 379 insertions, 135 deletions
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 0b78fee1b..c6027301d 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -4606,6 +4606,8 @@ ofputil_decode_table_features(struct ofpbuf *msg,
struct ofp13_table_features *otf;
unsigned int len;
+ memset(tf, 0, sizeof *tf);
+
if (!msg->frame) {
ofpraw_pull_assert(msg);
}
@@ -4991,108 +4993,160 @@ ofputil_decode_role_status(const struct ofp_header *oh,
/* Table stats. */
+/* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be
+ * matched and a field that must be wildcarded. This function returns a bitmap
+ * that contains both kinds of fields. */
+static struct mf_bitmap
+wild_or_nonmatchable_fields(const struct ofputil_table_features *features)
+{
+ struct mf_bitmap wc = features->match;
+ bitmap_not(wc.bm, MFF_N_IDS);
+ bitmap_or(wc.bm, features->wildcard.bm, MFF_N_IDS);
+ return wc;
+}
+
+struct ofp10_wc_map {
+ enum ofp10_flow_wildcards wc10;
+ enum mf_field_id mf;
+};
+
+static const struct ofp10_wc_map ofp10_wc_map[] = {
+ { OFPFW10_IN_PORT, MFF_IN_PORT },
+ { OFPFW10_DL_VLAN, MFF_VLAN_VID },
+ { OFPFW10_DL_SRC, MFF_ETH_SRC },
+ { OFPFW10_DL_DST, MFF_ETH_DST},
+ { OFPFW10_DL_TYPE, MFF_ETH_TYPE },
+ { OFPFW10_NW_PROTO, MFF_IP_PROTO },
+ { OFPFW10_TP_SRC, MFF_TCP_SRC },
+ { OFPFW10_TP_DST, MFF_TCP_DST },
+ { OFPFW10_NW_SRC_MASK, MFF_IPV4_SRC },
+ { OFPFW10_NW_DST_MASK, MFF_IPV4_DST },
+ { OFPFW10_DL_VLAN_PCP, MFF_VLAN_PCP },
+ { OFPFW10_NW_TOS, MFF_IP_DSCP },
+};
+
+static ovs_be32
+mf_bitmap_to_of10(const struct mf_bitmap *fields)
+{
+ const struct ofp10_wc_map *p;
+ uint32_t wc10 = 0;
+
+ for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) {
+ if (bitmap_is_set(fields->bm, p->mf)) {
+ wc10 |= p->wc10;
+ }
+ }
+ return htonl(wc10);
+}
+
+static struct mf_bitmap
+mf_bitmap_from_of10(ovs_be32 wc10)
+{
+ struct mf_bitmap fields = MF_BITMAP_INITIALIZER;
+ const struct ofp10_wc_map *p;
+
+ for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) {
+ if (wc10 & htonl(p->wc10)) {
+ bitmap_set1(fields.bm, p->mf);
+ }
+ }
+ return fields;
+}
+
static void
-ofputil_put_ofp10_table_stats(const struct ofputil_table_stats *in,
+ofputil_put_ofp10_table_stats(const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features,
struct ofpbuf *buf)
{
- struct wc_map {
- enum ofp10_flow_wildcards wc10;
- enum mf_field_id mf;
- };
-
- static const struct wc_map wc_map[] = {
- { OFPFW10_IN_PORT, MFF_IN_PORT },
- { OFPFW10_DL_VLAN, MFF_VLAN_VID },
- { OFPFW10_DL_SRC, MFF_ETH_SRC },
- { OFPFW10_DL_DST, MFF_ETH_DST},
- { OFPFW10_DL_TYPE, MFF_ETH_TYPE },
- { OFPFW10_NW_PROTO, MFF_IP_PROTO },
- { OFPFW10_TP_SRC, MFF_TCP_SRC },
- { OFPFW10_TP_DST, MFF_TCP_DST },
- { OFPFW10_NW_SRC_MASK, MFF_IPV4_SRC },
- { OFPFW10_NW_DST_MASK, MFF_IPV4_DST },
- { OFPFW10_DL_VLAN_PCP, MFF_VLAN_PCP },
- { OFPFW10_NW_TOS, MFF_IP_DSCP },
- };
-
+ struct mf_bitmap wc = wild_or_nonmatchable_fields(features);
struct ofp10_table_stats *out;
- const struct wc_map *p;
out = ofpbuf_put_zeros(buf, sizeof *out);
- out->table_id = in->table_id;
- ovs_strlcpy(out->name, in->name, sizeof out->name);
- out->wildcards = 0;
- for (p = wc_map; p < &wc_map[ARRAY_SIZE(wc_map)]; p++) {
- if (bitmap_is_set(in->wildcards.bm, p->mf)) {
- out->wildcards |= htonl(p->wc10);
+ out->table_id = features->table_id;
+ ovs_strlcpy(out->name, features->name, sizeof out->name);
+ out->wildcards = mf_bitmap_to_of10(&wc);
+ out->max_entries = htonl(features->max_entries);
+ out->active_count = htonl(stats->active_count);
+ put_32aligned_be64(&out->lookup_count, htonll(stats->lookup_count));
+ put_32aligned_be64(&out->matched_count, htonll(stats->matched_count));
+}
+
+struct ofp11_wc_map {
+ enum ofp11_flow_match_fields wc11;
+ enum mf_field_id mf;
+};
+
+static const struct ofp11_wc_map ofp11_wc_map[] = {
+ { OFPFMF11_IN_PORT, MFF_IN_PORT },
+ { OFPFMF11_DL_VLAN, MFF_VLAN_VID },
+ { OFPFMF11_DL_VLAN_PCP, MFF_VLAN_PCP },
+ { OFPFMF11_DL_TYPE, MFF_ETH_TYPE },
+ { OFPFMF11_NW_TOS, MFF_IP_DSCP },
+ { OFPFMF11_NW_PROTO, MFF_IP_PROTO },
+ { OFPFMF11_TP_SRC, MFF_TCP_SRC },
+ { OFPFMF11_TP_DST, MFF_TCP_DST },
+ { OFPFMF11_MPLS_LABEL, MFF_MPLS_LABEL },
+ { OFPFMF11_MPLS_TC, MFF_MPLS_TC },
+ /* I don't know what OFPFMF11_TYPE means. */
+ { OFPFMF11_DL_SRC, MFF_ETH_SRC },
+ { OFPFMF11_DL_DST, MFF_ETH_DST },
+ { OFPFMF11_NW_SRC, MFF_IPV4_SRC },
+ { OFPFMF11_NW_DST, MFF_IPV4_DST },
+ { OFPFMF11_METADATA, MFF_METADATA },
+};
+
+static ovs_be32
+mf_bitmap_to_of11(const struct mf_bitmap *fields)
+{
+ const struct ofp11_wc_map *p;
+ uint32_t wc11 = 0;
+
+ for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) {
+ if (bitmap_is_set(fields->bm, p->mf)) {
+ wc11 |= p->wc11;
}
}
- out->max_entries = htonl(in->max_entries);
- out->active_count = htonl(in->active_count);
- put_32aligned_be64(&out->lookup_count, htonll(in->lookup_count));
- put_32aligned_be64(&out->matched_count, htonll(in->matched_count));
+ return htonl(wc11);
}
-static ovs_be32
-fields_to_ofp11_flow_match_fields(const struct mf_bitmap *fields)
-{
- struct map {
- enum ofp11_flow_match_fields fmf11;
- enum mf_field_id mf;
- };
-
- static const struct map map[] = {
- { OFPFMF11_IN_PORT, MFF_IN_PORT },
- { OFPFMF11_DL_VLAN, MFF_VLAN_VID },
- { OFPFMF11_DL_VLAN_PCP, MFF_VLAN_PCP },
- { OFPFMF11_DL_TYPE, MFF_ETH_TYPE },
- { OFPFMF11_NW_TOS, MFF_IP_DSCP },
- { OFPFMF11_NW_PROTO, MFF_IP_PROTO },
- { OFPFMF11_TP_SRC, MFF_TCP_SRC },
- { OFPFMF11_TP_DST, MFF_TCP_DST },
- { OFPFMF11_MPLS_LABEL, MFF_MPLS_LABEL },
- { OFPFMF11_MPLS_TC, MFF_MPLS_TC },
- /* I don't know what OFPFMF11_TYPE means. */
- { OFPFMF11_DL_SRC, MFF_ETH_SRC },
- { OFPFMF11_DL_DST, MFF_ETH_DST },
- { OFPFMF11_NW_SRC, MFF_IPV4_SRC },
- { OFPFMF11_NW_DST, MFF_IPV4_DST },
- { OFPFMF11_METADATA, MFF_METADATA },
- };
-
- const struct map *p;
- uint32_t fmf11;
-
- fmf11 = 0;
- for (p = map; p < &map[ARRAY_SIZE(map)]; p++) {
- if (bitmap_is_set(fields->bm, p->mf)) {
- fmf11 |= p->fmf11;
+static struct mf_bitmap
+mf_bitmap_from_of11(ovs_be32 wc11)
+{
+ struct mf_bitmap fields = MF_BITMAP_INITIALIZER;
+ const struct ofp11_wc_map *p;
+
+ for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) {
+ if (wc11 & htonl(p->wc11)) {
+ bitmap_set1(fields.bm, p->mf);
}
}
- return htonl(fmf11);
+ return fields;
}
static void
-ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *in,
+ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features,
struct ofpbuf *buf)
{
+ struct mf_bitmap wc = wild_or_nonmatchable_fields(features);
struct ofp11_table_stats *out;
out = ofpbuf_put_zeros(buf, sizeof *out);
- out->table_id = in->table_id;
- ovs_strlcpy(out->name, in->name, sizeof out->name);
- out->wildcards = fields_to_ofp11_flow_match_fields(&in->wildcards);
- out->match = fields_to_ofp11_flow_match_fields(&in->match);
- out->instructions = htonl(in->instructions);
- out->write_actions = ofpact_bitmap_to_openflow(in->write_ofpacts,
- OFP11_VERSION);
- out->apply_actions = ofpact_bitmap_to_openflow(in->apply_ofpacts,
- OFP11_VERSION);
- out->config = htonl(in->config);
- out->max_entries = htonl(in->max_entries);
- out->active_count = htonl(in->active_count);
- out->lookup_count = htonll(in->lookup_count);
- out->matched_count = htonll(in->matched_count);
+ out->table_id = features->table_id;
+ ovs_strlcpy(out->name, features->name, sizeof out->name);
+ out->wildcards = mf_bitmap_to_of11(&wc);
+ out->match = mf_bitmap_to_of11(&features->match);
+ out->instructions = ovsinst_bitmap_to_openflow(
+ features->nonmiss.instructions, OFP11_VERSION);
+ out->write_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.write.ofpacts, OFP11_VERSION);
+ out->apply_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.apply.ofpacts, OFP11_VERSION);
+ out->config = htonl(features->config);
+ out->max_entries = htonl(features->max_entries);
+ out->active_count = htonl(stats->active_count);
+ out->lookup_count = htonll(stats->lookup_count);
+ out->matched_count = htonll(stats->matched_count);
}
static ovs_be64
@@ -5114,84 +5168,274 @@ mf_bitmap_to_oxm_bitmap(const struct mf_bitmap *fields,
return htonll(oxm_bitmap);
}
+static struct mf_bitmap
+mf_bitmap_from_oxm_bitmap(ovs_be64 oxm_bitmap, enum ofp_version version)
+{
+ struct mf_bitmap fields = MF_BITMAP_INITIALIZER;
+
+ for (enum mf_field_id id = 0; id < MFF_N_IDS; id++) {
+ const struct mf_field *f = mf_from_id(id);
+ uint32_t oxm = f->oxm_header;
+ uint32_t vendor = NXM_VENDOR(oxm);
+ int field = NXM_FIELD(oxm);
+
+ if (version >= f->oxm_version
+ && vendor == OFPXMC12_OPENFLOW_BASIC
+ && field < 64
+ && oxm_bitmap & htonll(UINT64_C(1) << field)) {
+ bitmap_set1(fields.bm, id);
+ }
+ }
+ return fields;
+}
+
static void
-ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *in,
+ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features,
struct ofpbuf *buf)
{
struct ofp12_table_stats *out;
out = ofpbuf_put_zeros(buf, sizeof *out);
- out->table_id = in->table_id;
- ovs_strlcpy(out->name, in->name, sizeof out->name);
- out->match = mf_bitmap_to_oxm_bitmap(&in->match, OFP12_VERSION);
- out->wildcards = mf_bitmap_to_oxm_bitmap(&in->wildcards, OFP12_VERSION);
- out->write_actions = ofpact_bitmap_to_openflow(in->write_ofpacts,
- OFP12_VERSION);
- out->apply_actions = ofpact_bitmap_to_openflow(in->apply_ofpacts,
- OFP12_VERSION);
- out->write_setfields = mf_bitmap_to_oxm_bitmap(&in->write_setfields,
- OFP12_VERSION);
- out->apply_setfields = mf_bitmap_to_oxm_bitmap(&in->apply_setfields,
- OFP12_VERSION);
- out->metadata_match = in->metadata_match;
- out->metadata_write = in->metadata_write;
- out->instructions = htonl(in->instructions & OFPIT11_ALL);
- printf ("%d\n", in->instructions);
- out->config = htonl(in->config);
- out->max_entries = htonl(in->max_entries);
- out->active_count = htonl(in->active_count);
- out->lookup_count = htonll(in->lookup_count);
- out->matched_count = htonll(in->matched_count);
+ out->table_id = features->table_id;
+ ovs_strlcpy(out->name, features->name, sizeof out->name);
+ out->match = mf_bitmap_to_oxm_bitmap(&features->match, OFP12_VERSION);
+ out->wildcards = mf_bitmap_to_oxm_bitmap(&features->wildcard,
+ OFP12_VERSION);
+ out->write_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.write.ofpacts, OFP12_VERSION);
+ out->apply_actions = ofpact_bitmap_to_openflow(
+ features->nonmiss.apply.ofpacts, OFP12_VERSION);
+ out->write_setfields = mf_bitmap_to_oxm_bitmap(
+ &features->nonmiss.write.set_fields, OFP12_VERSION);
+ out->apply_setfields = mf_bitmap_to_oxm_bitmap(
+ &features->nonmiss.apply.set_fields, OFP12_VERSION);
+ out->metadata_match = features->metadata_match;
+ out->metadata_write = features->metadata_write;
+ out->instructions = ovsinst_bitmap_to_openflow(
+ features->nonmiss.instructions, OFP12_VERSION);
+ out->config = htonl(features->config);
+ out->max_entries = htonl(features->max_entries);
+ out->active_count = htonl(stats->active_count);
+ out->lookup_count = htonll(stats->lookup_count);
+ out->matched_count = htonll(stats->matched_count);
}
static void
-ofputil_put_ofp13_table_stats(const struct ofputil_table_stats *in,
+ofputil_put_ofp13_table_stats(const struct ofputil_table_stats *stats,
struct ofpbuf *buf)
{
struct ofp13_table_stats *out;
- out = ofpbuf_put_uninit(buf, sizeof *out);
- out->table_id = in->table_id;
- out->active_count = htonl(in->active_count);
- out->lookup_count = htonll(in->lookup_count);
- out->matched_count = htonll(in->matched_count);
+ out = ofpbuf_put_zeros(buf, sizeof *out);
+ out->table_id = stats->table_id;
+ out->active_count = htonl(stats->active_count);
+ out->lookup_count = htonll(stats->lookup_count);
+ out->matched_count = htonll(stats->matched_count);
}
struct ofpbuf *
-ofputil_encode_table_stats_reply(const struct ofputil_table_stats stats[],
- int n, const struct ofp_header *request)
+ofputil_encode_table_stats_reply(const struct ofp_header *request)
{
- struct ofpbuf *reply;
- int i;
+ return ofpraw_alloc_stats_reply(request, 0);
+}
+
+void
+ofputil_append_table_stats_reply(struct ofpbuf *reply,
+ const struct ofputil_table_stats *stats,
+ const struct ofputil_table_features *features)
+{
+ struct ofp_header *oh = ofpbuf_l2(reply);
- reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats);
+ ovs_assert(stats->table_id == features->table_id);
- for (i = 0; i < n; i++) {
- switch ((enum ofp_version) request->version) {
- case OFP10_VERSION:
- ofputil_put_ofp10_table_stats(&stats[i], reply);
- break;
+ switch ((enum ofp_version) oh->version) {
+ case OFP10_VERSION:
+ ofputil_put_ofp10_table_stats(stats, features, reply);
+ break;
- case OFP11_VERSION:
- ofputil_put_ofp11_table_stats(&stats[i], reply);
- break;
+ case OFP11_VERSION:
+ ofputil_put_ofp11_table_stats(stats, features, reply);
+ break;
- case OFP12_VERSION:
- ofputil_put_ofp12_table_stats(&stats[i], reply);
- break;
+ case OFP12_VERSION:
+ ofputil_put_ofp12_table_stats(stats, features, reply);
+ break;
- case OFP13_VERSION:
- case OFP14_VERSION:
- case OFP15_VERSION:
- ofputil_put_ofp13_table_stats(&stats[i], reply);
- break;
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ ofputil_put_ofp13_table_stats(stats, reply);
+ break;
- default:
- OVS_NOT_REACHED();
- }
+ default:
+ OVS_NOT_REACHED();
}
+}
- return reply;
+static int
+ofputil_decode_ofp10_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp10_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+ ovs_strlcpy(features->name, ots->name, sizeof features->name);
+ features->max_entries = ntohl(ots->max_entries);
+ features->match = features->wildcard = mf_bitmap_from_of10(ots->wildcards);
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(get_32aligned_be64(&ots->lookup_count));
+ stats->matched_count = ntohll(get_32aligned_be64(&ots->matched_count));
+
+ return 0;
+}
+
+static int
+ofputil_decode_ofp11_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp11_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+ ovs_strlcpy(features->name, ots->name, sizeof features->name);
+ features->max_entries = ntohl(ots->max_entries);
+ features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
+ ots->instructions, OFP11_VERSION);
+ features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow(
+ ots->write_actions, OFP11_VERSION);
+ features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
+ ots->write_actions, OFP11_VERSION);
+ features->miss = features->nonmiss;
+ features->config = ntohl(ots->config);
+ features->match = mf_bitmap_from_of11(ots->match);
+ features->wildcard = mf_bitmap_from_of11(ots->wildcards);
+ bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(ots->lookup_count);
+ stats->matched_count = ntohll(ots->matched_count);
+
+ return 0;
+}
+
+static int
+ofputil_decode_ofp12_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp12_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+ ovs_strlcpy(features->name, ots->name, sizeof features->name);
+ features->metadata_match = ots->metadata_match;
+ features->metadata_write = ots->metadata_write;
+ features->config = ntohl(ots->config);
+ features->max_entries = ntohl(ots->max_entries);
+
+ features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
+ ots->instructions, OFP12_VERSION);
+ features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow(
+ ots->write_actions, OFP12_VERSION);
+ features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
+ ots->apply_actions, OFP12_VERSION);
+ features->nonmiss.write.set_fields = mf_bitmap_from_oxm_bitmap(
+ ots->write_setfields, OFP12_VERSION);
+ features->nonmiss.apply.set_fields = mf_bitmap_from_oxm_bitmap(
+ ots->apply_setfields, OFP12_VERSION);
+ features->miss = features->nonmiss;
+
+ features->match = mf_bitmap_from_oxm_bitmap(ots->match, OFP12_VERSION);
+ features->wildcard = mf_bitmap_from_oxm_bitmap(ots->wildcards,
+ OFP12_VERSION);
+ bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(ots->lookup_count);
+ stats->matched_count = ntohll(ots->matched_count);
+
+ return 0;
+}
+
+static int
+ofputil_decode_ofp13_table_stats(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ struct ofp13_table_stats *ots;
+
+ ots = ofpbuf_try_pull(msg, sizeof *ots);
+ if (!ots) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ features->table_id = ots->table_id;
+
+ stats->table_id = ots->table_id;
+ stats->active_count = ntohl(ots->active_count);
+ stats->lookup_count = ntohll(ots->lookup_count);
+ stats->matched_count = ntohll(ots->matched_count);
+
+ return 0;
+}
+
+int
+ofputil_decode_table_stats_reply(struct ofpbuf *msg,
+ struct ofputil_table_stats *stats,
+ struct ofputil_table_features *features)
+{
+ const struct ofp_header *oh;
+
+ if (!msg->frame) {
+ ofpraw_pull_assert(msg);
+ }
+ oh = msg->frame;
+
+ if (!ofpbuf_size(msg)) {
+ return EOF;
+ }
+
+ memset(stats, 0, sizeof *stats);
+ memset(features, 0, sizeof *features);
+
+ switch ((enum ofp_version) oh->version) {
+ case OFP10_VERSION:
+ return ofputil_decode_ofp10_table_stats(msg, stats, features);
+
+ case OFP11_VERSION:
+ return ofputil_decode_ofp11_table_stats(msg, stats, features);
+
+ case OFP12_VERSION:
+ return ofputil_decode_ofp12_table_stats(msg, stats, features);
+
+ case OFP13_VERSION:
+ case OFP14_VERSION:
+ case OFP15_VERSION:
+ return ofputil_decode_ofp13_table_stats(msg, stats, features);
+
+ default:
+ OVS_NOT_REACHED();
+ }
}
/* ofputil_flow_monitor_request */