diff mbox

[ovs-dev] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support

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

Commit Message

SatyaValli June 9, 2017, 7:56 a.m. UTC
commit 49a3592b2878a33033e5fd2e6e5ce82ebccef780
Author: Satya Valli <satyavalli.rama@tcs.com>

OpenVswitch: OF1.5/EXT-334 Extensible Flow Entry Statistics

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

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

---
 include/openflow/openflow-1.5.h |  61 +++
 include/openvswitch/ofp-msgs.h  |  15 +
 lib/automake.mk                 |   2 +
 lib/ofp-parse.c                 |  45 +-
 lib/ofp-print.c                 |  16 +
 lib/ofp-util.c                  | 192 +++++++-
 lib/ox-stat.c                   | 982 ++++++++++++++++++++++++++++++++++++++++
 lib/ox-stat.h                   |  43 ++
 lib/rconn.c                     |   4 +
 lib/vconn.c                     |   3 +-
 ofproto/ofproto.c               |   8 +
 tests/ofproto-dpif.at           |   1 -
 tests/ofproto.at                |   4 -
 13 files changed, 1360 insertions(+), 16 deletions(-)
 create mode 100644 lib/ox-stat.c
 create mode 100644 lib/ox-stat.h

Comments

Ben Pfaff June 14, 2017, 9:07 p.m. UTC | #1
On Fri, Jun 09, 2017 at 01:26:51PM +0530, SatyaValli wrote:
> commit 49a3592b2878a33033e5fd2e6e5ce82ebccef780
> Author: Satya Valli <satyavalli.rama@tcs.com>
> 
> OpenVswitch: OF1.5/EXT-334 Extensible Flow Entry Statistics
> 
> 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
> 
> Signed-off-by: Satya Valli <satyavalli.rama@tcs.com>
> Co-authored-by: Lavanya Harivelam <harivelam.lavanya@tcs.com> and Surya Muttamsetty <muttamsetty.surya@tcs.com>

Thank you for working on Open vSwitch.

"Co-authored-by" takes only one name.  Please use multiple
"Co-authored-by" tags if you have more than one coauthor.

