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

SatyaValli satyavalli.rama at gmail.com
Wed Feb 28 14:35:56 UTC 2018


From: SatyaValli <satyavalli.rama at tcs.com>

This Patch provides implementation Existing flow entry statistics are
redefined as standard OXS(OpenFlow Extensible Statistics) fields for
displaying the arbitrary flow stats.The existing Flow Stats were renamed
as Flow Description.

To support this implementation below messages are newly added

OFPRAW_OFPT15_FLOW_REMOVED,
OFPRAW_OFPST15_FLOW_REQUEST,
OFPRAW_OFPST15_FLOW_DESC_REQUEST,
OFPRAW_OFPST15_AGGREGATE_REQUEST,
OFPRAW_OFPST15_FLOW_REPLY,
OFPRAW_OFPST15_FLOW_DESC_REPLY,
OFPRAW_OFPST15_AGGREGATE_REPLY,

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

"ovs-ofctl dump-flows" & "ovs-ofctl dump-aggregate" now accepts a new option
"--oxs-stats" provided with the arbitrary OXS fields for displaying the
desired flow stats.

Below are Commands to display OXS stats field wise

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

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

Signed-off-by: Satya Valli <satyavalli.rama at tcs.com>
Co-authored-by: Lavanya Harivelam <harivelam.lavanya at tcs.com>
Signed-off-by: Lavanya Harivelam <harivelam.lavanya at tcs.com>
Co-authored-by: Surya Muttamsetty <muttamsetty.surya at tcs.com>
Signed-off-by: Surya Muttamsetty <muttamsetty.surya at tcs.com>
Co-authored-by: Manasa Cherukupally <manasa.cherukupally at tcs.com>
Signed-off-by: Manasa Cherukupally <manasa.cherukupally at tcs.com>
Co-authored-by: Pavani Panthagada <p.pavani1 at tcs.com>
Signed-off-by: Pavani Panthagada <p.pavani1 at tcs.com>

---
 NEWS                            |    7 +
 include/openflow/openflow-1.5.h |   81 +++
 include/openvswitch/ofp-msgs.h  |   31 +-
 include/openvswitch/ofp-print.h |    9 +-
 lib/automake.mk                 |    2 +
 lib/ofp-bundle.c                |    2 +
 lib/ofp-flow.c                  |  198 +++++++-
 lib/ofp-monitor.c               |   46 +-
 lib/ofp-parse.c                 |    1 +
 lib/ofp-print.c                 |   52 ++
 lib/ox-stat.c                   | 1040 +++++++++++++++++++++++++++++++++++++++
 lib/ox-stat.h                   |   60 +++
 lib/rconn.c                     |    2 +
 lib/vconn.c                     |    7 +-
 ofproto/ofproto.c               |    3 +-
 tests/ofp-print.at              |   80 +++
 tests/ofproto-dpif.at           |   85 ++++
 tests/ofproto.at                |    3 +-
 utilities/ovs-ofctl.8.in        |   31 +-
 utilities/ovs-ofctl.c           |  123 ++++-
 20 files changed, 1815 insertions(+), 48 deletions(-)
 create mode 100644 lib/ox-stat.c
 create mode 100644 lib/ox-stat.h

diff --git a/NEWS b/NEWS
index 4230189..0704960 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,13 @@ Post-v2.9.0
        "table#".  These are not helpful names for the purpose of accepting
        and displaying table names, so now tables by default have no names.
    - ovs-ofctl:
+     * Existing flow entry statistics are redefined as standard OXS(OpenFlow
+       Extensible Statistics) fields for displaying the arbitrary flow stats.
+     * The existing flow statistics are renamed as Flow Description.
+     * Now "ovs-ofctl dump-flows" and "ovs-ofctl dump-aggregate" accepts a new
+       option --oxs-stats provided with the arbitrary OXS fields i.e
+       flow duration, flow count, packet count, byte count for displaying
+       the desired flow stats. See ovs-ofctl(8) for details.
      * ovs-ofctl now accepts and display table names in place of numbers.  By
        default it always accepts names and in interactive use it displays them;
        use --names or --no-names to override.  See ovs-ofctl(8) for details.
diff --git a/include/openflow/openflow-1.5.h b/include/openflow/openflow-1.5.h
index 73b76d8..d1870ce 100644
--- a/include/openflow/openflow-1.5.h
+++ b/include/openflow/openflow-1.5.h
@@ -163,4 +163,85 @@ struct ofp15_packet_out {
 };
 OFP_ASSERT(sizeof(struct ofp15_packet_out) == 8);
 
