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

SatyaValli satyavalli.rama at gmail.com
Tue Feb 6 11:47:19 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" needs to be 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> idle_time
ovs-ofctl dump-flows -O OpenFlow15 <bridge> packet_count
ovs-ofctl dump-flows -O OpenFlow15 <bridge> byte_count

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

Flow Descritption
ovs-ofctl dump-flow-desc -O OpenFlow15 <bridge> idle_time
ovs-ofctl dump-flow-desc -O OpenFlow15 <bridge> packet_count
ovs-ofctl dump-flow-desc -O OpenFlow15 <bridge> byte_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                            |   9 +
 include/openflow/openflow-1.5.h |  81 ++++
 include/openvswitch/ofp-msgs.h  |  31 +-
 include/openvswitch/ofp-parse.h |   6 +-
 include/openvswitch/ofp-util.h  |  18 +-
 lib/automake.mk                 |   2 +
 lib/ofp-parse.c                 |  71 ++-
 lib/ofp-print.c                 |   2 +
 lib/ofp-util.c                  | 287 +++++++++++-
 lib/ox-stat.c                   | 984 ++++++++++++++++++++++++++++++++++++++++
 lib/ox-stat.h                   |  52 +++
 lib/rconn.c                     |   2 +
 ofproto/ofproto.c               |   9 +-
 tests/ofp-print.at              | 127 ++++++
 tests/ofproto-dpif.at           |  84 ++++
 tests/ofproto.at                |   5 +
 utilities/ovs-ofctl.8.in        |  48 +-
 utilities/ovs-ofctl.c           |  34 +-
 18 files changed, 1796 insertions(+), 56 deletions(-)
 create mode 100644 lib/ox-stat.c
 create mode 100644 lib/ox-stat.h

diff --git a/NEWS b/NEWS
index 8c360ba..87d2228 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,15 @@ Post-v2.9.0
     - ovs-vswitchd:
       * New options --l7 and --l7-len to "ofproto/trace" command.
    - ovs-ofctl:
+     * Existing flow entry statistics are redefined as standard OXS(OpenFlow
+        Extensible Statistics) fields for displaying the arbitrary flow stats.
+     * Now "ovs-ofctl dump-flows" needs to be provided with the arbitrary OXS
+       fields i.e flow duration, flow count, packet count, byte count or all
+       for displaying the desired flow stats.By default with "ovs-ofctl dump-
+       flows" displays only flow duration. See ovs-ofctl(8) for details.
+     * The existing flow statistics are renamed as Flow Description. Now the
+       information about individual flow entries will be displayed with the
+       help of ovs-ofctl dump-flow-desc. 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 5f3815c..109dcc0 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,
 
@@ -291,8 +293,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,
 
@@ -300,20 +306,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,
 
@@ -559,6 +573,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.
@@ -634,16 +649,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-parse.h b/include/openvswitch/ofp-parse.h
index c4228a5..4aea090 100644
--- a/include/openvswitch/ofp-parse.h
+++ b/include/openvswitch/ofp-parse.h
@@ -43,7 +43,8 @@ struct ofputil_tlv_table_mod;
 struct simap;
 enum ofputil_protocol;
 
-char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
+char *parse_ofp_str(struct ofputil_flow_mod *, int command,
+                    uint8_t *oxs_field_set, const char *str_,
                     const struct ofputil_port_map *,
                     const struct ofputil_table_map *,
                     enum ofputil_protocol *usable_protocols)
@@ -77,7 +78,8 @@ char *parse_ofp_flow_mod_file(const char *file_name,
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
-                                       bool aggregate, const char *string,
+                                       bool aggregate, bool flow_desc,
+                                       const char *string,
                                        const struct ofputil_port_map *,
                                        const struct ofputil_table_map *,
                                        enum ofputil_protocol *usable_protocols)
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index 5dd1b34..5fa6307 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -374,9 +374,20 @@ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
 struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
                                        enum ofputil_protocol);
 