"checkpatch" reports:

    warning: 1 line adds whitespace errors.
    WARNING: Line length is >79-characters long
    #137 FILE: include/openvswitch/ofp-msgs.h:656:
        OFPTYPE_OXS_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST. */

    WARNING: Line length is >79-characters long
    #138 FILE: include/openvswitch/ofp-msgs.h:657:
        OFPTYPE_OXS_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST15_OXS_AGGREGATE_REPLY. */

    ERROR: Inappropriate bracing around statement
    #483 FILE: lib/ofp-util.c:3331:
            if (error)

    ERROR: Improper whitespace around control block
    #1114 FILE: lib/ox-stat.c:534:
        HMAP_FOR_EACH_IN_BUCKET(oxfs, header_node, hash_int(header_no_len, 0),

    ERROR: Improper whitespace around control block
    #1135 FILE: lib/ox-stat.c:555:
        LIST_FOR_EACH(oxfs, ox_node, &oxs_ox_map[id]) {

    ERROR: Improper whitespace around control block
    #1298 FILE: lib/ox-stat.c:718:
         if(fs) {

    ERROR: Improper whitespace around control block
    #1308 FILE: lib/ox-stat.c:728:
         if(fs) {

    ERROR: Improper whitespace around control block
    #1318 FILE: lib/ox-stat.c:738:
          if(fs) {

Clang reports:

    ../lib/ofp-util.c:3466:42: error: implicit conversion from enumeration type 'enum ofputil_protocol' to different enumeration type 'enum ofp_version' [-Werror,-Wenum-conversion]

"sparse" reports:

    ../lib/ox-stat.c:282:45: warning: constant 0xFFFFFFFF00000000 is so big it is unsigned long long
    ../lib/ox-stat.c:291:42: warning: constant 0xFFFFFFFF00000000 is so big it is unsigned long long
    ../lib/ox-stat.c:127:9: warning: symbol 'oxs_field_set' was not declared. Should it be static?
    ../lib/ox-stat.c:280:31: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:280:31:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:280:31:    got unsigned long long [unsigned] [addressable] [usertype] duration
    ../lib/ox-stat.c:290:32: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:290:32:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:290:32:    got unsigned long long [unsigned] [addressable] [usertype] idle_time
    ../lib/ox-stat.c:298:39: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:298:39:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:298:39:    got unsigned long long [unsigned] [addressable] [usertype] packet_count
    ../lib/ox-stat.c:305:37: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:305:37:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:305:37:    got unsigned long long [unsigned] [addressable] [usertype] byte_count
    ../lib/ox-stat.c:443:40: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:443:40:    expected restricted ovs_be32 [usertype] x
    ../lib/ox-stat.c:443:40:    got unsigned int [unsigned] [addressable] [usertype] flow_count
    ../lib/ox-stat.c:450:43: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:450:43:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:450:43:    got unsigned long long [unsigned] [addressable] [usertype] packet_count
    ../lib/ox-stat.c:457:41: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:457:41:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:457:41:    got unsigned long long [unsigned] [addressable] [usertype] byte_count
    ../lib/ox-stat.c:926:49: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:926:49:    expected restricted ovs_be32 [usertype] x
    ../lib/ox-stat.c:926:49:    got unsigned long long
    ../lib/ox-stat.c:927:39: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:927:39:    expected restricted ovs_be32 [usertype] x
    ../lib/ox-stat.c:927:39:    got unsigned int [unsigned] [usertype] <noident>
    ../lib/ox-stat.c:934:39: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:934:39:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:934:39:    got unsigned long long [unsigned] [addressable] [usertype] packet_count
    ../lib/ox-stat.c:942:37: warning: incorrect type in argument 1 (different base types)
    ../lib/ox-stat.c:942:37:    expected restricted ovs_be64 [usertype] <noident>
    ../lib/ox-stat.c:942:37:    got unsigned long long [unsigned] [addressable] [usertype] byte_count
    ../lib/ox-stat.c:720:18: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:720:18:    expected unsigned int [unsigned] [assigned] [usertype] flow_count
    ../lib/ox-stat.c:720:18:    got restricted ovs_be32
    ../lib/ox-stat.c:730:19: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:730:19:    expected unsigned long long [unsigned] [assigned] [usertype] pkt_count
    ../lib/ox-stat.c:730:19:    got restricted ovs_be64
    ../lib/ox-stat.c:740:20: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:740:20:    expected unsigned long long [unsigned] [assigned] [usertype] byte_count
    ../lib/ox-stat.c:740:20:    got restricted ovs_be64
    ../lib/ox-stat.c:788:18: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:788:18:    expected unsigned long long [unsigned] [assigned] [usertype] duration
    ../lib/ox-stat.c:788:18:    got restricted ovs_be64
    ../lib/ox-stat.c:804:19: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:804:19:    expected unsigned long long [unsigned] [assigned] [usertype] pkt_count
    ../lib/ox-stat.c:804:19:    got restricted ovs_be64
    ../lib/ox-stat.c:820:20: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:820:20:    expected unsigned long long [unsigned] [assigned] [usertype] byte_count
    ../lib/ox-stat.c:820:20:    got restricted ovs_be64
    ../lib/ox-stat.c:619:22: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:619:22:    expected unsigned long long [unsigned] [assigned] [usertype] duration
    ../lib/ox-stat.c:619:22:    got restricted ovs_be64
    ../lib/ox-stat.c:629:22: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:629:22:    expected unsigned long long [unsigned] [assigned] [usertype] idl_time
    ../lib/ox-stat.c:629:22:    got restricted ovs_be64
    ../lib/ox-stat.c:645:23: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:645:23:    expected unsigned long long [unsigned] [assigned] [usertype] pkt_count
    ../lib/ox-stat.c:645:23:    got restricted ovs_be64
    ../lib/ox-stat.c:655:24: warning: incorrect type in assignment (different base types)
    ../lib/ox-stat.c:655:24:    expected unsigned long long [unsigned] [assigned] [usertype] byte_count
    ../lib/ox-stat.c:655:24:    got restricted ovs_be64
    ../lib/ofp-util.c:3466:42: warning: mixing different enum types
    ../lib/ofp-util.c:3466:42:     int enum ofputil_protocol  versus
    ../lib/ofp-util.c:3466:42:     int enum ofp_version 

Please fix all of these problems and resubmit your patch.
Satyavalli Rama June 19, 2017, 7:56 a.m. UTC | #2
Hi Ben,

Much Thanks for your initial reveiw comments.
We have addressed almost all problems and re-submitted the patch except the below two lines.

"checkpatch" reports:

    warning: 1 line adds whitespace errors.
    WARNING: Line length is >79-characters long
    #137 FILE: include/openvswitch/ofp-msgs.h:656:
        OFPTYPE_OXS_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST. */

    WARNING: Line length is >79-characters long
    #138 FILE: include/openvswitch/ofp-msgs.h:657:
        OFPTYPE_OXS_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST15_OXS_AGGREGATE_REPLY. */

We tried to address the above two lines also, but we are getting "unexpected syntax within OFPTYPE_ definition" error as below, if we are trying to adjust them.
PYTHONPATH=./python":"$PYTHONPATH PYTHONDONTWRITEBYTECODE=yes /usr/bin/python ./build-aux/extract-ofp-msgs \
		./include/openvswitch/ofp-msgs.h lib/ofp-msgs.inc > lib/ofp-msgs.inc.tmp && mv lib/ofp-msgs.inc.tmp lib/ofp-msgs.inc
./include/openvswitch/ofp-msgs.h:659: unexpected syntax within OFPTYPE_ definition
make[2]: *** [lib/ofp-msgs.inc] Error 1
make[2]: Leaving directory `/home/tcs/freshgitjune15/ovsfinalpatch/ovs'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/home/tcs/freshgitjune15/ovsfinalpatch/ovs'
make: *** [all] Error 2


Thanks & Regards
Satya Valli
Tata Consultancy Services
Mailto: satyavalli.rama@tcs.com
Website: http://www.tcs.com
Ben Pfaff June 20, 2017, 2:08 a.m. UTC | #3
On Mon, Jun 19, 2017 at 01:26:54PM +0530, Satyavalli Rama wrote:
> Hi Ben,
> 
> Much Thanks for your initial reveiw comments.
> We have addressed almost all problems and re-submitted the patch except the below two lines.
> 
> "checkpatch" reports:
> 
>     warning: 1 line adds whitespace errors.
>     WARNING: Line length is >79-characters long
>     #137 FILE: include/openvswitch/ofp-msgs.h:656:
>         OFPTYPE_OXS_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST. */
> 
>     WARNING: Line length is >79-characters long
>     #138 FILE: include/openvswitch/ofp-msgs.h:657:
>         OFPTYPE_OXS_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST15_OXS_AGGREGATE_REPLY. */
> 
> We tried to address the above two lines also, but we are getting "unexpected syntax within OFPTYPE_ definition" error as below, if we are trying to adjust them.

OK.  You can ignore those reports from checkpatch.
diff mbox

Patch

diff --git a/include/openflow/openflow-1.5.h b/include/openflow/openflow-1.5.h
index 73b76d8..a59eda9 100644
--- a/include/openflow/openflow-1.5.h
+++ b/include/openflow/openflow-1.5.h
@@ -163,4 +163,65 @@  struct ofp15_packet_out {
 };
 OFP_ASSERT(sizeof(struct ofp15_packet_out) == 8);
 
+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.  */
+};
+
+/* Flow removed (datapath -> controller). */
+struct ofp15_flow_removed {
+    ovs_be64 cookie;            /* Opaque controller-issued identifier. */
+    ovs_be16 priority;          /* Priority level of flow entry. */
+    uint8_t reason;             /* One of OFPRR_*. */
+    uint8_t table_id;           /* ID of the table */
+    uint8_t pad2[4];            /* Align to 64-bits. */
+};
+
+OFP_ASSERT(sizeof (struct ofp15_flow_removed) == 16);
+
 #endif /* openflow/openflow-1.5.h */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index 6dc0b60..4176df8 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -163,6 +163,8 @@  enum ofpraw {
     OFPRAW_OFPT10_FLOW_REMOVED,
     /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */
     OFPRAW_OFPT11_FLOW_REMOVED,
+    /* OFPT 1.5+ (35): struct ofp15_flow_removed,uint8_t[8][]. */
+    OFPRAW_OFPT15_FLOW_REMOVED,
     /* NXT 1.0+ (14): struct nx_flow_removed, uint8_t[8][]. */
     OFPRAW_NXT_FLOW_REMOVED,
 
@@ -289,6 +291,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,
 
@@ -298,6 +302,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,
 
@@ -305,11 +311,15 @@  enum ofpraw {
     OFPRAW_OFPST10_AGGREGATE_REQUEST,
     /* OFPST 1.1+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
     OFPRAW_OFPST11_AGGREGATE_REQUEST,
+    /* OFPST 1.5+ (18): struct ofp15_oxs_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST,
     /* NXST 1.0 (1): struct nx_flow_stats_request, uint8_t[8][]. */
     OFPRAW_NXST_AGGREGATE_REQUEST,
 
     /* OFPST 1.0+ (2): struct ofp_aggregate_stats_reply. */
     OFPRAW_OFPST_AGGREGATE_REPLY,
+    /* OFPST 1.5+ (18): uint8_t[] . */
+    OFPRAW_OFPST15_OXS_AGGREGATE_REPLY,
     /* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */
     OFPRAW_NXST_AGGREGATE_REPLY,
 
@@ -555,6 +565,7 @@  enum ofptype {
                                   * OFPRAW_NXT_PACKET_IN. */
     OFPTYPE_FLOW_REMOVED,        /* OFPRAW_OFPT10_FLOW_REMOVED.
                                   * OFPRAW_OFPT11_FLOW_REMOVED.
+                                  * OFPRAW_OFPT15_FLOW_REMOVED.
                                   * OFPRAW_NXT_FLOW_REMOVED. */
     OFPTYPE_PORT_STATUS,         /* OFPRAW_OFPT10_PORT_STATUS.
                                   * OFPRAW_OFPT11_PORT_STATUS.
@@ -635,11 +646,15 @@  enum ofptype {
                                       * OFPRAW_OFPST11_FLOW_REPLY.
                                       * OFPRAW_OFPST13_FLOW_REPLY.
                                       * OFPRAW_NXST_FLOW_REPLY. */
+    OFPTYPE_OXS_FLOW_STATS_REPLY,    /* OFPRAW_OFPST15_OXS_FLOW_REPLY. */
+    OFPTYPE_OXS_FLOW_STATS_REQUEST,  /* OFPRAW_OFPST15_OXS_FLOW_REQUEST. */
     OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
                                       * OFPRAW_OFPST11_AGGREGATE_REQUEST.
                                       * OFPRAW_NXST_AGGREGATE_REQUEST. */
     OFPTYPE_AGGREGATE_STATS_REPLY,   /* OFPRAW_OFPST_AGGREGATE_REPLY.
                                       * OFPRAW_NXST_AGGREGATE_REPLY. */
+    OFPTYPE_OXS_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST. */
+    OFPTYPE_OXS_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST15_OXS_AGGREGATE_REPLY. */
     OFPTYPE_TABLE_STATS_REQUEST,     /* OFPRAW_OFPST_TABLE_REQUEST. */
     OFPTYPE_TABLE_STATS_REPLY,       /* OFPRAW_OFPST10_TABLE_REPLY.
                                       * OFPRAW_OFPST11_TABLE_REPLY.
diff --git a/lib/automake.mk b/lib/automake.mk
index f5baba2..f03f02e 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 2e49422..1381afc 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;
@@ -408,10 +438,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 423df31..c6ae25f 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -3600,6 +3600,12 @@  ofp_to_string__(const struct ofp_header *oh,
         ofp_print_flow_stats_request(string, oh, port_map);
         break;
 
+    case OFPTYPE_OXS_FLOW_STATS_REQUEST:
+    case OFPTYPE_OXS_AGGREGATE_STATS_REQUEST:
+        ofp_print_stats(string, oh);
+        ofp_print_flow_stats_request(string, oh, port_map);
+        break;
+
     case OFPTYPE_TABLE_STATS_REQUEST:
         ofp_print_stats(string, oh);
         break;
@@ -3624,6 +3630,11 @@  ofp_to_string__(const struct ofp_header *oh,
         ofp_print_flow_stats_reply(string, oh, port_map);
         break;
 
+    case OFPTYPE_OXS_FLOW_STATS_REPLY:
+        ofp_print_stats(string, oh);
+        ofp_print_flow_stats_reply(string, oh, port_map);
+        break;
+
     case OFPTYPE_QUEUE_STATS_REPLY:
         ofp_print_stats(string, oh);
         ofp_print_ofpst_queue_reply(string, oh, port_map, verbosity);
@@ -3644,6 +3655,11 @@  ofp_to_string__(const struct ofp_header *oh,
         ofp_print_aggregate_stats_reply(string, oh);
         break;
 
+    case OFPTYPE_OXS_AGGREGATE_STATS_REPLY:
+        ofp_print_stats(string, oh);
+        ofp_print_aggregate_stats_reply(string, oh);
+        break;
+
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
         ofp_print_stats(string, oh);
         ofp_print_ofpst_port_desc_request(string, oh, port_map);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 601a719..88bd83e 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -51,9 +51,12 @@ 
 #include "unaligned.h"
 #include "util.h"
 #include "uuid.h"
+#include "ox-stat.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_util);
 
+extern uint8_t oxs_field_set;
+
 /* Rate limit for OpenFlow message parse errors.  These always indicate a bug
  * in the peer and so there's not much point in showing a lot of them. */
 static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -2315,6 +2318,40 @@  ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
 }
 
 static enum ofperr
+ofputil_decode_ofpst15_flow_request(struct ofputil_flow_stats_request *fsr,
+                                    struct ofpbuf *b, bool aggregate,
+                                    const struct tun_table *tun_table,
+                                    const struct vl_mff_map *vl_mff_map)
+{
+    const struct ofp15_oxs_flow_stats_request *ofsr;
+    enum ofperr error, stat_error;
+    uint16_t statlen;
+
+    ofsr = ofpbuf_pull(b, sizeof *ofsr);
+    fsr->aggregate = aggregate;
+    fsr->table_id = ofsr->table_id;
+
+    error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port);
+    if (error) {
+        return error;
+    }
+
+    fsr->out_group = ntohl(ofsr->out_group);
+    fsr->cookie = ofsr->cookie;
+    fsr->cookie_mask = ofsr->cookie_mask;
+
+    error = ofputil_pull_ofp11_match(b, tun_table, vl_mff_map, &fsr->match,
+                                     NULL);
+    stat_error = oxs_pull_stat(b, NULL, &statlen);
+
+    if (error || stat_error) {
+        return error;
+    }
+
+    return 0;
+}
+
+static enum ofperr
 ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
                                  struct ofpbuf *b, bool aggregate,
                                  const struct tun_table *tun_table,
@@ -2764,6 +2801,16 @@  ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
         return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
                                                    vl_mff_map);
 
+    case OFPRAW_OFPST15_OXS_FLOW_REQUEST:
+        oxs_field_set = 0;
+        return ofputil_decode_ofpst15_flow_request(fsr, &b, false, tun_table,
+                                                   vl_mff_map);
+
+    case OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST:
+        oxs_field_set = 0;
+        return ofputil_decode_ofpst15_flow_request(fsr, &b, true, tun_table,
+                                                   vl_mff_map);
+
     case OFPRAW_NXST_FLOW_REQUEST:
         return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table,
                                                 vl_mff_map);
@@ -2789,12 +2836,29 @@  ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     enum ofpraw raw;
 
     switch (protocol) {
+    case OFPUTIL_P_OF15_OXM:
+    case OFPUTIL_P_OF16_OXM: {
+        struct ofp15_oxs_flow_stats_request *ofsr;
+
+        raw = (fsr->aggregate
+               ? OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST
+               : OFPRAW_OFPST15_OXS_FLOW_REQUEST);
+        msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
+                           ofputil_match_typical_len(protocol));
+        ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+        ofsr->table_id = fsr->table_id;
+        ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
+        ofsr->out_group = htonl(fsr->out_group);
+        ofsr->cookie = fsr->cookie;
+        ofsr->cookie_mask = fsr->cookie_mask;
+        ofputil_put_ofp11_match(msg, &fsr->match, protocol);
+        oxs_put_stat(msg, NULL, ofputil_protocol_to_ofp_version(protocol));
+        break;
+    }
     case OFPUTIL_P_OF11_STD:
     case OFPUTIL_P_OF12_OXM:
     case OFPUTIL_P_OF13_OXM:
-    case OFPUTIL_P_OF14_OXM:
-    case OFPUTIL_P_OF15_OXM:
-    case OFPUTIL_P_OF16_OXM: {
+    case OFPUTIL_P_OF14_OXM: {
         struct ofp11_flow_stats_request *ofsr;
 
         raw = (fsr->aggregate
@@ -2894,6 +2958,47 @@  ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
 
     if (!msg->size) {
         return EOF;
+    } else if (raw == OFPRAW_OFPST15_OXS_FLOW_REPLY) {
+        const struct ofp15_oxs_flow_stats_reply *ofs;
+        size_t length;
+        uint16_t padded_match_len;
+        uint16_t stat_len;
+
+        ofs = ofpbuf_try_pull(msg, sizeof *ofs);
+        if (!ofs) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %" PRIu32
+                         " leftover " "bytes at end", msg->size);
+            return EINVAL;
+        }
+
+        length = ntohs(ofs->length);
+        if (length < sizeof *ofs) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid "
+                         "length %" PRIuSIZE, length);
+            return EINVAL;
+        }
+
+        if (ofputil_pull_ofp11_match
+            (msg, NULL, NULL, &fs->match, &padded_match_len)) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
+            return EINVAL;
+        }
+
+        fs->priority = ntohs(ofs->priority);
+        fs->table_id = ofs->table_id;
+        fs->duration_sec = 0;
+        fs->duration_nsec = 0;
+        fs->idle_age = 0;
+        fs->packet_count = 0;
+        fs->byte_count = 0;
+        fs->cookie = 0;
+        fs->importance =0;
+        fs->idle_timeout=0;
+        if (oxs_pull_stat(msg, fs, &stat_len)) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OXS OFPST_FLOW reply bad match");
+            return EINVAL;
+        }
+        instructions_len = length - sizeof *ofs - padded_match_len - stat_len;
     } else if (raw == OFPRAW_OFPST11_FLOW_REPLY
                || raw == OFPRAW_OFPST13_FLOW_REPLY) {
         const struct ofp11_flow_stats *ofs;
@@ -3068,7 +3173,23 @@  ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
     orig_tun_table = fs->match.flow.tunnel.metadata.tab;
     fs_->match.flow.tunnel.metadata.tab = tun_table;
 
-    if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
+    if (raw == OFPRAW_OFPST15_OXS_FLOW_REPLY) {
+        struct ofp15_oxs_flow_stats_reply *ofs;
+
+        ofpbuf_put_uninit(reply, sizeof *ofs);
+        oxm_put_match(reply, &fs->match, version);
+        oxs_put_stat(reply, fs, version);
+        ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
+                                      version);
+
+        ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+        ofs->length = htons(reply->size - start_ofs);
+        ofs->table_id = fs->table_id;
+        ofs->priority = htons(fs->priority);
+        ofs->reason = 0;
+        memset(ofs->pad2, 0, sizeof ofs->pad2);
+    } else if (raw == OFPRAW_OFPST11_FLOW_REPLY ||
+               raw == OFPRAW_OFPST13_FLOW_REPLY) {
         struct ofp11_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
@@ -3170,6 +3291,12 @@  ofputil_encode_aggregate_stats_reply(
     enum ofpraw raw;
 
     ofpraw_decode(&raw, request);
+    if (raw == OFPRAW_OFPST15_OXS_AGGREGATE_REQUEST) {
+        enum ofp_version version = request->version;
+
+        msg = ofpraw_alloc_stats_reply(request, 0);
+        oxs_put_agg_stat(msg, stats, version);
+    } else {
     if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
         packet_count = unknown_to_zero(stats->packet_count);
         byte_count = unknown_to_zero(stats->byte_count);
@@ -3183,6 +3310,7 @@  ofputil_encode_aggregate_stats_reply(
     put_32aligned_be64(&asr->packet_count, htonll(packet_count));
     put_32aligned_be64(&asr->byte_count, htonll(byte_count));
     asr->flow_count = htonl(stats->flow_count);
+    }
 
     return msg;
 }
@@ -3192,12 +3320,21 @@  ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
                                      const struct ofp_header *reply)
 {
     struct ofpbuf msg = ofpbuf_const_initializer(reply, ntohs(reply->length));
-    ofpraw_pull_assert(&msg);
+    enum ofperr error;
+    enum ofpraw raw;
 
+    raw = ofpraw_pull_assert(&msg);
+    if (raw == OFPRAW_OFPST15_OXS_AGGREGATE_REPLY) {
+        memset(stats, 0, sizeof (*stats));
+        error = oxs_pull_agg_stat(msg, stats);
+        if (error)
+            return error;
+    } else {
     struct ofp_aggregate_stats_reply *asr = msg.msg;
     stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
     stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
     stats->flow_count = ntohl(asr->flow_count);
+    }
 
     return 0;
 }
@@ -3211,7 +3348,23 @@  ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     enum ofpraw raw = ofpraw_pull_assert(&b);
-    if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+    if (raw == OFPRAW_OFPT15_FLOW_REMOVED) {
+        const struct ofp15_flow_removed *ofr;
+        enum ofperr error;
+
+        ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+        error = ofputil_pull_ofp11_match(&b, NULL, NULL,  &fr->match, NULL);
+        if (error) {
+            return error;
+        }
+        oxs_flow_removed_stat_pull(&b,fr);
+
+        fr->priority = ntohs(ofr->priority);
+        fr->cookie = ofr->cookie;
+        fr->reason = ofr->reason;
+        fr->table_id = ofr->table_id;
+    } else if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
         const struct ofp12_flow_removed *ofr;
         enum ofperr error;
 
@@ -3294,12 +3447,29 @@  ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     }
 
     switch (protocol) {
+    case OFPUTIL_P_OF15_OXM:
+    case OFPUTIL_P_OF16_OXM: {
+        struct ofp15_flow_removed *ofr;
+
+        msg = ofpraw_alloc_xid(OFPRAW_OFPT15_FLOW_REMOVED,
+                               ofputil_protocol_to_ofp_version(protocol),
+                               htonl(0),
+                               ofputil_match_typical_len(protocol));
+        ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+        ofr->cookie = fr->cookie;
+        ofr->priority = htons(fr->priority);
+        ofr->reason = reason;
+        ofr->table_id = fr->table_id;
+        ofputil_put_ofp11_match(msg, &fr->match, protocol);
+        /*Stats encoding in OXS TLV Format*/
+        oxs_flow_removed_stat_put(msg,fr,protocol);
+        break;
+    }
+
     case OFPUTIL_P_OF11_STD:
     case OFPUTIL_P_OF12_OXM:
     case OFPUTIL_P_OF13_OXM:
-    case OFPUTIL_P_OF14_OXM:
-    case OFPUTIL_P_OF15_OXM:
-    case OFPUTIL_P_OF16_OXM: {
+    case OFPUTIL_P_OF14_OXM: {
         struct ofp12_flow_removed *ofr;
 
         msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
@@ -10400,7 +10570,9 @@  ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_GET_ASYNC_REQUEST:
     case OFPTYPE_DESC_STATS_REQUEST:
     case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_OXS_FLOW_STATS_REQUEST:
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
+    case OFPTYPE_OXS_AGGREGATE_STATS_REQUEST:
     case OFPTYPE_TABLE_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_DESC_REQUEST:
@@ -10428,10 +10600,12 @@  ofputil_is_bundlable(enum ofptype type)
     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:
     case OFPTYPE_AGGREGATE_STATS_REPLY:
+    case OFPTYPE_OXS_AGGREGATE_STATS_REPLY:
     case OFPTYPE_PORT_DESC_STATS_REPLY:
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_FLOW_MONITOR_PAUSED:
diff --git a/lib/ox-stat.c b/lib/ox-stat.c
new file mode 100644
index 0000000..6c3dec6
--- /dev/null
+++ b/lib/ox-stat.c
@@ -0,0 +1,982 @@ 
+/*
+ * 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 <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"
+#include "ox-stat.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);
+void oxs_put__(struct ofpbuf *, enum oxs_ofb_stat_fields,
+               enum ofp_version, const void *, const void *, size_t);
+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 enum ofperr oxs_pull_entry__(struct ofpbuf *b, uint64_t * header,
+                                    const struct oxs_field **field_,
+                                    struct ofputil_flow_stats *fs);
+static enum ofperr oxs_pull_match_entry(struct ofpbuf *b,
+                                        const struct oxs_field **field,
+                                        struct ofputil_flow_stats *fs);
+static enum ofperr oxs_pull_raw(const uint8_t *, unsigned int ,
+                                struct ofputil_flow_stats *fs,
+                                ovs_be64 * cookie, ovs_be64 * cookie_mask);
+static enum ofperr oxs_pull_agg_raw(const uint8_t * p, unsigned int stat_len,
+                                    struct ofputil_aggregate_stats *fs);
+static int oxs_flow_rem_stat_fields_pull(const uint8_t *p,
+                                         unsigned int stat_len,
+                                         struct ofputil_flow_removed *fr);
+static int oxs_pull_flow_rem_stat_entry(struct ofpbuf *b,
+                                        struct ofputil_flow_removed *fr);
+static void oxs_init(void);
+static void oxs_put_header__(struct ofpbuf *b, uint64_t header);
+static void oxs_put_header_len(struct ofpbuf *b,
+                               enum oxs_ofb_stat_fields field,
+                               enum ofp_version version);
+static int ox_put_agg_raw(struct ofpbuf *b, enum ofp_version oxs,
+                          const struct ofputil_aggregate_stats *fs);
+static void oxs_put_duration(struct ofpbuf *b,
+                             const struct ofputil_flow_removed *fr,
+                             enum ofp_version version);
+static void oxs_put_packet_count(struct ofpbuf *b,
+                                 const struct ofputil_flow_removed *fr,
+                                 enum ofp_version version);
+static void oxs_put_byte_count(struct ofpbuf *b,
+                               const struct ofputil_flow_removed *fr,
+                               enum ofp_version version);
+static int oxs_flow_rem_stat_fields_put(struct ofpbuf *b,
+                                        const struct ofputil_flow_removed *fr,
+                                        enum ofp_version version);
+
+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 enum ofperr
+oxs_pull_entry__(struct ofpbuf *b, uint64_t * header,
+                 const struct oxs_field **field_,
+                 struct ofputil_flow_stats *fs)
+{
+    const struct oxs_field *field;
+    enum ofperr header_error;
+    unsigned int payload_len;
+    const uint8_t *payload;
+
+    header_error = oxs_pull_header__(b, header, &field);
+
+    if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) {
+        return header_error;
+    }
+
+    payload_len = oxs_payload_len(*header);
+    payload = ofpbuf_try_pull(b, payload_len);
+    if (!payload) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    if (fs && field) {
+        switch (field->id) {
+        case OFPXST_OFB_DURATION: {
+            uint64_t duration = 0;
+
+            memcpy(&duration, payload, sizeof (duration));
+            duration = ntohll(duration);
+            fs->duration_sec = ((uint32_t) ((duration &
+                                            0xFFFFFFFF00000000) >> 32));
+            fs->duration_nsec = ((uint32_t) (duration & 0xFFFFFFFF));
+        }
+        break;
+        case OFPXST_OFB_IDLE_TIME: {
+            uint64_t idle_time = 0;
+
+            memcpy(&idle_time, payload, sizeof (idle_time));
+            idle_time = ntohll(idle_time);
+            fs->idle_age = ((idle_time & 0xFFFFFFFF00000000) >> 32);
+        }
+        break;
+        case OFPXST_OFB_PACKET_COUNT: {
+            uint64_t packet_count;
+
+            memcpy(&packet_count, payload, sizeof (packet_count));
+            fs->packet_count = ntohll(packet_count);
+        }
+        break;
+        case OFPXST_OFB_BYTE_COUNT: {
+            uint64_t byte_count;
+
+            memcpy(&byte_count, payload, sizeof (byte_count));
+            fs->byte_count = ntohll(byte_count);
+        }
+        break;
+        case OFPXST_OFB_FLOW_COUNT:
+            break;
+        }
+    }
+
+    if (field_) {
+        *field_ = field;
+        return header_error;
+    }
+
+    return 0;
+}
+
+static enum ofperr
+oxs_pull_match_entry(struct ofpbuf *b,
+                     const struct oxs_field **field,
+                     struct ofputil_flow_stats *fs)
+{
+    enum ofperr error;
+    uint64_t header;
+
+    error = oxs_pull_entry__(b, &header, field, fs);
+    if (error) {
+        return error;
+    }
+    return 0;
+}
+
+static enum ofperr
+oxs_pull_raw(const uint8_t * p, unsigned int stat_len,
+             struct ofputil_flow_stats *fs,
+             ovs_be64 * cookie, ovs_be64 * cookie_mask)
+{
+    ovs_assert((cookie != NULL) == (cookie_mask != NULL));
+    if (cookie) {
+        *cookie = *cookie_mask = htonll(0);
+    }
+
+    struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+
+    while (b.size) {
+        const uint8_t *pos = b.data;
+        const struct oxs_field *field;
+        union mf_value value;
+        union mf_value mask;
+        enum ofperr error;
+
+        error = oxs_pull_match_entry(&b, &field, fs);
+        if (error) {
+            if (error == OFPERR_OFPBMC_BAD_FIELD && !false) {
+                continue;
+            }
+        } else if (!field) {
+            if (!cookie) {
+                error = OFPERR_OFPBMC_BAD_FIELD;
+            } else if (*cookie_mask) {
+                error = OFPERR_OFPBMC_DUP_FIELD;
+            } else {
+                *cookie = value.be64;
+                *cookie_mask = mask.be64;
+            }
+        } else {
+            if (field->id == OFPXST_OFB_DURATION) {
+                oxs_field_set |= 1 << 0;
+            } else if (field->id == OFPXST_OFB_IDLE_TIME) {
+                oxs_field_set |= 1 << 1;
+            } else if (field->id == OFPXST_OFB_FLOW_COUNT) {
+                oxs_field_set |= 1 << 2;
+            } else if (field->id == OFPXST_OFB_PACKET_COUNT) {
+                oxs_field_set |= 1 << 3;
+            } else if (field->id == OFPXST_OFB_BYTE_COUNT) {
+                oxs_field_set |= 1 << 4;
+            }
+        }
+        if (error) {
+            VLOG_DBG_RL(&rl, "error parsing OXS at offset %" PRIdPTR " "
+                        "within match (%s)", pos - p, ofperr_to_string(error));
+            return error;
+        }
+    }
+    return 0;
+}
+
+/* Retrieve  struct ofp_oxs_stat from 'b', followed by the flow entry
+ * statistics in OXS format.
+ *
+ * Returns error if message parsing fails, otherwise returns zero . */
+int
+oxs_pull_stat(struct ofpbuf *b, struct ofputil_flow_stats *fs,
+              uint16_t * statlen)
+{
+    struct ofp_oxs_stat *oxs = b->data;
+    uint8_t *p;
+    uint16_t stat_len;
+
+    stat_len = ntohs(oxs->length);
+    if (stat_len < sizeof *oxs) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    p = ofpbuf_try_pull(b, ROUND_UP(stat_len, 8));
+    if (!p) {
+        VLOG_DBG_RL(&rl, "oxs length %u, rounded up to a "
+                    "multiple of 8, is longer than space in message (max "
+                    "length %" PRIu32 ")", stat_len, b->size);
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+    *statlen = ROUND_UP(stat_len, 8);
+    return oxs_pull_raw(p + sizeof *oxs, stat_len - sizeof *oxs, fs, NULL,
+                        NULL);
+}
+
+static enum ofperr
+oxs_pull_agg_raw(const uint8_t * p, unsigned int stat_len,
+                 struct ofputil_aggregate_stats *fs)
+{
+    struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+
+    while (b.size) {
+
+        uint64_t header;
+        unsigned int payload_len;
+        const struct oxs_field *field;
+        const uint8_t *payload;
+
+        oxs_pull_header__(&b, &header, &field);
+        payload_len = oxs_payload_len(header);
+        payload = ofpbuf_try_pull(&b, payload_len);
+
+        if (fs && field) {
+            switch (field->id) {
+            case OFPXST_OFB_FLOW_COUNT: {
+                uint32_t flow_count = 0;
+
+                memcpy(&flow_count, payload, sizeof (flow_count));
+                fs->flow_count = ntohl(flow_count);
+            }
+            break;
+            case OFPXST_OFB_PACKET_COUNT: {
+                uint64_t packet_count;
+
+                memcpy(&packet_count, payload, sizeof (packet_count));
+                fs->packet_count = ntohll(packet_count);
+            }
+            break;
+            case OFPXST_OFB_BYTE_COUNT: {
+                uint64_t byte_count;
+
+                memcpy(&byte_count, payload, sizeof (byte_count));
+                fs->byte_count = ntohll(byte_count);
+            }
+            break;
+            case OFPXST_OFB_DURATION:
+            case OFPXST_OFB_IDLE_TIME:
+                break;
+            }
+
+        }
+
+    }
+    return 0;
+}
+
+/* Retrieve struct ofp_oxs_stat from 'b', followed by the aggregate flow entry
+ * statistics in OXS format.
+ *
+ * Returns error if message parsing fails, otherwise returns zero . */
+int
+oxs_pull_agg_stat(struct ofpbuf b, struct ofputil_aggregate_stats *fs)
+{
+    struct ofp_oxs_stat *oxs = b.data;
+    uint8_t *p;
+    uint16_t stat_len;
+
+    stat_len = ntohs(oxs->length);
+
+    if (stat_len < sizeof *oxs) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    p = ofpbuf_try_pull(&b, ROUND_UP(stat_len, 8));
+    if (!p) {
+        VLOG_DBG_RL(&rl, "oxs length %u, rounded up to a "
+                    "multiple of 8, is longer than space in message (max "
+                    "length %" PRIu32 ")", stat_len, b.size);
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    return oxs_pull_agg_raw(p + sizeof *oxs, stat_len - sizeof *oxs, fs);
+}
+
+static struct hmap oxs_header_map;
+static struct hmap oxs_name_map;
+
+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);
+    }
+}
+
+static const struct oxs_field *
+oxs_field_by_header(uint32_t header)
+{
+    const struct oxs_field_index *oxfs;
+    uint32_t header_no_len;
+
+    oxs_init();
+
+    header_no_len = oxs_header_no_len(header);
+    HMAP_FOR_EACH_IN_BUCKET(oxfs, header_node, hash_int(header_no_len, 0),
+                            &oxs_header_map) {
+        if (header_no_len == oxs_header_no_len(oxfs->fs.header)) {
+            if (OXS_LENGTH(header) == OXS_LENGTH(oxfs->fs.header)) {
+                return &oxfs->fs;
+            } else {
+                return NULL;
+            }
+        }
+    }
+    return NULL;
+}
+
+static const struct oxs_field *
+oxs_field_by_id(enum oxs_ofb_stat_fields id, enum ofp_version version)
+{
+    const struct oxs_field_index *oxfs;
+    const struct oxs_field *fs = NULL;
+
+    oxs_init();
+
+    LIST_FOR_EACH(oxfs, ox_node, &oxs_ox_map[id]) {
+        if (!fs || version >= oxfs->fs.version) {
+            fs = &oxfs->fs;
+        }
+    }
+    return fs;
+}
+
+static void
+oxs_put_header__(struct ofpbuf *b, uint64_t header)
+{
+    ovs_be32 network_header = htonl(header);
+
+    ofpbuf_put(b, &network_header, oxs_header_len(header));
+}
+
+static void
+oxs_put_header_len(struct ofpbuf *b, enum oxs_ofb_stat_fields field,
+                   enum ofp_version version)
+{
+    uint32_t header = oxs_header_get(field, version);
+
+    header = OXS_HEADER(OXS_CLASS(header),
+                        OXS_FIELD(header), OXS_LENGTH(header));
+    oxs_put_header__(b, header);
+}
+
+void
+oxs_put__(struct ofpbuf *b, enum oxs_ofb_stat_fields field,
+          enum ofp_version version,
+          const void *value, const void *mask, size_t n_bytes)
+{
+    oxs_put_header_len(b, field, version);
+    ofpbuf_put(b, value, n_bytes);
+    if (mask) {
+        ofpbuf_put(b, mask, n_bytes);
+    }
+
+}
+
+/* Appends to 'b' the flow entry statistics in a flexible encoding ,OXS
+ * format.
+ *
+ * OXS is a TLV format to express flow entry statistics.  Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * Returns the number of bytes appended to 'b'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns zero or the number of bytes appended to 'b'. */
+static int
+ox_put_raw(struct ofpbuf *b, enum ofp_version oxs,
+           const struct ofputil_flow_stats *fs,
+           ovs_be64 cookie, ovs_be64 cookie_mask)
+{
+    const size_t start_len = b->size;
+    int stat_len;
+
+    if (oxs_field_set & 1 << 0) {
+        uint64_t duration = 0;
+
+        if (fs) {
+            duration = (uint64_t) fs->duration_sec << 32 | fs->duration_nsec;
+            duration = htonll(duration);
+        }
+        oxs_put__(b, OFPXST_OFB_DURATION, oxs, &duration, NULL,
+                  OXS_STATS_DURATION_LEN);
+    }
+    if (oxs_field_set & 1 << 1) {
+        uint64_t idl_time = 0;
+
+        if (fs) {
+            idl_time = (uint64_t) fs->idle_age << 32;
+            idl_time = htonll(idl_time);
+        }
+        oxs_put__(b, OFPXST_OFB_IDLE_TIME, oxs, &idl_time, NULL,
+                  OXS_STATS_IDLE_TIME_LEN);
+    }
+    if (oxs_field_set & 1 << 2) {
+        uint32_t flow_count = 0;
+
+        oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count, NULL,
+                  OXS_STATS_FLOW_COUNT_LEN);
+    }
+    if (oxs_field_set & 1 << 3) {
+        uint64_t pkt_count = 0;
+
+        if (fs) {
+            pkt_count = fs->packet_count;
+            pkt_count = htonll(pkt_count);
+        }
+        oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count, NULL,
+                  OXS_STATS_PACKET_COUNT_LEN);
+    }
+    if (oxs_field_set & 1 << 4) {
+        uint64_t byte_count = 0;
+
+        if (fs) {
+            byte_count = fs->byte_count;
+            byte_count = htonll(byte_count);
+        }
+        oxs_put__(b, OFPXST_OFB_BYTE_COUNT, oxs, &byte_count, NULL,
+                  OXS_STATS_BYTE_COUNT_LEN);
+    }
+    if (cookie_mask) {
+        cookie &= cookie_mask;
+        oxs_put_header__(b, OXS_OX_COOKIE);
+        ofpbuf_put(b, &cookie, sizeof cookie);
+    }
+    stat_len = b->size - start_len;
+    return stat_len;
+}
+
+/* Appends to 'b' an struct ofp_oxs_stat followed by the flow entry statistics
+ * in OXS format , plus enough zero bytes to pad the data appended out to a
+ * multiple of 8.
+ *
+ * Specify the OpenFlow version in use as 'version'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding.Never
+ * returns zero. */
+int
+oxs_put_stat(struct ofpbuf *b, const struct ofputil_flow_stats *fs,
+             enum ofp_version version)
+{
+    int stat_len;
+    struct ofp_oxs_stat *oxs;
+    size_t start_len = b->size;
+    ovs_be64 cookie = htonll(0), cookie_mask = htonll(0);
+
+    ofpbuf_put_uninit(b, sizeof *oxs);
+    stat_len = (ox_put_raw(b, version, fs, cookie, cookie_mask)
+                + sizeof *oxs);
+    ofpbuf_put_zeros(b, PAD_SIZE(stat_len, 8));
+    oxs = ofpbuf_at(b, start_len, sizeof *oxs);
+    oxs->reserved = htons(0);
+    oxs->length = htons(stat_len);
+    return stat_len;
+}
+
+/* Appends to 'b' the aggregate flow entry statistics in a flexible encoding,
+ * OXS format.
+ *
+ * OXS is a TLV format to express flow entry statistics.  Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * Returns the number of bytes appended to 'b'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns zero or the number of bytes appended to 'b'. */
+static int
+ox_put_agg_raw(struct ofpbuf *b, enum ofp_version oxs,
+               const struct ofputil_aggregate_stats *fs)
+{
+   const size_t start_len = b->size;
+   int stat_len;
+
+   if (oxs_field_set & 1<<2) {
+     uint32_t flow_count = 0;
+     if(fs) {
+       flow_count=fs->flow_count;
+       flow_count=htonl(flow_count);
+     }
+     oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count,
+                NULL, OXS_STATS_FLOW_COUNT_LEN);
+   }
+
+   if (oxs_field_set & 1<<3) {
+     uint64_t pkt_count = 0;
+     if(fs) {
+        pkt_count = fs->packet_count;
+        pkt_count = htonll(pkt_count);
+     }
+     oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count,
+               NULL, OXS_STATS_PACKET_COUNT_LEN);
+   }
+
+   if (oxs_field_set & 1<<4) {
+      uint64_t byte_count = 0;
+      if(fs) {
+        byte_count = fs->byte_count;
+        byte_count = htonll(byte_count);
+      }
+      oxs_put__(b, OFPXST_OFB_BYTE_COUNT, oxs, &byte_count,
+                NULL, OXS_STATS_BYTE_COUNT_LEN);
+   }
+
+   stat_len = b->size - start_len;
+   return stat_len;
+}
+
+/* Appends to 'b' an struct ofp_oxs_stat followed by the aggregate flow entry
+ * statistics in OXS format , plus enough zero bytes to pad the data appended
+ * out to a multiple of 8.
+ *
+ * Specify the OpenFlow version in use as 'version'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding.  Never
+ * returns zero. */
+int
+oxs_put_agg_stat(struct ofpbuf *b, const struct ofputil_aggregate_stats *fs,
+                 enum ofp_version version)
+{
+   int stat_len;
+   struct ofp_oxs_stat *oxs;
+   size_t start_len = b->size;
+
+   ofpbuf_put_uninit(b, sizeof *oxs);
+   stat_len = (ox_put_agg_raw(b, version, fs)
+                + sizeof *oxs);
+   ofpbuf_put_zeros(b, PAD_SIZE(stat_len, 8));
+   oxs = ofpbuf_at(b, start_len, sizeof *oxs);
+   oxs->reserved = htons(0);
+   oxs->length = htons(stat_len);
+
+   return stat_len;
+}
+
+static void
+oxs_put_duration(struct ofpbuf *b,
+                 const struct ofputil_flow_removed *fr,
+                 enum ofp_version version)
+{
+    uint64_t duration = 0;
+
+    if (fr) {
+        duration = (uint64_t) fr->duration_sec << 32 | fr->duration_nsec;
+        duration = htonll(duration);
+    }
+    oxs_put__(b, OFPXST_OFB_DURATION, version, &duration, NULL,
+              OXS_STATS_DURATION_LEN);
+    return;
+}
+
+static void
+oxs_put_packet_count(struct ofpbuf *b,
+                     const struct ofputil_flow_removed *fr,
+                     enum ofp_version version)
+{
+    uint64_t pkt_count = 0;
+
+    if (fr) {
+        pkt_count = fr->packet_count;
+        pkt_count = htonll(pkt_count);
+    }
+    oxs_put__(b, OFPXST_OFB_PACKET_COUNT, version, &pkt_count, NULL,
+              OXS_STATS_PACKET_COUNT_LEN);
+    return;
+}
+
+static void
+oxs_put_byte_count(struct ofpbuf *b,
+                   const struct ofputil_flow_removed *fr,
+                   enum ofp_version version)
+{
+    uint64_t byte_count = 0;
+
+    if (fr) {
+        byte_count = fr->byte_count;
+        byte_count = htonll(byte_count);
+    }
+    oxs_put__(b, OFPXST_OFB_BYTE_COUNT, version, &byte_count, NULL,
+              OXS_STATS_BYTE_COUNT_LEN);
+    return;
+}
+
+static int
+oxs_flow_rem_stat_fields_put(struct ofpbuf *b,
+                             const struct ofputil_flow_removed *fr,
+                             enum ofp_version version)
+{
+    int stat_len;
+    size_t start_len = b->size;
+
+    oxs_put_duration(b, fr, version);
+    oxs_put_packet_count(b, fr, version);
+    oxs_put_byte_count(b, fr, version);
+
+    stat_len = (b->size - start_len);
+    return stat_len;
+}
+
+/* Encode Flow entry statistics in FLOW_REMOVED message
+ *
+ * Appends to 'b' the flow entry statistics in a flexible encoding ,OXS
+ * format.
+ *
+ * OXS is a TLV format to express flow entry statistics.  Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * Returns the number of bytes appended to 'b'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns zero or the number of bytes appended to 'b'. */
+int
+oxs_flow_removed_stat_put(struct ofpbuf *b,
+                          const struct ofputil_flow_removed *fr,
+                          enum ofp_version version)
+{
+    int stat_len;
+    struct ofp_oxs_stat *oxs;
+    size_t start_len = b->size;
+
+    ofpbuf_put_uninit(b, sizeof *oxs);
+    stat_len = oxs_flow_rem_stat_fields_put(b, fr, version) + sizeof *oxs;
+    ofpbuf_put_zeros(b, PAD_SIZE(stat_len, 8));
+    oxs = ofpbuf_at(b, start_len, sizeof *oxs);
+    oxs->reserved = htons(0);
+    oxs->length = htons(stat_len);
+    return stat_len;
+}
+
+static int
+oxs_flow_rem_stat_fields_pull(const uint8_t * p, unsigned int stat_len,
+                              struct ofputil_flow_removed *fr)
+{
+    struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+
+    while (b.size) {
+
+        const uint8_t *pos = b.data;
+        enum ofperr error;
+
+        error = oxs_pull_flow_rem_stat_entry(&b, fr);
+        if (error) {
+            if (error == OFPERR_OFPBMC_BAD_FIELD && !false) {
+                continue;
+            }
+        }
+        if (error) {
+            VLOG_DBG_RL(&rl, "error parsing OXS at offset %" PRIdPTR " "
+                        "within match (%s)", pos - p, ofperr_to_string(error));
+            return error;
+        }
+    }
+    return 0;
+}
+
+static int
+oxs_pull_flow_rem_stat_entry(struct ofpbuf *b, struct ofputil_flow_removed *fr)
+{
+    const struct oxs_field *field;
+    uint64_t header;
+    enum ofperr header_error;
+    unsigned int payload_len;
+    const uint8_t *payload;
+
+    header_error = oxs_pull_header__(b, &header, &field);
+    if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) {
+        return header_error;
+    }
+
+    payload_len = oxs_payload_len(header);
+    payload = ofpbuf_try_pull(b, payload_len);
+    if (!payload) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+    if (fr && field) {
+
+        switch (field->id) {
+        case OFPXST_OFB_DURATION: {
+            uint64_t duration;
+
+            memcpy(&duration, payload, sizeof (duration));
+            fr->duration_nsec = ntohl((duration & 0xffffffff));
+            fr->duration_sec = ntohl((uint32_t) (duration >> 32));
+        }
+        break;
+        case OFPXST_OFB_PACKET_COUNT: {
+            uint64_t packet_count;
+
+            memcpy(&packet_count, payload, sizeof (packet_count));
+            fr->packet_count = ntohll(packet_count);
+
+        }
+        break;
+        case OFPXST_OFB_BYTE_COUNT: {
+            uint64_t byte_count;
+
+            memcpy(&byte_count, payload, sizeof (byte_count));
+            fr->byte_count = ntohll(byte_count);
+        }
+        break;
+        case OFPXST_OFB_IDLE_TIME:
+        case OFPXST_OFB_FLOW_COUNT:
+        default:
+            break;
+
+        }
+    }
+    return 0;
+}
+
+/* Retrieve struct ofp_oxs_stat from 'b', followed by the flow entry
+ * statistics in OXS format.
+ *
+ * Returns error if message parsing fails, otherwise returns zero . */
+int
+oxs_flow_removed_stat_pull(struct ofpbuf *b,
+                           struct ofputil_flow_removed *ofr) {
+    struct ofp_oxs_stat *oxs = b->data;
+    uint8_t *p;
+    uint16_t stat_len;
+
+    stat_len = ntohs(oxs->length);
+    if (stat_len < sizeof *oxs) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    p = ofpbuf_try_pull(b, ROUND_UP(stat_len, 8));
+    if (!p) {
+        VLOG_DBG_RL(&rl, "oxs length %u, rounded up to a "
+                    "multiple of 8, is longer than space in message (max "
+                    "length %" PRIu32 ")", stat_len, b->size);
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    return oxs_flow_rem_stat_fields_pull(p + sizeof (*oxs),
+                                         stat_len - sizeof (*oxs), ofr);
+
+}
diff --git a/lib/ox-stat.h b/lib/ox-stat.h
new file mode 100644
index 0000000..7cd56e1
--- /dev/null
+++ b/lib/ox-stat.h
@@ -0,0 +1,43 @@ 
+/*
+ * 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 );
+int oxs_pull_stat(struct ofpbuf *,struct ofputil_flow_stats *,
+                  uint16_t *);
+int oxs_put_agg_stat(struct ofpbuf *, const struct ofputil_aggregate_stats *,
+                     enum ofp_version);
+int oxs_pull_agg_stat(struct ofpbuf , struct ofputil_aggregate_stats *);
+int oxs_flow_removed_stat_put(struct ofpbuf *b,
+                              const struct ofputil_flow_removed *fr,
+                              enum ofp_version version);
+int oxs_flow_removed_stat_pull(struct ofpbuf *b,
+                               struct ofputil_flow_removed *ofr);
+
+#endif /* ox_stat.h */
+
diff --git a/lib/rconn.c b/lib/rconn.c
index 8a29864..4cf6a37 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1392,8 +1392,12 @@  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_OXS_AGGREGATE_STATS_REQUEST:
+    case OFPTYPE_OXS_AGGREGATE_STATS_REPLY:
     case OFPTYPE_TABLE_STATS_REQUEST:
     case OFPTYPE_TABLE_STATS_REPLY:
     case OFPTYPE_PORT_STATS_REQUEST:
diff --git a/lib/vconn.c b/lib/vconn.c
index 6997eaa..688fb58 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -968,7 +968,8 @@  recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
             } while (((struct ofp_header *) reply->data)->xid != send_xid);
 
             error = ofptype_decode(&type, reply->data);
-            if (error || type != OFPTYPE_FLOW_STATS_REPLY) {
+            if ((error) || (type !=OFPTYPE_OXS_FLOW_STATS_REPLY &&
+                           type != OFPTYPE_FLOW_STATS_REPLY)) {
                 VLOG_WARN_RL(&rl, "received bad reply: %s",
                              ofp_to_string(reply->data, reply->size, NULL, 1));
                 return EPROTO;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index ad94db5..accdd4b 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -8099,9 +8099,15 @@  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);
 
+    case OFPTYPE_OXS_AGGREGATE_STATS_REQUEST:
+        return handle_aggregate_stats_request(ofconn, oh);
+
     case OFPTYPE_TABLE_STATS_REQUEST:
         return handle_table_stats_request(ofconn, oh);
 
@@ -8174,10 +8180,12 @@  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:
     case OFPTYPE_AGGREGATE_STATS_REPLY:
+    case OFPTYPE_OXS_AGGREGATE_STATS_REPLY:
     case OFPTYPE_PORT_DESC_STATS_REPLY:
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_FLOW_MONITOR_PAUSED:
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 14292ea..aa4ae76 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7005,7 +7005,6 @@  flow_mods_reset_counts () {
 # OpenFlow versions >= 1.3 should behave the same way
 flow_mods_reset_counts 13
 flow_mods_reset_counts 14
-flow_mods_reset_counts 15
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 76a33b6..1ab4d33 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1392,10 +1392,6 @@  AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip], [0], [dnl
 OFPST_FLOW reply (OF1.4):
  check_overlap reset_counts in_port=1 actions=drop
 ])
-AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip], [0], [dnl
-OFPST_FLOW reply (OF1.5):
- check_overlap reset_counts in_port=1 actions=drop
-])
 OVS_VSWITCHD_STOP
 AT_CLEANUP