diff mbox

[ovs-dev,1/6] OF1.5/EXT-334 OXS/Individal Flow Statistics -1

Message ID 1495039290-24399-1-git-send-email-satyavalli.rama@tcs.com
State Changes Requested
Headers show

Commit Message

SatyaValli May 17, 2017, 4:41 p.m. UTC
From: SatyaValli <satyavalli.rama@tcs.com>

OpenFlow 1.5 introduces the Extensible Statistics (OXS) by redefining the existing
flow entry statistics with OXS Fields.
This Patch provides implementation for OXS fields encoding in TLV format.

To support this implementation below two messages are newly added

OFPST_OXS_FLOW_STATS_REQUEST
OFPST_OXS_FLOW_STATS_REPLY
OFPST_OXS_AGGREGATE_STATS_REQUEST
OFPST_OXS_AGGREGATE_STATS_REPLY
OFPST_FLOW_REMOVED

As per the openflow specification-1.5, this enhancement should take place
on the existing flow entry statistics with the OXS fields on all the messages
that carries flow entry statistics.

The current commit adds support for the new feature in flow statistics multipart messages,
aggregate multipart messages and OXS flow statistics support for flow removal message.

Some more fields are added to ovs-ofctl dump-flows command to support OpenFlow15 OXS stats.
Below are Commands to display OXS stats field wise

Flow Statistics Multipart
ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-duration
ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-idle_time
ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-packet_count
ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-byte_count

Aggregate Flow Statistics Multipart
ovs-ofctl dump-aggregate -O OpenFlow15 <bridge> oxs-packet_count
ovs-ofctl dump-aggregate -O OpenFlow15 <bridge> oxs-byte_count
ovs-ofctl dump-aggregate -O OpenFlow15 <bridge> oxs-flow_count

For feature vise verification please apply 
Patch 1 and 2 for OXS support for Flow multiplart stats support
Patch 3 and 4 for OXS support for Aggregate multipart stats support
Patch 5 and 6 for OXS support for Flow Removal   

Signed-off-by: Satya Valli <satyavalli.rama@tcs.com>
Co-authored-by: Lavanya Harivelam <harivelam.lavanya@tcs.com>

---
 include/openflow/openflow-1.5.h |  50 +++++++++
 include/openvswitch/ofp-msgs.h  |   6 +
 lib/automake.mk                 |   2 +
 lib/ofp-parse.c                 |  45 +++++++-
 lib/ofp-print.c                 |  10 ++
 lib/ox-stat.c                   | 238 ++++++++++++++++++++++++++++++++++++++++
 lib/ox-stat.h                   |  32 ++++++
 lib/rconn.c                     |   2 +
 ofproto/ofproto.c               |   4 +
 9 files changed, 388 insertions(+), 1 deletion(-)
 create mode 100644 lib/ox-stat.c
 create mode 100644 lib/ox-stat.h

Comments

Ben Pfaff May 31, 2017, 9:59 p.m. UTC | #1
On Wed, May 17, 2017 at 10:11:30PM +0530, satyavalli.rama@gmail.com wrote:
> From: SatyaValli <satyavalli.rama@tcs.com>
> 
> OpenFlow 1.5 introduces the Extensible Statistics (OXS) by redefining the existing
> flow entry statistics with OXS Fields.
> This Patch provides implementation for OXS fields encoding in TLV format.
> 
> To support this implementation below two messages are newly added
> 
> OFPST_OXS_FLOW_STATS_REQUEST
> OFPST_OXS_FLOW_STATS_REPLY
> OFPST_OXS_AGGREGATE_STATS_REQUEST
> OFPST_OXS_AGGREGATE_STATS_REPLY
> OFPST_FLOW_REMOVED
> 
> As per the openflow specification-1.5, this enhancement should take place
> on the existing flow entry statistics with the OXS fields on all the messages
> that carries flow entry statistics.
> 
> The current commit adds support for the new feature in flow statistics multipart messages,
> aggregate multipart messages and OXS flow statistics support for flow removal message.
> 
> Some more fields are added to ovs-ofctl dump-flows command to support OpenFlow15 OXS stats.
> Below are Commands to display OXS stats field wise
> 
> Flow Statistics Multipart
> ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-duration
> ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-idle_time
> ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-packet_count
> ovs-ofctl dump-flows -O OpenFlow15 <bridge> oxs-byte_count
> 
> Aggregate Flow Statistics Multipart
> ovs-ofctl dump-aggregate -O OpenFlow15 <bridge> oxs-packet_count
> ovs-ofctl dump-aggregate -O OpenFlow15 <bridge> oxs-byte_count
> ovs-ofctl dump-aggregate -O OpenFlow15 <bridge> oxs-flow_count