-/* Flow stats or aggregate stats request, independent of protocol. */
+/* Flow stats or flow description stats or aggregate stats request,
+ * independent of protocol.
+ *
+ * The boolean variable flow_desc is used to distinguish OFPMP_FLOW_DESC
+ * from OFPMP_FLOW_STATS and the boolean variable aggregate is used to
+ * distinguish OFPMP_AGGREGATE_STATS from OFPMP_FLOW_STATS.
+ *
+ * The oxs_fields in the request messages is just for the sake of avoiding
+ * garbage values, and the values of this variable oxs_fields is for mentioning
+ * the stats field in the request/reply message. */
 struct ofputil_flow_stats_request {
+    uint8_t oxs_fields;         /* one or more OXS Fields */
     bool aggregate;             /* Aggregate results? */
+    bool flow_desc;             /* Flow Desc results? */
     struct match match;
     ovs_be64 cookie;
     ovs_be64 cookie_mask;
@@ -417,7 +428,8 @@ int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
                                     struct ofpbuf *ofpacts);
 void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
                                      struct ovs_list *replies,
-                                     const struct tun_table *);
+                                     const struct tun_table *,
+                                     uint8_t oxs_field_set);
 
 /* Aggregate stats reply, independent of protocol. */
 struct ofputil_aggregate_stats {
@@ -428,7 +440,7 @@ struct ofputil_aggregate_stats {
 
 struct ofpbuf *ofputil_encode_aggregate_stats_reply(
     const struct ofputil_aggregate_stats *stats,
-    const struct ofp_header *request);
+    const struct ofp_header *request, uint8_t oxs_field_set);
 enum ofperr ofputil_decode_aggregate_stats_reply(
     struct ofputil_aggregate_stats *,
     const struct ofp_header *reply);
diff --git a/lib/automake.mk b/lib/automake.mk
index 38d2a99..8ebfd1d 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -201,6 +201,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 1e30c20..ac4fd9d 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -41,6 +41,7 @@
 #include "simap.h"
 #include "socket-util.h"
 #include "util.h"
+#include "ox-stat.h"
 
 /* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
  *
@@ -189,6 +190,29 @@ str_to_connhelper(const char *str, uint16_t *alg)
     return xasprintf("invalid conntrack helper \"%s\"", str);
 }
 
+static 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;
+}
+
 struct protocol {
     const char *name;
     uint16_t dl_type;
@@ -320,7 +344,8 @@ extract_actions(char *s)
 
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
+parse_ofp_str__(struct ofputil_flow_mod *fm, int command,
+                uint8_t *oxs_field_set, char *string,
                 const struct ofputil_port_map *port_map,
                 const struct ofputil_table_map *table_map,
                 enum ofputil_protocol *usable_protocols)
@@ -413,10 +438,13 @@ 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_field *oxfs;
         const struct mf_field *mf;
         char *error = NULL;
 
-        if (parse_protocol(name, &p)) {
+        if (parse_oxs_field(name, &oxfs)) {
+           oxs_bitmap_set(oxs_field_set,oxfs->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);
@@ -613,16 +641,16 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
-parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
-              const struct ofputil_port_map *port_map,
+parse_ofp_str(struct ofputil_flow_mod *fm, int command, uint8_t *oxs_field_set,
+              const char *str_, const struct ofputil_port_map *port_map,
               const struct ofputil_table_map *table_map,
               enum ofputil_protocol *usable_protocols)
 {
     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, oxs_field_set, string, port_map,
+                            table_map, usable_protocols);
     if (error) {
         fm->ofpacts = NULL;
         fm->ofpacts_len = 0;
@@ -1105,7 +1133,7 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
                        int command,
                        enum ofputil_protocol *usable_protocols)
 {
-    char *error = parse_ofp_str(fm, command, string, port_map, table_map,
+    char *error = parse_ofp_str(fm, command, NULL, string, port_map, table_map,
                                 usable_protocols);
 
     if (!error) {
@@ -1325,29 +1353,44 @@ parse_ofp_flow_mod_file(const char *file_name,
 
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
-                                 bool aggregate, const char *string,
+                                 bool aggregate, bool flow_desc,
+                                 const char *string,
                                  const struct ofputil_port_map *port_map,
                                  const struct ofputil_table_map *table_map,
                                  enum ofputil_protocol *usable_protocols)
 {
     struct ofputil_flow_mod fm;
     char *error;
+    uint8_t oxs_field_set = 0;
+    enum oxs_ofb_stat_fields;
 
-    error = parse_ofp_str(&fm, -1, string, port_map, table_map,
+    error = parse_ofp_str(&fm, -1, &oxs_field_set, string, port_map, table_map,
                           usable_protocols);
     if (error) {
         return error;
     }
 
     /* Special table ID support not required for stats requests. */
-    if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) {
-        *usable_protocols |= OFPUTIL_P_OF10_STD;
-    }
-    if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) {
-        *usable_protocols |= OFPUTIL_P_OF10_NXM;
+    if (flow_desc) {
+        *usable_protocols = OFPUTIL_P_OF15_UP;
+    } else {
+        /* Special table ID support not required for stats requests. */
+        if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) {
+            *usable_protocols |= OFPUTIL_P_OF10_STD;
+        }
+        if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) {
+            *usable_protocols |= OFPUTIL_P_OF10_NXM;
+        }
     }
 
+    if (aggregate) {
+        oxs_bitmap_set(&oxs_field_set, OFPXST_OFB_FLOW_COUNT);
+    } else {
+        oxs_bitmap_set(&oxs_field_set, OFPXST_OFB_DURATION);
+    }
+    fsr->oxs_fields = oxs_field_set;
     fsr->aggregate = aggregate;
+    fsr->flow_desc = flow_desc;
     fsr->cookie = fm.cookie;
     fsr->cookie_mask = fm.cookie_mask;
     fsr->match = fm.match;
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index dede319..b467a66 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -3673,6 +3673,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);
 
@@ -3693,6 +3694,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/ofp-util.c b/lib/ofp-util.c
index 59fbf5f..e18c834 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -51,6 +51,7 @@
 #include "unaligned.h"
 #include "util.h"
 #include "uuid.h"
+#include "ox-stat.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_util);
 
@@ -2335,6 +2336,43 @@ 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,
+                                    bool flow_desc,
+                                    const struct tun_table *tun_table,
+                                    const struct vl_mff_map *vl_mff_map)
+{
+    const struct ofp15_flow_stats_request *ofsr;
+    enum ofperr error, stat_error;
+    uint16_t statlen;
+    uint8_t oxs_field_set;
+
+    ofsr = ofpbuf_pull(b, sizeof *ofsr);
+    fsr->aggregate = aggregate;
+    fsr->table_id = ofsr->table_id;
+    fsr->flow_desc = flow_desc;
+
+    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, &oxs_field_set);
+    fsr->oxs_fields = oxs_field_set;
+    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,
@@ -2779,10 +2817,21 @@ 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_REQUEST:
+        return ofputil_decode_ofpst15_flow_request(fsr, &b, false, 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, false,
+                                                   tun_table, vl_mff_map);
+    case OFPRAW_OFPST15_FLOW_DESC_REQUEST:
+        return ofputil_decode_ofpst15_flow_request(fsr, &b, false, 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);
@@ -2808,12 +2857,38 @@ 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_flow_stats_request *ofsr;
+
+        if (fsr->aggregate) {
+           raw = OFPRAW_OFPST15_AGGREGATE_REQUEST;
+        } else if (fsr->flow_desc) {
+           raw = OFPRAW_OFPST15_FLOW_DESC_REQUEST;
+        } else {
+           raw = OFPRAW_OFPST15_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);
+        /* ofputil_flow_stats structure is used to encode/decode oxs stats
+         * fields. Just for the sake of avoiding garbage values this structure
+         * is included in request messages with NULL value as an argument */
+        oxs_put_stat(msg, NULL, ofputil_protocol_to_ofp_version(protocol),
+                     fsr->oxs_fields);
+        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
@@ -2913,6 +2988,103 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
 
     if (!msg->size) {
         return EOF;
+    } else if (raw == OFPRAW_OFPST15_FLOW_REPLY) {
+        const struct ofp15_flow_stats_reply *ofs;
+        size_t length;
+        uint16_t padded_match_len;
+        uint16_t stat_len;
+        uint8_t oxs_field_set;
+
+        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;
+        fs->flags = 0;
+        fs->hard_timeout = 0;
+        fs->hard_age = -1;
+        if (oxs_pull_stat(msg, fs, &stat_len, &oxs_field_set)) {
+            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_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(&bad_ofmsg_rl, "OFPST_FLOW reply has %" PRIu32
+                         " leftover " "bytes at end", msg->size);
+            return EINVAL;
+        }
+
+        length = ntohs(ofd->length);
+        if (length < sizeof *ofd) {
+            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(ofd->priority);
+        fs->table_id = ofd->table_id;
+        fs->duration_sec = 0;
+        fs->duration_nsec = 0;
+        fs->idle_age = 0;
+        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(&bad_ofmsg_rl, "OXS OFPST_FLOW 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;
@@ -3074,7 +3246,8 @@ unknown_to_zero(uint64_t count)
 void
 ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
                                 struct ovs_list *replies,
-                                const struct tun_table *tun_table)
+                                const struct tun_table *tun_table,
+                                uint8_t oxs_field_set)
 {
     struct ofputil_flow_stats *fs_ = CONST_CAST(struct ofputil_flow_stats *,
                                                 fs);
@@ -3087,7 +3260,42 @@ 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_REPLY) {
+        struct ofp15_flow_stats_reply *ofs;
+
+        ofpbuf_put_uninit(reply, sizeof *ofs);
+        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);
+
+        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_OFPST15_FLOW_DESC_REPLY) {
+        struct ofp15_flow_desc *ofd;
+        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);
@@ -3180,7 +3388,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
 struct ofpbuf *
 ofputil_encode_aggregate_stats_reply(
     const struct ofputil_aggregate_stats *stats,
-    const struct ofp_header *request)
+    const struct ofp_header *request, uint8_t oxs_set_field)
 {
     struct ofp_aggregate_stats_reply *asr;
     uint64_t packet_count;
@@ -3189,6 +3397,12 @@ ofputil_encode_aggregate_stats_reply(
     enum ofpraw raw;
 
     ofpraw_decode(&raw, request);
+    if (raw == OFPRAW_OFPST15_AGGREGATE_REQUEST) {
+        enum ofp_version version = request->version;
+
+        msg = ofpraw_alloc_stats_reply(request, 0);
+        oxs_put_agg_stat(msg, stats, version, oxs_set_field);
+    } else {
     if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
         packet_count = unknown_to_zero(stats->packet_count);
         byte_count = unknown_to_zero(stats->byte_count);
@@ -3202,6 +3416,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;
 }
@@ -3211,12 +3426,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;
 
+    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;
 }
@@ -3230,7 +3455,25 @@ 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;
 
@@ -3313,12 +3556,32 @@ 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;
+        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_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,
@@ -10688,6 +10951,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:
@@ -10716,6 +10980,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/ox-stat.c b/lib/ox-stat.c
new file mode 100644
index 0000000..45ed535
--- /dev/null
+++ b/lib/ox-stat.c
@@ -0,0 +1,984 @@
+/*
+ * 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);
+
+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..1f1e820
--- /dev/null
+++ b/lib/ox-stat.h
@@ -0,0 +1,52 @@
+/*
+ * 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"
+
+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);
+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);
+#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/ofproto/ofproto.c b/ofproto/ofproto.c
index 02dd12b..e8b1564 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -4359,7 +4359,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
 
         fs.flags = flags;
         ofputil_append_flow_stats_reply(&fs, &replies,
-                                        ofproto_get_tun_tab(ofproto));
+                                        ofproto_get_tun_tab(ofproto),
+                                        fsr.oxs_fields);
     }
 
     rule_collection_unref(&rules);
@@ -4491,7 +4492,6 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     if (error) {
         return error;
     }
-
     memset(&stats, 0, sizeof stats);
     unknown_packets = unknown_bytes = false;
 
@@ -4528,7 +4528,8 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     rule_collection_unref(&rules);
     rule_collection_destroy(&rules);
 
-    reply = ofputil_encode_aggregate_stats_reply(&stats, oh);
+    reply = ofputil_encode_aggregate_stats_reply(&stats, oh,
+                                                 request.oxs_fields);
     ofconn_send_reply(ofconn, reply);
 
     return 0;
@@ -8122,6 +8123,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:
@@ -8199,6 +8201,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 821087c..2be5bba 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,22 @@ 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 78 00 00 00 04 00 01 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 3c 80 02 00 08 \
+00 00 00 00 00 00 00 00 80 02 02 08 00 00 00 00 \
+00 00 00 00 80 02 08 04 00 00 00 00 80 02 06 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_FLOW 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 +1503,34 @@ 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 20 00 00 00 04 00 01 00 00 00 00 00 00 \
+00 88 00 00 00 00 80 00 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 \
+02 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 00 00 00 02 \
+00 00 00 00 00 00 00 00 00 88 00 00 00 00 80 00 \
+00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \
+24 23 00 d6 00 99 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 88 29 e6 ed c0 \
+80 02 02 08 00 00 00 7a 00 01 00 00 80 02 08 08 \
+00 00 00 00 00 00 00 01 80 02 0a 08 00 00 00 00 \
+00 00 00 40 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 02 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW reply (OF1.5) (xid=0x4):
+ cookie=0x0, duration=152.703s, table=0, n_packets=2, n_bytes=128, idle_age=152, hard_age=0, ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 actions=output:2
+ cookie=0x0, duration=136.703s, table=0, n_packets=1, n_bytes=64, idle_age=122, hard_age=0, ip,metadata=0,in_port=2,dl_dst=24:23:00:d6:00:99,nw_tos=0 actions=output:2
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_AGGREGATE request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1507,6 +1567,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 +1614,59 @@ 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_FLOW_DESC request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 12 00 70 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 00 00 00 34 80 02 00 08 \
+00 00 00 00 00 00 00 00 80 02 02 08 00 00 00 00 \
+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_FLOW_DESC request (OF1.5) (xid=0x4):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_FLOW_DESC 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, hard_age=0, 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, hard_age=0, priority=4000,in_port=3 actions=NORMAL
+])
+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..8c1942d 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7265,6 +7265,90 @@ 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 desc reset_counts])
+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-flow-desc br0 packet_count,byte_count], [0], [stdout])
+    if [[ $1 -lt 13 -o "$5X" = "X" ]]; then
+        expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, idle_age=0, hard_age=0, ip actions=NORMAL"
+    else
+        expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, $5 idle_age=0, hard_age=0, 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 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
+
+    send_packet
+    warp_and_dump_OF $1 2 2 236
+    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
+
+    send_packet
+    warp_and_dump_OF $1 4 3 354
+    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 reset_counts
+
+    send_packet
+    warp_and_dump_OF $1 2 1 118 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 reset_counts
+
+    # Modify flow having reset_counts flag without reset_counts
+    send_packet
+    warp_and_dump_OF $1 4 1 118 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow])
+    warp_and_dump_OF $1 5 1 118 reset_counts
+
+    # Add flow having reset_counts flag without reset_counts
+    send_packet
+    warp_and_dump_OF $1 6 2 236 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow])
+    warp_and_dump_OF $1 1 2 236
+
+    # Modify flow w/o reset_counts flag with a flow_mod having reset_counts
+    send_packet
+    warp_and_dump_OF $1 2 3 354
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow])
+    warp_and_dump_OF $1 3 0 0
+}
+
+# OpenFlow versions >= 1.3 should behave the same way
 flow_mods_reset_counts 15
 
 OVS_VSWITCHD_STOP
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 1b0ba94..6ed7e94 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1402,8 +1402,13 @@ 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):
+ in_port=1 actions=drop
+])
+AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flow-desc br0 | ofctl_strip], [0], [dnl
+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..1394e2d 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -215,11 +215,13 @@ See the description of \fBip_frag\fR, below, for a way to match on
 whether a packet is a fragment and on its fragment offset.
 .
 .TP
-\fBdump\-flows \fIswitch \fR[\fIflows\fR]
+\fBdump\-flows \fIswitch \fR[\fIflows\fR] \fR[\fIoxs-fields\fR]
 Prints to the console all flow entries in \fIswitch\fR's
 tables that match \fIflows\fR.  If \fIflows\fR is omitted, all flows
 in the switch are retrieved.  See \fBFlow Syntax\fR, below, for the
-syntax of \fIflows\fR.  The output format is described in
+syntax of \fIflows\fR. If \fIoxs-fields\fR is emitted, all the \fIoxs-fields\fR
+except duration will be displayed as zero.See \fBoxs-fields Syntax\fR, below,
+for the syntax of \fIoxs-fields\fR.The output format is described in
 \fBTable Entry Output\fR.
 .
 .IP
@@ -232,12 +234,24 @@ formatting.  See the descriptions of these options, under
 \fBOPTIONS\fR below, for more information
 .
 .TP
-\fBdump\-aggregate \fIswitch \fR[\fIflows\fR]
+\fBdump\-flow\-desc \fIswitch \fR[\fIflows\fR] \fR[\fIoxs-fields\fR]
+Prints to the console all flow descriptions and entries in \fIswitch\fR's
+tables that match \fIflows\fR.  If \fIflows\fR is omitted, all flows
+in the switch are retrieved.  See \fBFlow Syntax\fR, below, for the
+syntax of \fIflows\fR. If \fIoxs-fields\fR is emitted, all the \fIoxs-fields\fR
+except duration will be displayed as zero.See \fBoxs-fields Syntax\fR, below,
+for the syntax of \fIoxs-fields\fR.The output format is described in
+\fBTable Entry Output\fR.
+.
+.TP
+\fBdump\-aggregate \fIswitch \fR[\fIflows\fR] \fR[\fIoxs-fields\fR]
 Prints to the console aggregate statistics for flows in
 \fIswitch\fR's tables that match \fIflows\fR.  If \fIflows\fR is omitted, 
 the statistics are aggregated across all flows in the switch's flow
 tables.  See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
-The output format is described in \fBTable Entry Output\fR.
+If \fIoxs-fields\fR is emitted, all the \fIoxs-fields\fR except duration
+will be displayed as zero.See \fBoxs-fields Syntax\fR, below,for the syntax
+of \fIoxs-fields\fR.The output format is described in \fBTable Entry Output\fR.
 .
 .IP "\fBqueue\-stats \fIswitch \fR[\fIport \fR[\fIqueue\fR]]"
 Prints to the console statistics for the specified \fIqueue\fR on
@@ -2360,6 +2374,32 @@ OpenFlow 1.4 support for OVS by setting the OVSDB \fIprotocols\fR
 column in the \fIbridge\fR table.
 .RE
 .
+.SS "oxs-fields Syntax"
+.PP
+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
+.
 .so lib/ofp-version.man
 .
 .IP "\fB\-F \fIformat\fR[\fB,\fIformat\fR...]"
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 6a30efe..d9b9e80 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -627,10 +627,8 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
         enum ofpraw request_raw;
         enum ofpraw reply_raw;
         bool done = false;
-
         ofpraw_decode_partial(&request_raw, request->data, request->size);
         reply_raw = ofpraw_stats_request_to_reply(request_raw, oh->version);
-
         send_openflow_buffer(vconn, request);
         while (!done) {
             ovs_be32 recv_xid;
@@ -641,7 +639,6 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
             recv_xid = ((struct ofp_header *) reply->data)->xid;
             if (send_xid == recv_xid) {
                 enum ofpraw raw;
-
                 ofp_print(stdout, reply->data, reply->size,
                           ports_to_show(vconn_get_name(vconn)),
                           tables_to_show(vconn_get_name(vconn)),
@@ -1430,7 +1427,7 @@ set_protocol_for_flow_dump(struct vconn *vconn,
 }
 
 static struct vconn *
-prepare_dump_flows(int argc, char *argv[], bool aggregate,
+prepare_dump_flows(int argc, char *argv[], bool aggregate, bool flow_desc,
                    struct ofputil_flow_stats_request *fsr,
                    enum ofputil_protocol *protocolp)
 {
@@ -1444,7 +1441,7 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
         = *match ? ports_to_accept(vconn_name) : NULL;
     const struct ofputil_table_map *table_map
         = *match ? tables_to_accept(vconn_name) : NULL;
-    error = parse_ofp_flow_stats_request_str(fsr, aggregate, match,
+    error = parse_ofp_flow_stats_request_str(fsr, aggregate, flow_desc, match,
                                              port_map, table_map,
                                              &usable_protocols);
     if (error) {
@@ -1457,13 +1454,13 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
 }
 
 static void
-ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
+ofctl_dump_flows__(int argc, char *argv[], bool aggregate, bool flow_desc)
 {
     struct ofputil_flow_stats_request fsr;
     enum ofputil_protocol protocol;
     struct vconn *vconn;
-
-    vconn = prepare_dump_flows(argc, argv, aggregate, &fsr, &protocol);
+    vconn = prepare_dump_flows(argc, argv, aggregate, flow_desc,
+                               &fsr, &protocol);
     dump_transaction(vconn, ofputil_encode_flow_stats_request(&fsr, protocol));
     vconn_close(vconn);
 }
@@ -1535,17 +1532,23 @@ compare_flows(const void *afs_, const void *bfs_)
 }
 
 static void
+ofctl_dump_flow_desc (struct ovs_cmdl_context *ctx)
+{
+  ofctl_dump_flows__(ctx->argc, ctx->argv, false, true);
+}
+
+static void
 ofctl_dump_flows(struct ovs_cmdl_context *ctx)
 {
     if (!n_criteria && !should_show_names() && show_stats) {
-        ofctl_dump_flows__(ctx->argc, ctx->argv, false);
+        ofctl_dump_flows__(ctx->argc, ctx->argv, false, false);
         return;
     } else {
         struct ofputil_flow_stats_request fsr;
         enum ofputil_protocol protocol;
         struct vconn *vconn;
 
-        vconn = prepare_dump_flows(ctx->argc, ctx->argv, false,
+        vconn = prepare_dump_flows(ctx->argc, ctx->argv, false, false,
                                    &fsr, &protocol);
 
         struct ofputil_flow_stats *fses;
@@ -1576,7 +1579,7 @@ 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);
+    ofctl_dump_flows__(ctx->argc, ctx->argv, true, false);
 }
 
 static void
@@ -3635,8 +3638,8 @@ read_flows_from_file(const char *filename, struct fte_state *state, int index)
         char *error;
         enum ofputil_protocol usable;
 
-        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), state->port_map,
-                              state->table_map, &usable);
+        error = parse_ofp_str(&fm, OFPFC_ADD, NULL, ds_cstr(&s),
+                              state->port_map, state->table_map, &usable);
         if (error) {
             ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
         }
@@ -4485,7 +4488,8 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
     string_s = match_to_string(&match, NULL, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
-    error_s = parse_ofp_str(&fm, -1, string_s, NULL, NULL, &usable_protocols);
+    error_s = parse_ofp_str(&fm, -1, NULL, string_s, NULL, NULL,
+                            &usable_protocols);
     if (error_s) {
         ovs_fatal(0, "%s", error_s);
     }
@@ -4775,6 +4779,8 @@ static const struct ovs_cmdl_command all_commands[] = {
       1, 1, ofctl_dump_table_features, OVS_RO },
     { "dump-table-desc", "switch",
       1, 1, ofctl_dump_table_desc, OVS_RO },
+    { "dump-flow-desc", "switch",
+      1, 2, ofctl_dump_flow_desc, OVS_RO },
     { "dump-flows", "switch",
       1, 2, ofctl_dump_flows, OVS_RO },
     { "dump-aggregate", "switch",
-- 
1.9.1



More information about the dev mailing list