+struct ofp_oxs_stat {
+    ovs_be16 reserved;          /* Reserved for future use,
+                                 * currently zeroed. */
+    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_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_flow_stats_request) == 32);
+
+/* Body of reply to OFPMP_FLOW_DESC request. */
+struct ofp15_flow_desc {
+    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 pad;
+    ovs_be16 priority;        /* Priority of the entry. */
+    ovs_be16 idle_timeout;    /* Number of seconds
+                                 idle before expiration. */
+    ovs_be16 hard_timeout;    /* Number of seconds
+                                 before expiration. */
+    ovs_be16 flags;           /* Bitmap of OFPFF_*. flags. */
+    ovs_be16 importance;      /* Eviction precedence. */
+    ovs_be64 cookie;          /* Opaque controller issued identifier. */
+};
+
+OFP_ASSERT(sizeof(struct ofp15_flow_desc) == 24);
+
+/* Body of reply to OFPMP_FLOW_STATS request
+ * and body for OFPIT_STAT_TRIGGER generated status. */
+struct ofp15_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_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 = 3,   /* Number of aggregated flow entries. */
+    OFPXST_OFB_PACKET_COUNT = 4, /* Number of packets in flow entry.  */
+    OFPXST_OFB_BYTE_COUNT = 5,   /* Number of bytes in flow entry.  */
+};
+
+/* Flow removed (datapath -> controller). */
+struct ofp15_flow_removed {
+    uint8_t table_id;           /* ID of the table */
+    uint8_t reason;             /* One of OFPRR_*. */
+    ovs_be16 priority;          /* Priority level of flow entry. */
+    ovs_be16 idle_timeout;      /* Idle timeout from original flow mod. */
+    ovs_be16 hard_timeout;      /* Hard timeout from original flow mod. */
+    ovs_be64 cookie;            /* Opaque controller issued identifier. */
+};
+
+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 3f92f2a..b7e3a09 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -165,8 +165,10 @@ enum ofpraw {
 
     /* OFPT 1.0 (11): struct ofp10_flow_removed. */
     OFPRAW_OFPT10_FLOW_REMOVED,
-    /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */
+    /* OFPT 1.1-1.4 (11): struct ofp11_flow_removed, uint8_t[8][]. */
     OFPRAW_OFPT11_FLOW_REMOVED,
+    /* OFPT 1.5+ (11): 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,
 
@@ -293,8 +295,12 @@ enum ofpraw {
 
     /* OFPST 1.0 (1): struct ofp10_flow_stats_request. */
     OFPRAW_OFPST10_FLOW_REQUEST,
-    /* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
+    /* OFPST 1.1-1.4 (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
     OFPRAW_OFPST11_FLOW_REQUEST,
+    /* OFPST 1.5+ (1): struct ofp15_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST15_FLOW_REQUEST,
+    /* OFPST 1.5+ (17): struct ofp15_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST15_FLOW_DESC_REQUEST,
     /* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */
     OFPRAW_NXST_FLOW_REQUEST,
 
@@ -302,20 +308,28 @@ enum ofpraw {
     OFPRAW_OFPST10_FLOW_REPLY,
     /* OFPST 1.1-1.2 (1): uint8_t[]. */
     OFPRAW_OFPST11_FLOW_REPLY,
-    /* OFPST 1.3+ (1): uint8_t[]. */
+    /* OFPST 1.3-1.4 (1): uint8_t[]. */
     OFPRAW_OFPST13_FLOW_REPLY,
+    /* OFPST 1.5+ (1): uint8_t[]. */
+    OFPRAW_OFPST15_FLOW_REPLY,
+    /* OFPST 1.5+ (17): uint8_t[]. */
+    OFPRAW_OFPST15_FLOW_DESC_REPLY,
     /* NXST 1.0 (0): uint8_t[]. */
     OFPRAW_NXST_FLOW_REPLY,
 
     /* OFPST 1.0 (2): struct ofp10_flow_stats_request. */
     OFPRAW_OFPST10_AGGREGATE_REQUEST,
-    /* OFPST 1.1+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
+    /* OFPST 1.1-1.4 (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
     OFPRAW_OFPST11_AGGREGATE_REQUEST,
+    /* OFPST 1.5+ (2): struct ofp15_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST15_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. */
+    /* OFPST 1.0-1.4 (2): struct ofp_aggregate_stats_reply. */
     OFPRAW_OFPST_AGGREGATE_REPLY,
+    /* OFPST 1.5+ (2): uint8_t[] . */
+    OFPRAW_OFPST15_AGGREGATE_REPLY,
     /* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */
     OFPRAW_NXST_AGGREGATE_REPLY,
 
@@ -561,6 +575,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.
@@ -637,16 +652,22 @@ enum ofptype {
     OFPTYPE_DESC_STATS_REPLY,        /* OFPRAW_OFPST_DESC_REPLY. */
     OFPTYPE_FLOW_STATS_REQUEST,      /* OFPRAW_OFPST10_FLOW_REQUEST.
                                       * OFPRAW_OFPST11_FLOW_REQUEST.
+                                      * OFPRAW_OFPST15_FLOW_REQUEST.
                                       * OFPRAW_NXST_FLOW_REQUEST. */
     OFPTYPE_FLOW_STATS_REPLY,        /* OFPRAW_OFPST10_FLOW_REPLY.
                                       * OFPRAW_OFPST11_FLOW_REPLY.
                                       * OFPRAW_OFPST13_FLOW_REPLY.
+                                      * OFPRAW_OFPST15_FLOW_REPLY.
                                       * OFPRAW_NXST_FLOW_REPLY. */
     OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
                                       * OFPRAW_OFPST11_AGGREGATE_REQUEST.
+                                      * OFPRAW_OFPST15_AGGREGATE_REQUEST.
                                       * OFPRAW_NXST_AGGREGATE_REQUEST. */
     OFPTYPE_AGGREGATE_STATS_REPLY,   /* OFPRAW_OFPST_AGGREGATE_REPLY.
+                                      * OFPRAW_OFPST15_AGGREGATE_REPLY.
                                       * OFPRAW_NXST_AGGREGATE_REPLY. */
+    OFPTYPE_FLOW_DESC_REQUEST,   /* OFPRAW_OFPST15_FLOW_DESC_REQUEST. */
+    OFPTYPE_FLOW_DESC_REPLY,     /* OFPRAW_OFPST15_FLOW_DESC_REPLY. */
     OFPTYPE_TABLE_STATS_REQUEST,     /* OFPRAW_OFPST_TABLE_REQUEST. */
     OFPTYPE_TABLE_STATS_REPLY,       /* OFPRAW_OFPST10_TABLE_REPLY.
                                       * OFPRAW_OFPST11_TABLE_REPLY.
diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h
index ed11378..f7eea44 100644
--- a/include/openvswitch/ofp-print.h
+++ b/include/openvswitch/ofp-print.h
@@ -34,6 +34,7 @@ struct ofputil_table_features;
 struct ofputil_table_map;
 struct ofputil_table_stats;
 struct dp_packet;
+struct ofputil_aggregate_stats;
 
 #ifdef  __cplusplus
 extern "C" {
@@ -62,12 +63,16 @@ void ofp_print_table_features(
     const struct ofputil_table_stats *stats,
     const struct ofputil_table_stats *prev_stats,
     const struct ofputil_table_map *table_map);
-
 void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *,
                           const struct ofputil_port_map *,
                           const struct ofputil_table_map *,
                           bool show_stats);
-
+void ofp_print_flow_oxs_stats(struct ds *string,
+                         const struct ofputil_flow_stats *fs,
+                         bool stats, uint8_t oxs_field_set);
+void ofp_print_flow_oxs_agg_stats(struct ds *,
+                         const struct ofputil_aggregate_stats ,
+                         bool , uint8_t );
 #ifdef  __cplusplus
 }
 #endif
diff --git a/lib/automake.mk b/lib/automake.mk
index 5c26e0f..7ecf647 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -216,6 +216,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-bundle.c b/lib/ofp-bundle.c
index 0921c81..a54e2fd 100644
--- a/lib/ofp-bundle.c
+++ b/lib/ofp-bundle.c
@@ -183,6 +183,7 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_GET_ASYNC_REQUEST:
     case OFPTYPE_DESC_STATS_REQUEST:
     case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_FLOW_DESC_REQUEST:
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
     case OFPTYPE_TABLE_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
@@ -211,6 +212,7 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_DESC_STATS_REPLY:
     case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_FLOW_DESC_REPLY:
     case OFPTYPE_QUEUE_STATS_REPLY:
     case OFPTYPE_PORT_STATS_REPLY:
     case OFPTYPE_TABLE_STATS_REPLY:
diff --git a/lib/ofp-flow.c b/lib/ofp-flow.c
index c29f5b7..3d2d979 100644
--- a/lib/ofp-flow.c
+++ b/lib/ofp-flow.c
@@ -30,6 +30,7 @@
 #include "openvswitch/ofpbuf.h"
 #include "openvswitch/vlog.h"
 #include "util.h"
+#include "ox-stat.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_flow);
 
@@ -552,6 +553,37 @@ 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_flow_stats_request *ofsr;
+    enum ofperr error;
+
+    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);
+    if (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,
@@ -605,10 +637,18 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
         return ofputil_decode_ofpst11_flow_request(fsr, &b, false, tun_table,
                                                    vl_mff_map);
 
+    case OFPRAW_OFPST15_FLOW_DESC_REQUEST:
+        return ofputil_decode_ofpst15_flow_request(fsr, &b, false,
+                                                   tun_table, vl_mff_map);
+
     case OFPRAW_OFPST11_AGGREGATE_REQUEST:
         return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
                                                    vl_mff_map);
 
+    case OFPRAW_OFPST15_AGGREGATE_REQUEST:
+       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);
@@ -637,9 +677,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     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
@@ -657,6 +695,25 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
         break;
     }
 
+    case OFPUTIL_P_OF15_OXM:
+    case OFPUTIL_P_OF16_OXM: {
+        struct ofp15_flow_stats_request *ofsr;
+
+        raw = (fsr->aggregate
+               ? OFPRAW_OFPST15_AGGREGATE_REQUEST
+               : OFPRAW_OFPST15_FLOW_DESC_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);
+        break;
+    }
+
     case OFPUTIL_P_OF10_STD:
     case OFPUTIL_P_OF10_STD_TID: {
         struct ofp10_flow_stats_request *ofsr;
@@ -773,6 +830,58 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
 
     if (!msg->size) {
         return EOF;
+    } else if (raw == OFPRAW_OFPST15_FLOW_DESC_REPLY) {
+       const struct ofp15_flow_desc *ofd;
+       size_t length;
+       uint16_t padded_match_len;
+       uint16_t stat_len;
+       uint8_t oxs_field_set;
+
+       ofd = ofpbuf_try_pull(msg, sizeof *ofd);
+      if (!ofd) {
+           VLOG_WARN_RL(&rl, "OFPST_FLOW_DESC reply has %" PRIu32
+                        " leftover " "bytes at end", msg->size);
+           return EINVAL;
+       }
+
+       length = ntohs(ofd->length);
+       if (length < sizeof *ofd) {
+           VLOG_WARN_RL(&rl, "OFPST_FLOW_DESC reply claims invalid "
+                        "length %" PRIuSIZE, length);
+           return EINVAL;
+       }
+
+       if (ofputil_pull_ofp11_match
+          (msg, NULL, NULL, &fs->match, &padded_match_len)) {
+           VLOG_WARN_RL(&rl, "OFPST_FLOW_DESC reply bad match");
+           return EINVAL;
+       }
+
+       fs->priority = ntohs(ofd->priority);
+       fs->table_id = ofd->table_id;
+       fs->duration_sec = 0;
+       fs->duration_nsec = 0;
+       fs->idle_age = 0;
+       fs->hard_age = -1;
+       fs->packet_count = 0;
+       fs->byte_count = 0;
+       fs->cookie = ofd->cookie;
+       fs->idle_timeout = ntohs(ofd->idle_timeout);
+       fs->hard_timeout = ntohs(ofd->hard_timeout);
+       fs->importance = ntohs(ofd->importance);
+
+      error = ofputil_decode_flow_mod_flags(ofd->flags, -1, oh->version,
+                                                &fs->flags);
+           if (error) {
+               return error;
+           }
+
+       if (oxs_pull_stat(msg, fs, &stat_len, &oxs_field_set)) {
+           VLOG_WARN_RL(&rl, "OXS OFPST_FLOW_DESC reply bad match");
+           return EINVAL;
+       }
+       instructions_len = length - sizeof *ofd - padded_match_len - stat_len;
+
     } else if (raw == OFPRAW_OFPST11_FLOW_REPLY
                || raw == OFPRAW_OFPST13_FLOW_REPLY) {
         const struct ofp11_flow_stats *ofs;
@@ -951,7 +1060,31 @@ 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_FLOW_DESC_REPLY) {
+        struct ofp15_flow_desc *ofd;
+        /* oxs_field_set is used to encode/decode OXS fields in TLV format*/
+        uint8_t oxs_field_set;
+
+        oxs_bitmap_set_all(&oxs_field_set);
+        ofpbuf_put_uninit(reply, sizeof *ofd);
+        oxm_put_match(reply, &fs->match, version);
+        oxs_put_stat(reply, fs, version, oxs_field_set);
+        ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
+                                      version);
+
+        ofd = ofpbuf_at_assert(reply, start_ofs, sizeof *ofd);
+        ofd->length = htons(reply->size - start_ofs);
+        ofd->table_id = fs->table_id;
+        ofd->priority = htons(fs->priority);
+        ofd->idle_timeout = htons(fs->idle_timeout);
+        ofd->hard_timeout = htons(fs->hard_timeout);
+        ofd->cookie = fs->cookie;
+        memset(ofd->pad2, 0, sizeof ofd->pad2);
+        ofd->pad = 0;
+        ofd->importance = htons(fs->importance);
+        ofd->flags = ofputil_encode_flow_mod_flags(fs->flags, version);
+    } else if (raw == OFPRAW_OFPST11_FLOW_REPLY ||
+               raw == OFPRAW_OFPST13_FLOW_REPLY) {
         struct ofp11_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
@@ -1053,20 +1186,29 @@ ofputil_encode_aggregate_stats_reply(
     enum ofpraw raw;
 
     ofpraw_decode(&raw, request);
-    if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
-        packet_count = unknown_to_zero(stats->packet_count);
-        byte_count = unknown_to_zero(stats->byte_count);
+    if (raw == OFPRAW_OFPST15_AGGREGATE_REQUEST) {
+        enum ofp_version version = request->version;
+        /* oxs_field_set is used to encode/decode OXS fields in TLV format*/
+        uint8_t oxs_set_field;
+
+        oxs_bitmap_set_all(&oxs_set_field);
+        msg = ofpraw_alloc_stats_reply(request, 0);
+        oxs_put_agg_stat(msg, stats, version, oxs_set_field);
     } else {
-        packet_count = stats->packet_count;
-        byte_count = stats->byte_count;
-    }
-
-    msg = ofpraw_alloc_stats_reply(request, 0);
-    asr = ofpbuf_put_zeros(msg, sizeof *asr);
-    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);
+        if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
+            packet_count = unknown_to_zero(stats->packet_count);
+            byte_count = unknown_to_zero(stats->byte_count);
+        } else {
+            packet_count = stats->packet_count;
+            byte_count = stats->byte_count;
+        }
 
+        msg = ofpraw_alloc_stats_reply(request, 0);
+        asr = ofpbuf_put_zeros(msg, sizeof *asr);
+        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;
 }
 
@@ -1075,12 +1217,22 @@ 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;
 
-    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);
+    raw = ofpraw_pull_assert(&msg);
+    if (raw == OFPRAW_OFPST15_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;
 }
@@ -1436,8 +1588,8 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     char *string = xstrdup(str_);
     char *error;
 
-    error = parse_ofp_str__(fm, command, string, port_map, table_map,
-                            usable_protocols);
+    error = parse_ofp_str__(fm, command, string, port_map,
+                            table_map, usable_protocols);
     if (error) {
         fm->ofpacts = NULL;
         fm->ofpacts_len = 0;
diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
index 49d623c..c65c629 100644
--- a/lib/ofp-monitor.c
+++ b/lib/ofp-monitor.c
@@ -28,6 +28,7 @@
 #include "openvswitch/ofp-parse.h"
 #include "openvswitch/ofp-table.h"
 #include "openvswitch/vlog.h"
+#include "ox-stat.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_monitor);
 
@@ -42,7 +43,26 @@ 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;
+        fr->idle_timeout = ntohs(ofr->idle_timeout);
+        fr->hard_timeout = ntohs(ofr->hard_timeout);
+    } else if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+
         const struct ofp12_flow_removed *ofr;
         enum ofperr error;
 
@@ -138,9 +158,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     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,
@@ -161,7 +179,27 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         ofputil_put_ofp11_match(msg, &fr->match, protocol);
         break;
     }
+    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;
+        ofr->idle_timeout = htons(fr->idle_timeout);
+        ofr->hard_timeout = htons(fr->hard_timeout);
+        ofputil_put_ofp11_match(msg, &fr->match, protocol);
+        /*Stats encoding in OXS TLV Format*/
+        oxs_flow_removed_stat_put(msg, fr,
+                                  ofputil_protocol_to_ofp_version(protocol));
+        break;
+    }
     case OFPUTIL_P_OF10_STD:
     case OFPUTIL_P_OF10_STD_TID: {
         struct ofp10_flow_removed *ofr;
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 4feb82e..f98cbdc 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -27,6 +27,7 @@
 #include "packets.h"
 #include "socket-util.h"
 #include "util.h"
+#include "ox-stat.h"
 
 /* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
  *
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 2f96b18..9c70ad0 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -63,6 +63,7 @@
 #include "unaligned.h"
 #include "util.h"
 #include "uuid.h"
+#include "ox-stat.h"
 
 static void ofp_print_queue_name(struct ds *string, uint32_t port);
 static void ofp_print_error(struct ds *, enum ofperr);
@@ -1805,6 +1806,55 @@ ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs,
     ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
 }
 
+void
+ofp_print_flow_oxs_stats(struct ds *string,
+                         const struct ofputil_flow_stats *fs,
+                         bool oxs, uint8_t oxs_field_set)
+{
+    if (!oxs && oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_DURATION)) {
+        ds_put_format(string, "%sduration=%s", colors.param, colors.end);
+        ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+        ds_put_cstr(string, "  ");
+    }
+    if (!oxs) {
+        if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+            ds_put_format(string, "%sn_packets=%s%"PRIu64"  ",
+                      colors.param, colors.end, fs->packet_count);
+        }
+        if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+            ds_put_format(string, "%sn_bytes=%s%"PRIu64"  ",
+                      colors.param, colors.end, fs->byte_count);
+        }
+    }
+    if (!oxs && fs->idle_age >= 0 &&
+        oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_IDLE_TIME)) {
+        ds_put_format(string, "%sidle_age=%s%d  ",
+                      colors.param, colors.end, fs->idle_age);
+    }
+
+}
+
+void
+ofp_print_flow_oxs_agg_stats(struct ds *string,
+                             const struct ofputil_aggregate_stats fs,
+                             bool stats, uint8_t oxs_field_set)
+{
+    if (!stats) {
+        if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+            ds_put_format(string, "%sn_packets=%s%"PRIu64"  ",
+                      colors.param, colors.end, fs.packet_count);
+        }
+        if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+            ds_put_format(string, "%sn_bytes=%s%"PRIu64"  ",
+                      colors.param, colors.end, fs.byte_count);
+        }
+        if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_FLOW_COUNT)) {
+            ds_put_format(string, "%sflow_count=%s%"PRIu32"  ",
+                      colors.param, colors.end, fs.flow_count);
+        }
+    }
+}
+
 static enum ofperr
 ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh,
                            const struct ofputil_port_map *port_map,
@@ -3684,6 +3734,7 @@ ofp_to_string__(const struct ofp_header *oh,
 
     case OFPTYPE_FLOW_STATS_REQUEST:
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
+    case OFPTYPE_FLOW_DESC_REQUEST:
         ofp_print_stats(string, oh);
         return ofp_print_flow_stats_request(string, oh, port_map, table_map);
 
@@ -3704,6 +3755,7 @@ ofp_to_string__(const struct ofp_header *oh,
         return ofp_print_ofpst_desc_reply(string, oh);
 
     case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_FLOW_DESC_REPLY:
         ofp_print_stats(string, oh);
         return ofp_print_flow_stats_reply(string, oh, port_map, table_map);
 
diff --git a/lib/ox-stat.c b/lib/ox-stat.c
new file mode 100644
index 0000000..29917b7
--- /dev/null
+++ b/lib/ox-stat.c
@@ -0,0 +1,1040 @@
+/*
+ * 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}},
+};
+
+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 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,
+                                uint8_t *oxs_field_set);
+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,
+                          uint8_t oxs_field_set);
+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);
+
+bool
+parse_oxs_key(char **stringp, char **keyp)
+{
+    /* Skip white space and delimiters. If that brings us to the end of the
+     * input string, we are done. */
+    *stringp += strspn(*stringp, ", \t\r\n");
+    if (*(*stringp) == '\0') {
+        *keyp = NULL;
+        return false;
+    }
+
+    /* Extract the key and the delimiter that ends the key.
+     * Advance the input position past the key and delimiter. */
+    char *key = *stringp;
+    size_t key_len = strcspn(key, ":=(, \t\r\n");
+    char key_delim = key[key_len];
+    key[key_len] = '\0';
+    *stringp += key_len + (key_delim != '\0');
+
+        *keyp = key;
+        return true;
+}
+
+bool
+parse_oxs_field(const char *name, const struct ox_field **f_out)
+{
+    static const struct ox_field fields[] = {
+        {"duration", OFPXST_OFB_DURATION},
+        {"idle_time", OFPXST_OFB_IDLE_TIME},
+        {"flow_count", OFPXST_OFB_FLOW_COUNT},
+        {"packet_count", OFPXST_OFB_PACKET_COUNT},
+        {"byte_count", OFPXST_OFB_BYTE_COUNT},
+    };
+
+    const struct ox_field *oxfs;
+
+    for (oxfs = fields; oxfs < &fields[ARRAY_SIZE(fields)]; oxfs++) {
+        if (!strcmp(oxfs->name, name)) {
+            *f_out = oxfs;
+            return true;
+        }
+    }
+    *f_out = NULL;
+    return false;
+}
+
+void
+oxs_bitmap_set_all(uint8_t *oxs_field_set)
+{
+    enum  oxs_ofb_stat_fields i;
+
+    for (i = OFPXST_OFB_DURATION; i <= OFPXST_OFB_BYTE_COUNT; i++) {
+        *oxs_field_set |= 1 << i;
+    }
+}
+
+void
+oxs_bitmap_set(uint8_t *oxs_field_set, enum  oxs_ofb_stat_fields i)
+{
+    *oxs_field_set |= 1 << i;
+
+}
+
+bool
+oxs_bitmap_is_set(uint8_t oxs_field_set, enum  oxs_ofb_stat_fields i)
+{
+    if (oxs_field_set & 1 << i) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+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: {
+            ovs_be64 duration = 0;
+
+            memcpy(&duration, payload, sizeof (duration));
+            uint64_t duration_ = ntohll(duration);
+            fs->duration_sec = ((uint32_t) ((duration_ &
+                                            0xFFFFFFFF00000000LL) >> 32));
+            fs->duration_nsec = ((uint32_t) (duration_ & 0xFFFFFFFFLL));
+        }
+        break;
+        case OFPXST_OFB_IDLE_TIME: {
+            ovs_be64 idle_time = 0;
+
+            memcpy(&idle_time, payload, sizeof (idle_time));
+            uint64_t idle_time_ = ntohll(idle_time);
+            fs->idle_age = ((idle_time_ & 0xFFFFFFFF00000000LL) >> 32);
+        }
+        break;
+        case OFPXST_OFB_PACKET_COUNT: {
+            ovs_be64 packet_count;
+
+            memcpy(&packet_count, payload, sizeof (packet_count));
+            fs->packet_count = ntohll(packet_count);
+        }
+        break;
+        case OFPXST_OFB_BYTE_COUNT: {
+            ovs_be64 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, uint8_t *oxs_field_set)
+{
+    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 {
+           oxs_bitmap_set(oxs_field_set,field->id);
+        }
+        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, uint8_t *oxs_field_set)
+{
+    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, oxs_field_set);
+}
+
+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: {
+                ovs_be32 flow_count;
+
+                memcpy(&flow_count, payload, sizeof (flow_count));
+                fs->flow_count = ntohl(flow_count);
+            }
+            break;
+            case OFPXST_OFB_PACKET_COUNT: {
+                ovs_be64 packet_count;
+
+                memcpy(&packet_count, payload, sizeof (packet_count));
+                fs->packet_count = ntohll(packet_count);
+            }
+            break;
+            case OFPXST_OFB_BYTE_COUNT: {
+                ovs_be64 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, uint8_t oxs_field_set)
+{
+    const size_t start_len = b->size;
+    int stat_len;
+
+    if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_DURATION)) {
+        ovs_be64 duration = 0;
+
+        if (fs) {
+            uint64_t 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_bitmap_is_set(oxs_field_set, OFPXST_OFB_IDLE_TIME)) {
+        ovs_be64 idl_time = 0;
+
+        if (fs) {
+            uint64_t idl_time_n = (uint64_t) fs->idle_age << 32;
+            idl_time = htonll(idl_time_n);
+        }
+        oxs_put__(b, OFPXST_OFB_IDLE_TIME, oxs, &idl_time, NULL,
+                  OXS_STATS_IDLE_TIME_LEN);
+    }
+    if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_FLOW_COUNT)) {
+        ovs_be32 flow_count = 0;
+
+        oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count, NULL,
+                  OXS_STATS_FLOW_COUNT_LEN);
+    }
+    if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+        ovs_be64 pkt_count = 0;
+
+        if (fs) {
+            pkt_count = htonll(fs->packet_count);
+        }
+        oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count, NULL,
+                  OXS_STATS_PACKET_COUNT_LEN);
+    }
+    if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+        ovs_be64 byte_count = 0;
+
+        if (fs) {
+            byte_count = htonll(fs->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, uint8_t oxs_field_set)
+{
+    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, oxs_field_set)
+                + 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, uint8_t oxs_field_set)
+{
+   const size_t start_len = b->size;
+   int stat_len;
+
+   if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_FLOW_COUNT)) {
+       ovs_be32 flow_count = 0;
+
+       if (fs) {
+           flow_count = htonl(fs->flow_count);
+       }
+       oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count,
+                NULL, OXS_STATS_FLOW_COUNT_LEN);
+   }
+
+   if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+       ovs_be64 pkt_count = 0;
+
+       if (fs) {
+           pkt_count = htonll(fs->packet_count);
+       }
+       oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count,
+               NULL, OXS_STATS_PACKET_COUNT_LEN);
+   }
+
+   if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+       ovs_be64 byte_count = 0;
+
+       if (fs) {
+           byte_count = htonll(fs->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, uint8_t oxs_set_field)
+{
+   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, oxs_set_field)
+                + 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)
+{
+    ovs_be64 duration = 0;
+
+    if (fr) {
+        uint64_t 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)
+{
+    ovs_be64 pkt_count = 0;
+
+    if (fr) {
+        pkt_count = htonll(fr->packet_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)
+{
+    ovs_be64 byte_count = 0;
+
+    if (fr) {
+        byte_count = htonll(fr->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: {
+            ovs_be64 duration;
+
+            memcpy(&duration, payload, sizeof (duration));
+            uint64_t duration_ = ntohll(duration);
+            fr->duration_sec = ((uint32_t) ((duration_ &
+                                            0xFFFFFFFF00000000LL) >> 32));
+            fr->duration_nsec = ((uint32_t) (duration_ & 0xFFFFFFFFLL));
+        }
+        break;
+        case OFPXST_OFB_PACKET_COUNT: {
+            ovs_be64 packet_count;
+
+            memcpy(&packet_count, payload, sizeof (packet_count));
+            fr->packet_count = ntohll(packet_count);
+
+        }
+        break;
+        case OFPXST_OFB_BYTE_COUNT: {
+            ovs_be64 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..7b14b29
--- /dev/null
+++ b/lib/ox-stat.h
@@ -0,0 +1,60 @@
+/*
+ * 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"
+#include "openvswitch/ofp-flow.h"
+#include "openvswitch/ofp-monitor.h"
+
+struct ox_field {
+    const char *name;
+    uint16_t fl_type;
+};
+
+int oxs_put_stat(struct ofpbuf *, const struct ofputil_flow_stats *,
+                 enum ofp_version, uint8_t );
+int oxs_pull_stat(struct ofpbuf *,struct ofputil_flow_stats *,
+                  uint16_t *,uint8_t *);
+int oxs_put_agg_stat(struct ofpbuf *, const struct ofputil_aggregate_stats *,
+                     enum ofp_version, uint8_t );
+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);
+void
+oxs_bitmap_set(uint8_t *oxs_field_set, enum  oxs_ofb_stat_fields i);
+void
+oxs_bitmap_set_all(uint8_t *oxs_field_set);
+bool
+oxs_bitmap_is_set(uint8_t oxs_field_set, enum  oxs_ofb_stat_fields i);
+void oxs_put__(struct ofpbuf *, enum oxs_ofb_stat_fields,
+               enum ofp_version, const void *, const void *, size_t);
+bool parse_oxs_key(char **stringp, char **keyp);
+bool parse_oxs_field(const char *name, const struct ox_field **f_out);
+
+
+#endif /* ox_stat.h */
diff --git a/lib/rconn.c b/lib/rconn.c
index 2eebbff..ee139a8 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1401,6 +1401,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_FLOW_DESC_REQUEST:
+    case OFPTYPE_FLOW_DESC_REPLY:
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
     case OFPTYPE_AGGREGATE_STATS_REPLY:
     case OFPTYPE_TABLE_STATS_REQUEST:
diff --git a/lib/vconn.c b/lib/vconn.c
index aeb2cd7..5ec6396 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -940,7 +940,6 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
     struct ofpbuf *reply = *replyp;
-
     for (;;) {
         int retval;
         bool more;
@@ -958,14 +957,14 @@ 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_FLOW_DESC_REPLY &&
+                          type != OFPTYPE_FLOW_STATS_REPLY)) {
                 VLOG_WARN_RL(&rl, "received bad reply: %s",
                              ofp_to_string(reply->data, reply->size,
                                            NULL, NULL, 1));
                 return EPROTO;
             }
         }
-
         /* Pull an individual flow stats reply out of the message. */
         retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts);
         switch (retval) {
@@ -1004,7 +1003,6 @@ vconn_dump_flows(struct vconn *vconn,
     struct ofputil_flow_stats *fses = NULL;
     size_t n_fses = 0;
     size_t allocated_fses = 0;
-
     struct ofpbuf *request = ofputil_encode_flow_stats_request(fsr, protocol);
     const struct ofp_header *oh = request->data;
     ovs_be32 send_xid = oh->xid;
@@ -1023,6 +1021,7 @@ vconn_dump_flows(struct vconn *vconn,
 
         struct ofputil_flow_stats *fs = &fses[n_fses];
         error = recv_flow_stats_reply(vconn, send_xid, &reply, fs, &ofpacts);
+
         if (error) {
             if (error == EOF) {
                 error = 0;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index a982de9..9a25d3f 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -4485,7 +4485,6 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     if (error) {
         return error;
     }
-
     memset(&stats, 0, sizeof stats);
     unknown_packets = unknown_bytes = false;
 
@@ -8116,6 +8115,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
         return handle_desc_stats_request(ofconn, oh);
 
     case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_FLOW_DESC_REQUEST:
         return handle_flow_stats_request(ofconn, oh);
 
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
@@ -8193,6 +8193,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_FLOW_DESC_REPLY:
     case OFPTYPE_QUEUE_STATS_REPLY:
     case OFPTYPE_PORT_STATS_REPLY:
     case OFPTYPE_TABLE_STATS_REPLY:
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index ab7ba26..7e6693d 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -638,6 +638,22 @@ OFPT_FLOW_REMOVED (OF1.3) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xf
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.5])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 0b 00 80 00 00 00 02 01 00 00 00 11 00 22 00 \
+00 00 00 00 00 00 00 01 00 01 00 2d 80 00 00 04 \
+00 00 00 02 80 00 06 06 52 54 00 c3 00 89 80 00 \
+0a 02 08 00 80 00 10 01 00 80 00 04 08 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 34 80 02 00 08 \
+00 00 00 98 29 e6 ed c0 80 02 02 08 00 00 00 98 \
+00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 02 \
+80 02 0a 08 00 00 00 00 00 00 00 80 00 00 00 00 \
+"], [0], [dnl
+OFPT_FLOW_REMOVED (OF1.5) (xid=0x2): priority=0,ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 reason=idle table_id=1 cookie:0x1 duration152.703s idle4352 hard8704 pkts2 bytes128
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PORT_STATUS - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1392,6 +1408,18 @@ OFPST_FLOW request (OF1.3) (xid=0x2):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_FLOW request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 12 00 38 00 00 00 04 00 11 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW_DESC request (OF1.5) (xid=0x4):
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_FLOW reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1471,6 +1499,32 @@ OFPST_FLOW reply (OF1.2) (xid=0x2):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_FLOW reply - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 13 01 00 00 00 00 04 00 11 00 00 00 00 00 00 \
+00 78 00 00 00 00 80 00 00 00 00 00 00 05 00 00 \
+00 00 00 00 00 00 00 00 00 01 00 0c 80 00 00 04 \
+00 00 00 02 00 00 00 00 00 00 00 34 80 02 00 08 \
+00 00 00 c4 0b 06 e0 40 80 02 02 08 00 00 00 c4 \
+00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 02 \
+80 02 0a 08 00 00 00 00 00 00 00 80 00 00 00 00 \
+00 04 00 18 00 00 00 00 00 00 00 10 ff ff ff fa \
+00 00 00 00 00 00 00 00 00 78 00 00 00 00 0f a0 \
+00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 0c 80 00 00 04 00 00 00 03 00 00 00 00 \
+00 00 00 34 80 02 00 08 00 00 00 b3 25 40 be 40 \
+80 02 02 08 00 00 00 b3 00 00 00 00 80 02 08 08 \
+00 00 00 00 00 00 00 02 80 02 0a 08 00 00 00 00 \
+00 00 00 80 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 ff ff ff fa 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW_DESC reply (OF1.5) (xid=0x4):
+ cookie=0x0, duration=196.185s, table=0, n_packets=2, n_bytes=128, send_flow_rem reset_counts idle_age=196, in_port=2 actions=NORMAL
+ cookie=0x0, duration=179.625s, table=0, n_packets=2, n_bytes=128, send_flow_rem reset_counts idle_age=179, priority=4000,in_port=3 actions=NORMAL
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_AGGREGATE request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1507,6 +1561,20 @@ OFPST_AGGREGATE request (OF1.3) (xid=0x2):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_AGGREGATE request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 12 00 60 00 00 00 04 00 02 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 00 00 00 24 80 02 06 04 \
+00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 00 \
+80 02 0a 08 00 00 00 00 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE request (OF1.5) (xid=0x4):
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_AGGREGATE reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1540,6 +1608,18 @@ OFPST_AGGREGATE reply (OF1.3) (xid=0x2): packet_count=121 byte_count=19279 flow_
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_AGGREGATE reply - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 13 00 38 00 00 00 04 00 02 00 00 00 00 00 00 \
+00 00 00 24 80 02 06 04 00 00 00 03 80 02 08 08 \
+00 00 00 00 00 00 00 79 80 02 0a 08 00 00 00 00 \
+00 00 4b 4f 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE reply (OF1.5) (xid=0x4): packet_count=121 byte_count=19279 flow_count=3
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_TABLE request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100030000"], [0], [dnl
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 600afdd..3c99e87 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7265,11 +7265,96 @@ flow_mods_reset_counts () {
 # OpenFlow versions >= 1.3 should behave the same way
 flow_mods_reset_counts 13
 flow_mods_reset_counts 14
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - flow stats reset_counts OpenFlow1.5])
+OVS_VSWITCHD_START
+flow="ip,actions=NORMAL"
+
+ovs-appctl time/stop
+
+AT_CHECK([ovs-ofctl add-flow br0 $flow])
+
+warp_and_dump_OF () {
+    AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore])
+    AT_CHECK([ovs-appctl revalidator/purge], [0])
+
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 dump-flows br0 ], [0], [stdout])
+    if [[ "$6X" = "X" ]]; then
+        expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, idle_age=$5, ip actions=NORMAL"
+    else
+        expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, $6 idle_age=$5, ip actions=NORMAL"
+    fi
+     AT_CHECK_UNQUOTED([strip_xids < stdout | sed -n 's/duration=\([[0-9]]*\)\.*[[0-9]]*s/duration=\1s/p' | sort], [0], [dnl
+$expected
+])
+}
+
+send_packet () {
+    ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)'
+}
+
+# OpenFlow 1.5, explicit reset_counts
+flow_mods_reset_counts () {
+    # Reset to a known state
+    AT_CHECK([ovs-ofctl add-flow br0 $flow])
+
+    send_packet
+    warp_and_dump_OF $1 1 1 118 1 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow])
+    # add-flow without flags resets duration, but not counts,
+    # idle age is inherited from the old flow
+    warp_and_dump_OF $1 1 1 118 2
+
+    send_packet
+    warp_and_dump_OF $1 2 2 236 1
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow])
+    # mod-flows without flags does not reset duration nor counts,
+    # idle age is inherited from the old flow
+    warp_and_dump_OF $1 3 2 236 2
+
+    send_packet
+    warp_and_dump_OF $1 4 3 354 1
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 reset_counts,$flow])
+    # add-flow with reset_counts resets both duration and counts,
+    # idle age is inherited from the old flow
+    warp_and_dump_OF $1 1 0 0 2 reset_counts
+
+    send_packet
+    warp_and_dump_OF $1 2 1 118 1 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow])
+    # mod-flows with reset_counts resets counts, but not duration,
+    # idle age is inherited from the old flow
+    warp_and_dump_OF $1 3 0 0 2 reset_counts
+
+    # Modify flow having reset_counts flag without reset_counts
+    send_packet
+    warp_and_dump_OF $1 4 1 118 1 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow])
+    warp_and_dump_OF $1 5 1 118 2 reset_counts
+
+    # Add flow having reset_counts flag without reset_counts
+    send_packet
+    warp_and_dump_OF $1 6 2 236 1 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow])
+    warp_and_dump_OF $1 1 2 236 2
+
+    # Modify flow w/o reset_counts flag with a flow_mod having reset_counts
+    send_packet
+    warp_and_dump_OF $1 2 3 354 1
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow])
+    warp_and_dump_OF $1 3 0 0 2
+}
+
+# OpenFlow versions >= 1.3 should behave the same way
 flow_mods_reset_counts 15
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+
 AT_SETUP([ofproto-dpif - flow stats, set-n-threads])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index c1beea7..6dc57f2 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1402,8 +1402,9 @@ 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
 ])
+# OF1.5 makes the flags invisible.
 AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip], [0], [dnl
-OFPST_FLOW reply (OF1.5):
+OFPST_FLOW_DESC reply (OF1.5):
  check_overlap reset_counts in_port=1 actions=drop
 ])
 OVS_VSWITCHD_STOP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 7649bc5..8b99d21 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -227,8 +227,8 @@ By default, \fBovs\-ofctl\fR prints flow entries in the same order
 that the switch sends them, which is unlikely to be intuitive or
 consistent.  Use \fB\-\-sort\fR and \fB\-\-rsort\fR to control display
 order.  The \fB\-\-names\fR/\fB\-\-no\-names\fR and
-\fB\-\-stats\fR/\fB\-\-no\-stats\fR options also affect output
-formatting.  See the descriptions of these options, under
+\fB\-\-stats\fR/\fB\-\-no\-stats\fR \fB\-\-oxs\-stats\fR options also affect
+output formatting.  See the descriptions of these options, under
 \fBOPTIONS\fR below, for more information
 .
 .TP
@@ -2358,6 +2358,33 @@ Bundles require OpenFlow 1.4 or higher.  An explicit \fB-O
 OpenFlow14\fR option is not needed, but you may need to enable
 OpenFlow 1.4 support for OVS by setting the OVSDB \fIprotocols\fR
 column in the \fIbridge\fR table.
+.
+.RE
+.RE
+.IP "\fB\-\-oxs-stats\fR"
+From OpenFlow1.5, the flow stat fields are described using the OpenFlow
+Extensible Stat (OXS) format, which is a compact type-length-value (TLV)
+format. Some \fBovs\-ofctl\fR commands accept an argument that describes  oxs-
+field or oxs-fields. Such oxs-fields comprise a series of fields,
+separated by commas or white space.  (Embedding  spaces  into  a flow
+description normally  requires quoting to prevent the shell from breaking
+the description into multiple arguments.)
+.PP
+The oxs-fields comprises of 5 fields and oxs-field value is only
+displayed in the flow table output only when the corresponding oxs-field
+is passed as an argument.
+.RS
+.IP \fBduration\fR
+Displays the entire duration of the flow in the output.
+.IP \fBidle_time\fR
+Displays the time that flow entry has been idle in the output.
+.IP \fBpacket_count\fR
+Displays the total packet-count of the flow in the output.
+.IP \fBbyte_count\fR
+Displays the total packet-count of the flow in the output.
+.IP \fBflow_count\fR
+Displays the total flow count in the output.
+.RE
 .RE
 .
 .so lib/ofp-version.man
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 386e4f0..ec1099c 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -75,6 +75,7 @@
 #include "timeval.h"
 #include "unixctl.h"
 #include "util.h"
+#include "ox-stat.h"
 
 VLOG_DEFINE_THIS_MODULE(ofctl);
 
@@ -150,6 +151,10 @@ static bool should_show_names(void);
 /* --stats, --no-stats: Show statistics in flow dumps? */
 static int show_stats = 1;
 
+/* --oxs-stats: Show only requested OXS-fields in the flow dump */
+static bool oxs = 1;
+static uint8_t oxs_field_set = 0;
+
 /* --pcap: Makes "compose-packet" print a pcap on stdout. */
 static int print_pcap = 0;
 
@@ -182,6 +187,25 @@ main(int argc, char *argv[])
 }
 
 static void
+set_oxs_bitmap(char *field)
+{
+
+    char *keyp;
+    char *fieldp;
+    fieldp = field;
+
+    while (parse_oxs_key(&fieldp, &keyp)) {
+        const struct ox_field *oxfs;
+
+        if (parse_oxs_field(keyp, &oxfs)) {
+             oxs_bitmap_set(&oxs_field_set,oxfs->fl_type);
+        } else {
+           ovs_fatal(0, "%s:unknown oxs field", keyp);
+        }
+    }
+}
+
+static void
 add_sort_criterion(enum sort_order order, const char *field)
 {
     struct sort_criterion *sc;
@@ -215,6 +239,7 @@ parse_options(int argc, char *argv[])
         OPT_BUNDLE,
         OPT_COLOR,
         OPT_MAY_CREATE,
+        OPT_OXS_STATS,
         OPT_READ_ONLY,
         DAEMON_OPTION_ENUMS,
         OFP_VERSION_OPTION_ENUMS,
@@ -235,6 +260,7 @@ parse_options(int argc, char *argv[])
         {"no-names", no_argument, &use_names, 0},
         {"stats", no_argument, &show_stats, 1},
         {"no-stats", no_argument, &show_stats, 0},
+        {"oxs-stats", required_argument, NULL, OPT_OXS_STATS},
         {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
         {"help", no_argument, NULL, 'h'},
         {"option", no_argument, NULL, 'o'},
@@ -377,6 +403,11 @@ parse_options(int argc, char *argv[])
             may_create = true;
             break;
 
+        case OPT_OXS_STATS:
+            oxs = 0;
+            set_oxs_bitmap(optarg);
+            break;
+
         DAEMON_OPTION_HANDLERS
         OFP_VERSION_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
@@ -425,7 +456,20 @@ parse_options(int argc, char *argv[])
     allowed_protocols &= version_protocols;
     mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap(
                                   allowed_protocols));
-
+   /* Openflow 1.5 should also be given explicitly with --oxs-stats option */
+    if (!oxs) {
+        enum ofputil_protocol oxs_allowed_protocols = OFPUTIL_P_OF15_UP;
+        if (!(oxs_allowed_protocols & version_protocols)) {
+            char *protocols =
+            ofputil_protocols_to_string(oxs_allowed_protocols);
+            struct ds version_s = DS_EMPTY_INITIALIZER;
+
+            ofputil_format_version_bitmap_names(&version_s, versions);
+            ovs_fatal(0, "None of the enabled OpenFlow versions (%s) supports "
+                  "oxs (%s).(Use -O to enable additional OpenFlow versions)",
+                   ds_cstr(&version_s), protocols);
+        }
+    }
     colors_init(enable_color);
 }
 
@@ -693,6 +737,50 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
 }
 
 static void
+dump_agg_transaction(struct vconn *vconn, struct ofpbuf *request,
+                     struct ofputil_aggregate_stats *stats)
+{
+    const struct ofp_header *oh = request->data;
+    if (ofpmsg_is_stat_request(oh)) {
+        ovs_be32 send_xid = oh->xid;
+        enum ofpraw request_raw;
+        bool done = false;
+
+        ofpraw_decode_partial(&request_raw, request->data, request->size);
+        ofpraw_stats_request_to_reply(request_raw, oh->version);
+        send_openflow_buffer(vconn, request);
+
+        while (!done) {
+            ovs_be32 recv_xid;
+            struct ofpbuf *reply;
+
+            run(vconn_recv_block(vconn, &reply),
+                "OpenFlow packet receive failed");
+            recv_xid = ((struct ofp_header *) reply->data)->xid;
+            if (send_xid == recv_xid) {
+
+                ofputil_decode_aggregate_stats_reply(stats, reply->data);
+                done = true;
+            } else {
+                VLOG_DBG("received reply with xid %08"PRIx32" "
+                         "!= expected %08"PRIx32, recv_xid, send_xid);
+            }
+            ofpbuf_delete(reply);
+        }
+    } else {
+        struct ofpbuf *reply;
+
+        run(vconn_transact(vconn, request, &reply), "talking to %s",
+            vconn_get_name(vconn));
+        ofp_print(stdout, reply->data, reply->size,
+                  ports_to_show(vconn_get_name(vconn)),
+                  tables_to_show(vconn_get_name(vconn)),
+                  verbosity + 1);
+        ofpbuf_delete(reply);
+    }
+}
+
+static void
 dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
 {
     struct ofpbuf *request;
@@ -1475,7 +1563,6 @@ ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
     struct ofputil_flow_stats_request fsr;
     enum ofputil_protocol protocol;
     struct vconn *vconn;
-
     vconn = prepare_dump_flows(argc, argv, aggregate, &fsr, &protocol);
     dump_transaction(vconn, ofputil_encode_flow_stats_request(&fsr, protocol));
     vconn_close(vconn);
@@ -1550,7 +1637,7 @@ compare_flows(const void *afs_, const void *bfs_)
 static void
 ofctl_dump_flows(struct ovs_cmdl_context *ctx)
 {
-    if (!n_criteria && !should_show_names() && show_stats) {
+    if (!n_criteria && !should_show_names() && show_stats && oxs) {
         ofctl_dump_flows__(ctx->argc, ctx->argv, false);
         return;
     } else {
@@ -1571,8 +1658,12 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
         struct ds s = DS_EMPTY_INITIALIZER;
         for (size_t i = 0; i < n_fses; i++) {
             ds_clear(&s);
-            ofp_print_flow_stats(&s, &fses[i], ports_to_show(ctx->argv[1]),
-                                 tables_to_show(ctx->argv[1]), show_stats);
+            if (!oxs) {
+                ofp_print_flow_oxs_stats(&s, &fses[i], oxs, oxs_field_set);
+            } else {
+                ofp_print_flow_stats(&s, &fses[i], ports_to_show(ctx->argv[1]),
+                                     tables_to_show(ctx->argv[1]), show_stats);
+            }
             printf(" %s\n", ds_cstr(&s));
         }
         ds_destroy(&s);
@@ -1589,7 +1680,27 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
 static void
 ofctl_dump_aggregate(struct ovs_cmdl_context *ctx)
 {
-    ofctl_dump_flows__(ctx->argc, ctx->argv, true);
+    if (oxs) {
+        ofctl_dump_flows__(ctx->argc, ctx->argv, true);
+    } else {
+        struct ofputil_aggregate_stats stats;
+        struct ofputil_flow_stats_request fsr;
+        enum ofputil_protocol protocol;
+        struct vconn *vconn;
+
+        vconn = prepare_dump_flows(ctx->argc, ctx->argv, true,
+                                   &fsr, &protocol);
+        dump_agg_transaction(vconn,
+                             ofputil_encode_flow_stats_request(&fsr,
+                             protocol), &stats);
+        struct ds s = DS_EMPTY_INITIALIZER;
+        ds_clear(&s);
+        ofp_print_flow_oxs_agg_stats(&s, stats, oxs, oxs_field_set);
+        printf(" %s\n", ds_cstr(&s));
+        ds_destroy(&s);
+
+        vconn_close(vconn);
+    }
 }
 
 static void
-- 
1.9.1



More information about the dev mailing list