This doesn't build:

    In file included from ../lib/ox-stat.c:19:
    ../lib/ox-stat.h:29:48: error: declaration of 'struct ofputil_flow_stats' will not be visible outside of this function [-Werror,-Wvisibility]
    ../lib/ox-stat.c:222:20: error: use of undeclared identifier 'oxs_header_map'
    ../lib/ox-stat.c:223:20: error: use of undeclared identifier 'oxs_name_map'; did you mean 'oxs_ox_map'?
    ../lib/ox-stat.c:46:24: note: 'oxs_ox_map' declared here
    ../lib/ox-stat.c:229:26: error: use of undeclared identifier 'oxs_header_map'
    ../include/openvswitch/hmap.h:101:20: note: expanded from macro 'hmap_insert'
    ../lib/ox-stat.c:231:26: error: use of undeclared identifier 'oxs_name_map'; did you mean 'oxs_ox_map'?
    ../include/openvswitch/hmap.h:101:20: note: expanded from macro 'hmap_insert'
    ../lib/ox-stat.c:46:24: note: 'oxs_ox_map' declared here
    ../lib/ofp-util.c:10166:13: error: enumeration values 'OFPTYPE_OXS_FLOW_STATS_REQUEST' and 'OFPTYPE_OXS_FLOW_STATS_REPLY' not handled in switch [-Werror,-Wswitch]

After applying every patch, the tree must build and pass all the tests.
Please fix your patch series to do that and repost it.
diff mbox

Patch

diff --git a/include/openflow/openflow-1.5.h b/include/openflow/openflow-1.5.h
index 3649e6c..3b66f7f 100644
--- a/include/openflow/openflow-1.5.h
+++ b/include/openflow/openflow-1.5.h
@@ -150,4 +150,54 @@  struct ofp15_group_desc_stats {
 };
 OFP_ASSERT(sizeof(struct ofp15_group_desc_stats) == 16);
 
+struct ofp_oxs_stat {
+    ovs_be16 reserved;          /* One of OFPST_* */
+    ovs_be16 length;            /* Stats Length */
+};
+
+OFP_ASSERT(sizeof(struct ofp_oxs_stat) == 4);
+
+/* Body for ofp_multipart_request of type
+* OFPMP_FLOW_DESC & OFPMP_FLOW_STATS. */
+struct ofp15_oxs_flow_stats_request {
+    uint8_t table_id;           /* ID of table to read (from ofp_table_desc),
+                                 * OFPTT_ALL for all tables. */
+    uint8_t pad[3];             /* Align to 32 bits. */
+    ovs_be32 out_port;          /* Require matching entries to include this as
+                                 * an output port. A value of OFP_ANY
+                                 * indicates no restriction. */
+    ovs_be32 out_group;         /* Require matching entries to include this as
+                                 * an output group. A value of OFPG_ANY
+                                 * indicates no restriction. */
+    uint8_t pad2[4];            /* Align to 64 bits. */
+    ovs_be64 cookie;            /* Require matching entries to contain this
+                                 * cookie value */
+    ovs_be64 cookie_mask;       /* Mask used to restrict the cookie bits that
+                                 * must match. A value of 0 indicates no
+                                 * restriction. */
+};
+
+OFP_ASSERT(sizeof(struct ofp15_oxs_flow_stats_request) == 32);
+
+/* Body of reply to OFPMP_FLOW_STATS request
+* and body for OFPIT_STAT_TRIGGER generated status. */
+struct ofp15_oxs_flow_stats_reply {
+    ovs_be16 length;            /* Length of this entry.  */
+    uint8_t pad2[2];            /* Align to 64-bits.  */
+    uint8_t table_id;           /* ID of table flow came from. */
+    uint8_t reason;             /* One of OFPFSR_*.  */
+    ovs_be16 priority;          /* Priority of the entry.  */
+};
+
+OFP_ASSERT(sizeof(struct ofp15_oxs_flow_stats_reply) == 8);
+
+/* OXS flow stat field types for OpenFlow basic class. */
+enum oxs_ofb_stat_fields {
+    OFPXST_OFB_DURATION = 0,     /* Time flow entry has been alive.  */
+    OFPXST_OFB_IDLE_TIME = 1,    /* Time flow entry has been idle.  */
+    OFPXST_OFB_FLOW_COUNT = 2,   /* Number of aggregated flow entries. */
+    OFPXST_OFB_PACKET_COUNT = 3, /* Number of packets in flow entry.  */
+    OFPXST_OFB_BYTE_COUNT = 4,   /* Number of bytes in flow entry.  */
+};
+
 #endif /* openflow/openflow-1.5.h */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index 34708f3..089db47 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -287,6 +287,8 @@  enum ofpraw {
     OFPRAW_OFPST10_FLOW_REQUEST,
     /* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
     OFPRAW_OFPST11_FLOW_REQUEST,
+    /* OFPST 1.5+ (17): struct ofp15_oxs_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST15_OXS_FLOW_REQUEST,
     /* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */
     OFPRAW_NXST_FLOW_REQUEST,
 
@@ -296,6 +298,8 @@  enum ofpraw {
     OFPRAW_OFPST11_FLOW_REPLY,
     /* OFPST 1.3+ (1): uint8_t[]. */
     OFPRAW_OFPST13_FLOW_REPLY,
+    /* OFPST 1.5+ (17): uint8_t[]. */
+    OFPRAW_OFPST15_OXS_FLOW_REPLY,
     /* NXST 1.0 (0): uint8_t[]. */
     OFPRAW_NXST_FLOW_REPLY,
 
@@ -628,6 +632,7 @@  enum ofptype {
     OFPTYPE_FLOW_STATS_REQUEST,      /* OFPRAW_OFPST10_FLOW_REQUEST.
                                       * OFPRAW_OFPST11_FLOW_REQUEST.
                                       * OFPRAW_NXST_FLOW_REQUEST. */
+    OFPTYPE_OXS_FLOW_STATS_REQUEST,  /* OFPRAW_OFPST15_OXS_FLOW_REQUEST. */
     OFPTYPE_FLOW_STATS_REPLY,        /* OFPRAW_OFPST10_FLOW_REPLY.
                                       * OFPRAW_OFPST11_FLOW_REPLY.
                                       * OFPRAW_OFPST13_FLOW_REPLY.
@@ -635,6 +640,7 @@  enum ofptype {
     OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
                                       * OFPRAW_OFPST11_AGGREGATE_REQUEST.
                                       * OFPRAW_NXST_AGGREGATE_REQUEST. */
+    OFPTYPE_OXS_FLOW_STATS_REPLY,    /* OFPRAW_OFPST15_OXS_FLOW_REPLY. */
     OFPTYPE_AGGREGATE_STATS_REPLY,   /* OFPRAW_OFPST_AGGREGATE_REPLY.
                                       * OFPRAW_NXST_AGGREGATE_REPLY. */
     OFPTYPE_TABLE_STATS_REQUEST,     /* OFPRAW_OFPST_TABLE_REQUEST. */
diff --git a/lib/automake.mk b/lib/automake.mk
index faace79..a9c402e 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -197,6 +197,8 @@  lib_libopenvswitch_la_SOURCES = \
 	lib/ovsdb-parser.h \
 	lib/ovsdb-types.c \
 	lib/ovsdb-types.h \
+       lib/ox-stat.c \
+       lib/ox-stat.h \
 	lib/packets.c \
 	lib/packets.h \
 	lib/pcap-file.c \
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index c8cac5b..78384c7 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -41,6 +41,8 @@ 
 #include "socket-util.h"
 #include "util.h"
 
+extern uint8_t oxs_field_set;
+
 /* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
  *
  * 'name' describes the value parsed in an error message, if any.
@@ -188,6 +190,34 @@  str_to_connhelper(const char *str, uint16_t *alg)
     return xasprintf("invalid conntrack helper \"%s\"", str);
 }
 
+struct ox_fields {
+    const char *name;
+    uint16_t fl_type;
+};
+
+static bool
+parse_oxs_field(const char *name, const struct ox_fields **f_out)
+{
+    static const struct ox_fields fields[] = {
+        {"oxs-duration", OFPXST_OFB_DURATION},
+        {"oxs-idle_time", OFPXST_OFB_IDLE_TIME},
+        {"oxs-flow_count", OFPXST_OFB_FLOW_COUNT},
+        {"oxs-packet_count", OFPXST_OFB_PACKET_COUNT},
+        {"oxs-byte_count", OFPXST_OFB_BYTE_COUNT},
+    };
+
+    const struct ox_fields *f;
+
+    for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
+        if (!strcmp(f->name, name)) {
+            *f_out = f;
+            return true;
+        }
+    }
+    *f_out = NULL;
+    return false;
+}
+
 struct protocol {
     const char *name;
     uint16_t dl_type;
@@ -406,10 +436,23 @@  parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
 
     while (ofputil_parse_key_value(&string, &name, &value)) {
         const struct protocol *p;
+        const struct ox_fields *f;
         const struct mf_field *mf;
         char *error = NULL;
 
-        if (parse_protocol(name, &p)) {
+        if (parse_oxs_field(name, &f)) {
+            if (f->fl_type == OFPXST_OFB_DURATION) {
+                oxs_field_set |= (1 << f->fl_type);
+            } else if (f->fl_type == OFPXST_OFB_IDLE_TIME) {
+                oxs_field_set |= (1 << f->fl_type);
+            } else if (f->fl_type == OFPXST_OFB_FLOW_COUNT) {
+                oxs_field_set |= (1 << f->fl_type);
+            } else if (f->fl_type == OFPXST_OFB_PACKET_COUNT) {
+                oxs_field_set |= (1 << f->fl_type);
+            } else if (f->fl_type == OFPXST_OFB_BYTE_COUNT) {
+                oxs_field_set |= (1 << f->fl_type);
+            }
+        } else if (parse_protocol(name, &p)) {
             match_set_dl_type(&fm->match, htons(p->dl_type));
             if (p->nw_proto) {
                 match_set_nw_proto(&fm->match, p->nw_proto);
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 7ca9531..0f855fa 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -3563,6 +3563,11 @@  ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_flow_stats_request(string, oh);
         break;
 
+    case OFPTYPE_OXS_FLOW_STATS_REQUEST:
+        ofp_print_stats(string, oh);
+        ofp_print_flow_stats_request(string, oh);
+        break;
+
     case OFPTYPE_TABLE_STATS_REQUEST:
         ofp_print_stats(string, oh);
         break;
@@ -3587,6 +3592,11 @@  ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_flow_stats_reply(string, oh);
         break;
 
+    case OFPTYPE_OXS_FLOW_STATS_REPLY:
+        ofp_print_stats(string, oh);
+        ofp_print_flow_stats_reply(string, oh);
+        break;
+
     case OFPTYPE_QUEUE_STATS_REPLY:
         ofp_print_stats(string, oh);
         ofp_print_ofpst_queue_reply(string, oh, verbosity);
diff --git a/lib/ox-stat.c b/lib/ox-stat.c
new file mode 100644
index 0000000..96f019c
--- /dev/null
+++ b/lib/ox-stat.c
@@ -0,0 +1,238 @@ 
+/*
+ * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "nx-match.h"
+#include "ox-stat.h"
+#include <netinet/icmp6.h>
+#include "classifier.h"
+#include "colors.h"
+#include "openvswitch/hmap.h"
+#include "openflow/nicira-ext.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
+#include "packets.h"
+#include "openvswitch/shash.h"
+#include "tun-metadata.h"
+#include "unaligned.h"
+#include "util.h"
+
+VLOG_DEFINE_THIS_MODULE(ox_stat);
+
+/* ## -------------------------- ## */
+/* ## OpenFlow Extensible Stats. ## */
+/* ## -------------------------- ## */
+
+/* Components of a OXS TLV header. */
+
+static struct ovs_list oxs_ox_map[OFPXST_OFB_BYTE_COUNT + 1];
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static uint32_t
+oxs_header_no_len(uint32_t header)
+{
+    return header & 0xffffff80;
+}
+
+#define OXS_CLASS(HEADER) ((HEADER) >> 16)
+#define OXS_FIELD(HEADER) (((HEADER) >> 9) & 0x7f)
+#define OXS_TYPE(HEADER) (((HEADER) >> 9) & 0x7fffff)
+#define OXS_RESERVED(HEADER) (((HEADER) >> 8) & 1)
+#define OXS_LENGTH(HEADER) ((HEADER) & 0xff)
+
+/* Components of a OXS TLV header. */
+#define OXS_HEADER__(CLASS, FIELD, RESERVED, LENGTH) \
+(((CLASS) << 16) | ((FIELD) << 9) | ((RESERVED) << 8) | (LENGTH))
+
+#define OXS_HEADER(CLASS, FIELD, LENGTH) \
+        OXS_HEADER__(CLASS, FIELD, 0, LENGTH)
+
+/*  OXS Class IDs.
+ *  The high order bit differentiate reserved classes from member classes.
+ *  Classes 0x0000 to 0x7FFF are member classes, allocated by ONF.
+ *  Classes 0x8000 to 0xFFFE are reserved classes, reserved for
+ *  standardisation.
+ */
+
+enum ofp_oxs_class {
+    OFPXSC_OPENFLOW_BASIC = 0x8002,     /* Basic stats class for OpenFlow */
+    OFPXSC_EXPERIMENTER = 0xFFFF,       /* Experimenter class */
+};
+
+#define OFPXST_OFB_ALL ((UINT64_C(1) << 6) - 1)
+#define OXS_OX_COOKIE    OXS_HEADER  (0x8002, 5 , 8)
+
+struct oxs_field {
+    uint32_t header;
+    enum ofp_version version;
+    const char *name;
+    enum oxs_ofb_stat_fields id;
+};
+
+struct oxs_field_index {
+    struct hmap_node header_node;
+    struct hmap_node name_node;
+    struct ovs_list ox_node;
+    const struct oxs_field fs;
+};
+
+#define OXS_STATS_DURATION_LEN     8
+#define OXS_STATS_IDLE_TIME_LEN    8
+#define OXS_STATS_FLOW_COUNT_LEN   4
+#define OXS_STATS_PACKET_COUNT_LEN 8
+#define OXS_STATS_BYTE_COUNT_LEN   8
+
+#define OXS_OF_DURATION     OXS_HEADER (0x8002, OFPXST_OFB_DURATION, \
+                                        OXS_STATS_DURATION_LEN)
+#define OXS_OF_IDLE_TIME    OXS_HEADER (0x8002, OFPXST_OFB_IDLE_TIME, \
+                                        OXS_STATS_IDLE_TIME_LEN)
+#define OXS_OF_FLOW_COUNT   OXS_HEADER (0x8002, OFPXST_OFB_FLOW_COUNT, \
+                                        OXS_STATS_FLOW_COUNT_LEN)
+#define OXS_OF_PACKET_COUNT OXS_HEADER (0x8002, OFPXST_OFB_PACKET_COUNT, \
+                                        OXS_STATS_PACKET_COUNT_LEN)
+#define OXS_OF_BYTE_COUNT   OXS_HEADER (0x8002, OFPXST_OFB_BYTE_COUNT, \
+                                        OXS_STATS_BYTE_COUNT_LEN)
+
+static struct oxs_field_index all_oxs_fields[] = {
+    {.fs = {OXS_OF_DURATION, OFP15_VERSION, "OFPXST_OFB_DURATION",
+            OFPXST_OFB_DURATION}},
+    {.fs = {OXS_OF_IDLE_TIME, OFP15_VERSION, "OFPXST_OFB_IDLE_TIME",
+            OFPXST_OFB_IDLE_TIME}},
+    {.fs = {OXS_OF_FLOW_COUNT, OFP15_VERSION, "OFPXST_OFB_FLOW_COUNT",
+            OFPXST_OFB_FLOW_COUNT}},
+    {.fs = {OXS_OF_PACKET_COUNT, OFP15_VERSION, "OFPXST_OFB_PACKET_COUNT",
+            OFPXST_OFB_PACKET_COUNT}},
+    {.fs = {OXS_OF_BYTE_COUNT, OFP15_VERSION, "OFPXST_OFB_BYTE_COUNT",
+            OFPXST_OFB_BYTE_COUNT}},
+};
+
+uint8_t oxs_field_set;
+
+static const struct oxs_field *oxs_field_by_header(uint32_t header);
+static const struct oxs_field *oxs_field_by_id(enum oxs_ofb_stat_fields,
+                                               enum ofp_version);
+static bool is_experimenter_oxs(uint64_t header);
+static int oxs_experimenter_len(uint64_t header);
+static int oxs_payload_len (uint64_t header);
+static int oxs_header_len (uint64_t header);
+static uint64_t oxs_header_get(enum oxs_ofb_stat_fields id,
+                                enum ofp_version version);
+static int oxs_pull_header__(struct ofpbuf *b, uint64_t * header,
+                              const struct oxs_field **field);
+static void oxs_init(void);
+
+static bool
+is_experimenter_oxs(uint64_t header)
+{
+    return OXS_CLASS(header) == OFPXSC_EXPERIMENTER;
+}
+
+static int
+oxs_experimenter_len(uint64_t header)
+{
+    return is_experimenter_oxs(header) ? 4 : 0;
+}
+
+static int
+oxs_payload_len(uint64_t header)
+{
+    return OXS_LENGTH(header) - oxs_experimenter_len(header);
+}
+
+static int
+oxs_header_len(uint64_t header)
+{
+    return 4 + oxs_experimenter_len(header);
+}
+
+static uint64_t
+oxs_header_get(enum oxs_ofb_stat_fields id, enum ofp_version version)
+{
+    const struct oxs_field *f = oxs_field_by_id(id, version);
+
+    return f ? f->header : 0;
+}
+
+static int
+oxs_pull_header__(struct ofpbuf *b, uint64_t * header,
+                  const struct oxs_field **field)
+{
+    if (b->size < 4) {
+        goto bad_len;
+    }
+
+    *header = ((uint32_t) ntohl(get_unaligned_be32(b->data)));
+    if (is_experimenter_oxs(*header)) {
+        if (b->size < 8) {
+            goto bad_len;
+        }
+
+        *header = ntohll(get_unaligned_be64(b->data));
+    }
+
+    if (OXS_LENGTH(*header) < oxs_experimenter_len(*header)) {
+        goto error;
+    }
+
+    ofpbuf_pull(b, oxs_header_len(*header));
+
+    if (field) {
+        *field = oxs_field_by_header(*header);
+        if (!*field || (*field == NULL)) {
+            return OFPERR_OFPBMC_BAD_FIELD;
+        }
+    }
+    return 0;
+
+bad_len:
+    VLOG_DBG_RL(&rl, "encountered partial (%" PRIu32 "-byte) OXS entry",
+                b->size);
+error:
+    *header = 0;
+    if (field) {
+        *field = NULL;
+    }
+    return OFPERR_OFPBMC_BAD_LEN;
+}
+
+static void
+oxs_init(void)
+{
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+    if (ovsthread_once_start(&once)) {
+        hmap_init(&oxs_header_map);
+        hmap_init(&oxs_name_map);
+        for (int i = 0; i < OFPXST_OFB_BYTE_COUNT + 1; i++) {
+            ovs_list_init(&oxs_ox_map[i]);
+        }
+        for (struct oxs_field_index * oxfs = all_oxs_fields;
+             oxfs < &all_oxs_fields[ARRAY_SIZE(all_oxs_fields)]; oxfs++) {
+            hmap_insert(&oxs_header_map, &oxfs->header_node,
+                        hash_int(oxs_header_no_len(oxfs->fs.header), 0));
+            hmap_insert(&oxs_name_map, &oxfs->name_node,
+                        hash_string(oxfs->fs.name, 0));
+            ovs_list_push_back(&oxs_ox_map[oxfs->fs.id], &oxfs->ox_node);
+        }
+        ovsthread_once_done(&once);
+    }
+}
+
diff --git a/lib/ox-stat.h b/lib/ox-stat.h
new file mode 100644
index 0000000..1dd8ab5
--- /dev/null
+++ b/lib/ox-stat.h
@@ -0,0 +1,32 @@ 
+/*
+ * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OX_STAT_H
+#define OX_STAT_H 1
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include "compiler.h"
+#include "flow.h"
+#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/types.h"
+
+int oxs_put_stat(struct ofpbuf *, const struct ofputil_flow_stats *,
+                 enum ofp_version );
+
+#endif /* ox_stat.h */
diff --git a/lib/rconn.c b/lib/rconn.c
index 8a29864..13890e7 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1392,6 +1392,8 @@  is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_DESC_STATS_REPLY:
     case OFPTYPE_FLOW_STATS_REQUEST:
     case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_OXS_FLOW_STATS_REQUEST:
+    case OFPTYPE_OXS_FLOW_STATS_REPLY:
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
     case OFPTYPE_AGGREGATE_STATS_REPLY:
     case OFPTYPE_TABLE_STATS_REQUEST:
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index d5410fd..4b95c9d 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -8092,6 +8092,9 @@  handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_STATS_REQUEST:
         return handle_flow_stats_request(ofconn, oh);
 
+    case OFPTYPE_OXS_FLOW_STATS_REQUEST:
+        return handle_flow_stats_request(ofconn, oh);
+
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
         return handle_aggregate_stats_request(ofconn, oh);
 
@@ -8167,6 +8170,7 @@  handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_DESC_STATS_REPLY:
     case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_OXS_FLOW_STATS_REPLY:
     case OFPTYPE_QUEUE_STATS_REPLY:
     case OFPTYPE_PORT_STATS_REPLY:
     case OFPTYPE_TABLE_STATS_REPLY: