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

Message ID 1514804324-22442-1-git-send-email-satyavalli.rama@tcs.com
State Changes Requested
Headers show
Series
  • [ovs-dev,V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
Related show

Commit Message

SatyaValli Jan. 1, 2018, 10:58 a.m.
From: SatyaValli <satyavalli.rama@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@tcs.com>
Co-authored-by: Lavanya Harivelam <harivelam.lavanya@tcs.com>
Signed-off-by: Lavanya Harivelam <harivelam.lavanya@tcs.com>
Co-authored-by: Surya Muttamsetty <muttamsetty.surya@tcs.com>
Signed-off-by: Surya Muttamsetty <muttamsetty.surya@tcs.com>
Co-authored-by: Manasa Cherukupally <manasa.cherukupally@tcs.com>
Signed-off-by: Manasa Cherukupally <manasa.cherukupally@tcs.com>
Co-authored-by: Pavani Panthagada <p.pavani1@tcs.com>
Signed-off-by: Pavani Panthagada <p.pavani1@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                 |  72 ++-
 lib/ofp-print.c                 |   2 +
 lib/ofp-util.c                  | 286 +++++++++++-
 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, 1795 insertions(+), 57 deletions(-)
 create mode 100644 lib/ox-stat.c
 create mode 100644 lib/ox-stat.h

Comments

Jan Scheurich Jan. 2, 2018, 8:13 a.m. | #1
> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of SatyaValli
> Sent: Monday, 01 January, 2018 11:59
> To: dev@openvswitch.org
> Cc: Manasa Cherukupally <manasa.cherukupally@tcs.com>; Surya Muttamsetty <muttamsetty.surya@tcs.com>; Pavani Panthagada
> <p.pavani1@tcs.com>; SatyaValli <satyavalli.rama@tcs.com>; Lavanya Harivelam <harivelam.lavanya@tcs.com>
> Subject: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
> 
> From: SatyaValli <satyavalli.rama@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

This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?

> 
> 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
>
Ben Pfaff Jan. 2, 2018, 7 p.m. | #2
On Tue, Jan 02, 2018 at 08:13:12AM +0000, Jan Scheurich wrote:
> > -----Original Message-----
> > From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of SatyaValli
> > Sent: Monday, 01 January, 2018 11:59
> > To: dev@openvswitch.org
> > Cc: Manasa Cherukupally <manasa.cherukupally@tcs.com>; Surya Muttamsetty <muttamsetty.surya@tcs.com>; Pavani Panthagada
> > <p.pavani1@tcs.com>; SatyaValli <satyavalli.rama@tcs.com>; Lavanya Harivelam <harivelam.lavanya@tcs.com>
> > Subject: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
> > 
> > From: SatyaValli <satyavalli.rama@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
> 
> This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?

I think you might be misinterpreting the meaning here.  It doesn't
appear to break compatibility, at least not in a major way, since it
doesn't do a lot of updates to the tests that would otherwise be
required.
Jan Scheurich Jan. 3, 2018, 4:24 p.m. | #3
> > >
> > > 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
> >
> > This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new
> command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?
> 
> I think you might be misinterpreting the meaning here.  It doesn't
> appear to break compatibility, at least not in a major way, since it
> doesn't do a lot of updates to the tests that would otherwise be
> required.

Perhaps I am missing the point of some of these changes. I understand that OVS needs to support the new extensible OXS flow stats syntax in OpenFlow 1.5 and the differentiated MP request/reply pairs OFPMP_FLOW_DESC (replacing the former OFPMP_FLOW) and OFPMP_FLOW_STATS (just fetching flow stats per flow w/o the rest of the flow data).

But I don't understand why this should have any impact on the existing CLI command "ovs-ofctl dump-flows" and its output. This tool expressly fetches and displays the complete flow dump from OVS, including match, instructions/actions and statistics. When using OF 1.5 it should transparently apply OFPMP_FLOW_DESC MP request/reply to fetch the data, up to OF 1.4 it should use the original OFPMP_FLOW.

I can't see any ovs-ofctl use case that would justify the use of the new OFPMP_FLOW_STATS request/reply. The removed data in the reply compared to the full flow description are mainly the instructions, the full match is still there to identify each flow. So cutting down the transferred data volume can hardly be the reason (Note, this may still be different for real OF 1.5 controllers).

If you believe we should have an ovs-ofctl command anyhow, e.g. for testing purposes, I suggest to introduce a new command or add an option to dump-flows to force use of this particular MP message. The output would be limited to flow match and stats in that case.

The new command to dump aggregate flow stats is of course a welcome addition. It should work irrespectively of the used OpenFlow version with the OFPMP_AGGREGATE_STATS primitive using classic flow stats prior to OF 1.5 and OXS flow stats in OF 1.5.

All in all I am NACK-ing this patch as it stands.

Regards, Jan

BTW: I have played a bit with the patch. The existing ovs-ofctl test cases appear to not break because the changes described in the commit message and the documentation are not effective. The legacy command format "ovs-ofctl dump-flows -O OpenFlow15 <bridge> [<match>]" still produces the complete flow dump including instructions/actions:

# ovs-ofctl -Oopenflow15 dump-flow-desc br0
OFPST_FLOW_DESC reply (OF1.5) (xid=0x2):
 cookie=0x0, duration=90.515s, table=0, n_packets=0, n_bytes=0, reset_counts idle_age=840, hard_age=32766, in_port="br0-ns1" actions=output:"br0-ns2"
 cookie=0x0, duration=90.507s, table=0, n_packets=0, n_bytes=0, reset_counts idle_age=840, hard_age=32766, in_port="br0-ns2" actions=output:"br0-ns1"
 cookie=0x0, duration=850.125s, table=0, n_packets=2, n_bytes=180, idle_age=849, hard_age=32766, priority=0 actions=NORMAL

# ovs-ofctl -Oopenflow15 dump-flows br0
 cookie=0x0, duration=94.634s, table=0, n_packets=0, n_bytes=0, idle_age=844, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
 cookie=0x0, duration=94.626s, table=0, n_packets=0, n_bytes=0, idle_age=844, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
 cookie=0x0, duration=854.244s, table=0, n_packets=2, n_bytes=180, idle_age=853, hard_age=0, priority=0 actions=NORMAL

The only difference appears to be that the flow property reset_counts is missing and that hard_age seems corrupt in the new ovs-ofctl dump-flow-desc.

Furthermore, specifying any OXS field at the end of ovs-ofctl dump-flows <bridge> is without effect:

# ovs-ofctl -Oopenflow15 dump-flows br0 duration
 cookie=0x0, duration=173.536s, table=0, n_packets=0, n_bytes=0, idle_age=923, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
 cookie=0x0, duration=173.528s, table=0, n_packets=0, n_bytes=0, idle_age=923, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
 cookie=0x0, duration=933.146s, table=0, n_packets=2, n_bytes=180, idle_age=932, hard_age=0, priority=0 actions=NORMAL

# ovs-ofctl -Oopenflow15 dump-flows br0 packet_count
 cookie=0x0, duration=184.600s, table=0, n_packets=0, n_bytes=0, idle_age=934, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
 cookie=0x0, duration=184.593s, table=0, n_packets=0, n_bytes=0, idle_age=934, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
 cookie=0x0, duration=944.211s, table=0, n_packets=2, n_bytes=180, idle_age=943, hard_age=0, priority=0 actions=NORMAL

When specifying it in addition to some filter match it is rejected:

# ovs-ofctl -Oopenflow15 dump-flows br0 in_port="br0-ns1" packet_count
ovs-ofctl: 'dump-flows' command takes at most 2 arguments
Ben Pfaff Jan. 4, 2018, 9:04 p.m. | #4
On Wed, Jan 03, 2018 at 04:24:06PM +0000, Jan Scheurich wrote:
> > > >
> > > > 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
> > >
> > > This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new
> > command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?
> > 
> > I think you might be misinterpreting the meaning here.  It doesn't
> > appear to break compatibility, at least not in a major way, since it
> > doesn't do a lot of updates to the tests that would otherwise be
> > required.
> 
> Perhaps I am missing the point of some of these changes. I understand that OVS needs to support the new extensible OXS flow stats syntax in OpenFlow 1.5 and the differentiated MP request/reply pairs OFPMP_FLOW_DESC (replacing the former OFPMP_FLOW) and OFPMP_FLOW_STATS (just fetching flow stats per flow w/o the rest of the flow data).
> 
> But I don't understand why this should have any impact on the existing CLI command "ovs-ofctl dump-flows" and its output. This tool expressly fetches and displays the complete flow dump from OVS, including match, instructions/actions and statistics. When using OF 1.5 it should transparently apply OFPMP_FLOW_DESC MP request/reply to fetch the data, up to OF 1.4 it should use the original OFPMP_FLOW.
> 
> I can't see any ovs-ofctl use case that would justify the use of the new OFPMP_FLOW_STATS request/reply. The removed data in the reply compared to the full flow description are mainly the instructions, the full match is still there to identify each flow. So cutting down the transferred data volume can hardly be the reason (Note, this may still be different for real OF 1.5 controllers).
> 
> If you believe we should have an ovs-ofctl command anyhow, e.g. for testing purposes, I suggest to introduce a new command or add an option to dump-flows to force use of this particular MP message. The output would be limited to flow match and stats in that case.
> 
> The new command to dump aggregate flow stats is of course a welcome addition. It should work irrespectively of the used OpenFlow version with the OFPMP_AGGREGATE_STATS primitive using classic flow stats prior to OF 1.5 and OXS flow stats in OF 1.5.
> 
> All in all I am NACK-ing this patch as it stands.
> 
> Regards, Jan
> 
> BTW: I have played a bit with the patch. The existing ovs-ofctl test cases appear to not break because the changes described in the commit message and the documentation are not effective. The legacy command format "ovs-ofctl dump-flows -O OpenFlow15 <bridge> [<match>]" still produces the complete flow dump including instructions/actions:
> 
> # ovs-ofctl -Oopenflow15 dump-flow-desc br0
> OFPST_FLOW_DESC reply (OF1.5) (xid=0x2):
>  cookie=0x0, duration=90.515s, table=0, n_packets=0, n_bytes=0, reset_counts idle_age=840, hard_age=32766, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=90.507s, table=0, n_packets=0, n_bytes=0, reset_counts idle_age=840, hard_age=32766, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=850.125s, table=0, n_packets=2, n_bytes=180, idle_age=849, hard_age=32766, priority=0 actions=NORMAL
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0
>  cookie=0x0, duration=94.634s, table=0, n_packets=0, n_bytes=0, idle_age=844, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=94.626s, table=0, n_packets=0, n_bytes=0, idle_age=844, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=854.244s, table=0, n_packets=2, n_bytes=180, idle_age=853, hard_age=0, priority=0 actions=NORMAL
> 
> The only difference appears to be that the flow property reset_counts is missing and that hard_age seems corrupt in the new ovs-ofctl dump-flow-desc.
> 
> Furthermore, specifying any OXS field at the end of ovs-ofctl dump-flows <bridge> is without effect:
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0 duration
>  cookie=0x0, duration=173.536s, table=0, n_packets=0, n_bytes=0, idle_age=923, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=173.528s, table=0, n_packets=0, n_bytes=0, idle_age=923, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=933.146s, table=0, n_packets=2, n_bytes=180, idle_age=932, hard_age=0, priority=0 actions=NORMAL
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0 packet_count
>  cookie=0x0, duration=184.600s, table=0, n_packets=0, n_bytes=0, idle_age=934, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=184.593s, table=0, n_packets=0, n_bytes=0, idle_age=934, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=944.211s, table=0, n_packets=2, n_bytes=180, idle_age=943, hard_age=0, priority=0 actions=NORMAL
> 
> When specifying it in addition to some filter match it is rejected:
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0 in_port="br0-ns1" packet_count
> ovs-ofctl: 'dump-flows' command takes at most 2 arguments

SatyaValli, can you respond to this?
Satyavalli Rama Jan. 5, 2018, 11:25 a.m. | #5
Hi Jan and Ben,

Please find the inline responses.


-----Ben Pfaff <blp@ovn.org> wrote: -----
To: Jan Scheurich <jan.scheurich@ericsson.com>
From: Ben Pfaff <blp@ovn.org>
Date: 01/05/2018 02:35AM
Cc: SatyaValli <satyavalli.rama@gmail.com>, "dev@openvswitch.org" <dev@openvswitch.org>, Manasa Cherukupally <manasa.cherukupally@tcs.com>, Pavani Panthagada <p.pavani1@tcs.com>, Lavanya Harivelam <harivelam.lavanya@tcs.com>, Surya Muttamsetty <muttamsetty.surya@tcs.com>, SatyaValli <satyavalli.rama@tcs.com>
Subject: Re: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support

On Wed, Jan 03, 2018 at 04:24:06PM +0000, Jan Scheurich wrote:
> > > >
> > > > 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
> > >
> > > This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new
> > command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?
> > 
> > I think you might be misinterpreting the meaning here.  It doesn't
> > appear to break compatibility, at least not in a major way, since it
> > doesn't do a lot of updates to the tests that would otherwise be
> > required.
> 
> Perhaps I am missing the point of some of these changes. I understand that OVS needs to support the new extensible OXS flow stats syntax in OpenFlow 1.5 and the differentiated MP request/reply pairs OFPMP_FLOW_DESC (replacing the former OFPMP_FLOW) and OFPMP_FLOW_STATS (just fetching flow stats per flow w/o the rest of the flow data).
> 
> But I don't understand why this should have any impact on the existing CLI command "ovs-ofctl dump-flows" and its output. This tool expressly fetches and displays the complete flow dump from OVS, including match, instructions/actions and statistics. When using OF 1.5 it should transparently apply OFPMP_FLOW_DESC MP request/reply to fetch the data, up to OF 1.4 it should use the original OFPMP_FLOW.
> 
> I can't see any ovs-ofctl use case that would justify the use of the new OFPMP_FLOW_STATS request/reply. The removed data in the reply compared to the full flow description are mainly the instructions, the full match is still there to identify each flow. So cutting down the transferred data volume can hardly be the reason (Note, this may still be different for real OF 1.5 controllers).
> 
> If you believe we should have an ovs-ofctl command anyhow, e.g. for testing purposes, I suggest to introduce a new command or add an option to dump-flows to force use of this particular MP message. The output would be limited to flow match and stats in that case.
> 

As per our understanding and from previous review comments we treated OF1.5+ has two different ways to request and get replies for Flow Stats: FLOW_DESC and FLOW_STATS (which will be even used for Flow Stats Trigger). And we've supported this with the help of two commands

OFPMP_FLOW_DESC - ovs-ofctl dump-flow-desc -O OpenFlow15
OFPMP_FLOW_STATS - ovs-ofctl dump-flows -O OpenFlow15

> The new command to dump aggregate flow stats is of course a welcome addition. It should work irrespectively of the used OpenFlow version with the OFPMP_AGGREGATE_STATS primitive using classic flow stats prior to OF 1.5 and OXS flow stats in OF 1.5.
> 
> All in all I am NACK-ing this patch as it stands.
> 
> Regards, Jan
> 
> BTW: I have played a bit with the patch. The existing ovs-ofctl test cases appear to not break because the changes described in the commit message and the documentation are not effective. The legacy command format "ovs-ofctl dump-flows -O OpenFlow15 <bridge> [<match>]" still produces the complete flow dump including instructions/actions:
> 

We cross checked and tested the patch again on the latest OVS GIT and we haven't observed any of these issues. Could you please share your topology and any other configurations for better understanding of the issue:

Please find logs for the same with the below configuration / flow-add rules:

# git log --after="2018-01-03"
commit b02bf23fe3caa011a842f22fc802d77f989dd81b
Author: SatyaValli <satyavalli.rama@tcs.com>
Date:   Thu Jan 4 16:59:03 2018 +0530

    OF_Extensible_Statistics

commit 77b7d23230fecf774c2a707b7b42fdb8a65a8892
Author: Ben Pfaff <blp@ovn.org>
Date:   Wed Jan 3 16:11:02 2018 -0800

    ofp-actions: Log version, vendor, and type of unknown actions being parsed.
    
    This may help debugging difficult controller problems.
    
    Reported-by: Su Wang <suwang@vmware.com>
    Signed-off-by: Ben Pfaff <blp@ovn.org>
    Acked-by: Justin Pettit <jpettit@ovn.org>


# ovs-vsctl --version
ovs-vsctl (Open vSwitch) 2.8.90
DB Schema 7.15.1

# ovs-ofctl add-flow -O OpenFlow15 br0 icmp,reset_counts,check_overlap,actions=normal

# ovs-ofctl add-flow -O OpenFlow15 br0 udp,tp_dst=200,actions=normal


# ovs-ofctl add-flow -O OpenFlow15 br0 tcp,tp_src=100,actions=normal


> # ovs-ofctl -Oopenflow15 dump-flow-desc br0
> OFPST_FLOW_DESC reply (OF1.5) (xid=0x2):
>  cookie=0x0, duration=90.515s, table=0, n_packets=0, n_bytes=0, reset_counts idle_age=840, hard_age=32766, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=90.507s, table=0, n_packets=0, n_bytes=0, reset_counts idle_age=840, hard_age=32766, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=850.125s, table=0, n_packets=2, n_bytes=180, idle_age=849, hard_age=32766, priority=0 actions=NORMAL
> 
##Flow Description Dump:
## ovs-ofctl dump-flow-desc -O OpenFlow15 br0
OFPST_FLOW_DESC reply (OF1.5) (xid=0x2):
 cookie=0x0, duration=446.251s, table=0, n_packets=0, n_bytes=0, check_overlap reset_counts idle_age=0, hard_age=0, icmp actions=NORMAL
 cookie=0x0, duration=440.523s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL
 cookie=0x0, duration=436.579s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL
 cookie=0x0, duration=3451.715s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

## ovs-ofctl dump-flow-desc -O OpenFlow15 br0 packet_count
OFPST_FLOW_DESC reply (OF1.5) (xid=0x4):
 cookie=0x0, duration=338.579s, table=0, n_packets=32918, n_bytes=0, check_overlap reset_counts idle_age=0, hard_age=0, icmp actions=NORMAL
 cookie=0x0, duration=332.851s, table=0, n_packets=16417, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL
 cookie=0x0, duration=328.907s, table=0, n_packets=12309, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL
 cookie=0x0, duration=3344.043s, table=0, n_packets=53604586, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

> # ovs-ofctl -Oopenflow15 dump-flows br0
>  cookie=0x0, duration=94.634s, table=0, n_packets=0, n_bytes=0, idle_age=844, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=94.626s, table=0, n_packets=0, n_bytes=0, idle_age=844, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=854.244s, table=0, n_packets=2, n_bytes=180, idle_age=853, hard_age=0, priority=0 actions=NORMAL
> 
> The only difference appears to be that the flow property reset_counts is missing and that hard_age seems corrupt in the new ovs-ofctl dump-flow-desc.
> 
> Furthermore, specifying any OXS field at the end of ovs-ofctl dump-flows <bridge> is without effect:
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0 duration
>  cookie=0x0, duration=173.536s, table=0, n_packets=0, n_bytes=0, idle_age=923, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=173.528s, table=0, n_packets=0, n_bytes=0, idle_age=923, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=933.146s, table=0, n_packets=2, n_bytes=180, idle_age=932, hard_age=0, priority=0 actions=NORMAL
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0 packet_count
>  cookie=0x0, duration=184.600s, table=0, n_packets=0, n_bytes=0, idle_age=934, hard_age=0, in_port="br0-ns1" actions=output:"br0-ns2"
>  cookie=0x0, duration=184.593s, table=0, n_packets=0, n_bytes=0, idle_age=934, hard_age=0, in_port="br0-ns2" actions=output:"br0-ns1"
>  cookie=0x0, duration=944.211s, table=0, n_packets=2, n_bytes=180, idle_age=943, hard_age=0, priority=0 actions=NORMAL
> 

As OXS_OF_DURATION field is mandatory as per OF 1.5+ Spec for tracking the age of flow entry in the switch, we've kept it as a default field. The other fields OXS_OF_PACKET_COUNT and OXS_OF_BYTE_COUNT are kept as optional.

##Flow Status Dump:

## ovs-ofctl dump-flows -O OpenFlow15 br0 duration

 cookie=0x0, duration=235.211s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL

 cookie=0x0, duration=229.483s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL

 cookie=0x0, duration=225.539s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

 cookie=0x0, duration=3240.675s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

The OXS field value reflection is based on the flow match

## ovs-ofctl dump-flows -O OpenFlow15 br0 packet_count

 cookie=0x0, duration=176.872s, table=0, n_packets=1102, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL

 cookie=0x0, duration=171.144s, table=0, n_packets=544, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL

 cookie=0x0, duration=167.200s, table=0, n_packets=408, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL


 cookie=0x0, duration=3182.336s, table=0, n_packets=53592657, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 packet_count,idle_time

 cookie=0x0, duration=143.448s, table=0, n_packets=1102, n_bytes=0, idle_age=5, hard_age=0, icmp actions=NORMAL

 cookie=0x0, duration=137.720s, table=0, n_packets=544, n_bytes=0, idle_age=5, hard_age=0, udp,tp_dst=200 actions=NORMAL

 cookie=0x0, duration=133.776s, table=0, n_packets=408, n_bytes=0, idle_age=5, hard_age=0, tcp,tp_src=100 actions=NORMAL

 cookie=0x0, duration=3148.912s, table=0, n_packets=53592655, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

> When specifying it in addition to some filter match it is rejected:
> 
> # ovs-ofctl -Oopenflow15 dump-flows br0 in_port="br0-ns1" packet_count
> ovs-ofctl: 'dump-flows' command takes at most 2 arguments

There is a slight syntax mismatch, please find the below dumps with match field:

##Dumps with Match Field:
## ovs-ofctl dump-flows -O OpenFlow15 br0 tcp,packet_count
 cookie=0x0, duration=549s, table=0, n_packets=42146, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 tcp,byte_count
 cookie=0x0, duration=565.577s, table=0, n_packets=0, n_bytes=2275884, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 udp,packet_count
 cookie=0x0, duration=600.984s, table=0, n_packets=56200, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 icmp,packet_count
 cookie=0x0, duration=614.544s, table=0, n_packets=112611, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL

## ovs-ofctl dump-flow-desc -O OpenFlow15 br0 icmp,packet_count
OFPST_FLOW_DESC reply (OF1.5) (xid=0x4):
 cookie=0x0, duration=625.217s, table=0, n_packets=112611, n_bytes=0, check_overlap reset_counts idle_age=0, hard_age=0, icmp actions=NORMAL

## ovs-ofctl dump-aggregate -O OpenFlow15 br0 icmp,packet_count
OFPST_AGGREGATE reply (OF1.5) (xid=0x4): packet_count=112611 byte_count=0 flow_count=1

SatyaValli, can you respond to this?


Thanks & Regards
Satya Valli
Tata Consultancy Services
Mailto: satyavalli.rama@tcs.com
Website: http://www.tcs.com
Jan Scheurich Jan. 8, 2018, 12:53 p.m. | #6
Hi Satyavalli,

Please find my responses below.

Regards, Jan

From: Satyavalli Rama [mailto:satyavalli.rama@tcs.com]
Sent: Friday, 05 January, 2018 12:25
To: Ben Pfaff <blp@ovn.org>; Jan Scheurich <jan.scheurich@ericsson.com>
Cc: SatyaValli <satyavalli.rama@gmail.com>; dev@openvswitch.org; manasa.cherukupally@tcs.com; p.pavani1@tcs.com; Harivelam Lavanya <harivelam.lavanya@tcs.com>; muttamsetty.surya@tcs.com
Subject: Re: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support

Hi Jan and Ben,

Please find the inline responses.


-----Ben Pfaff <blp@ovn.org<mailto:blp@ovn.org>> wrote: -----
To: Jan Scheurich <jan.scheurich@ericsson.com<mailto:jan.scheurich@ericsson.com>>
From: Ben Pfaff <blp@ovn.org<mailto:blp@ovn.org>>
Date: 01/05/2018 02:35AM
Cc: SatyaValli <satyavalli.rama@gmail.com<mailto:satyavalli.rama@gmail.com>>, "dev@openvswitch.org<mailto:dev@openvswitch.org>" <dev@openvswitch.org<mailto:dev@openvswitch.org>>, Manasa Cherukupally <manasa.cherukupally@tcs.com<mailto:manasa.cherukupally@tcs.com>>, Pavani Panthagada <p.pavani1@tcs.com<mailto:p.pavani1@tcs.com>>, Lavanya Harivelam <harivelam.lavanya@tcs.com<mailto:harivelam.lavanya@tcs.com>>, Surya Muttamsetty <muttamsetty.surya@tcs.com<mailto:muttamsetty.surya@tcs.com>>, SatyaValli <satyavalli.rama@tcs.com<mailto:satyavalli.rama@tcs.com>>
Subject: Re: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
On Wed, Jan 03, 2018 at 04:24:06PM +0000, Jan Scheurich wrote:
> > > >
> > > > 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
> > >
> > > This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new
> > command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?
> >
> > I think you might be misinterpreting the meaning here.  It doesn't
> > appear to break compatibility, at least not in a major way, since it
> > doesn't do a lot of updates to the tests that would otherwise be
> > required.
>
> Perhaps I am missing the point of some of these changes. I understand that OVS needs to support the new extensible OXS flow stats syntax in OpenFlow 1.5 and the differentiated MP request/reply pairs OFPMP_FLOW_DESC (replacing the former OFPMP_FLOW) and OFPMP_FLOW_STATS (just fetching flow stats per flow w/o the rest of the flow data).
>
> But I don't understand why this should have any impact on the existing CLI command "ovs-ofctl dump-flows" and its output. This tool expressly fetches and displays the complete flow dump from OVS, including match, instructions/actions and statistics. When using OF 1.5 it should transparently apply OFPMP_FLOW_DESC MP request/reply to fetch the data, up to OF 1.4 it should use the original OFPMP_FLOW.
>
> I can't see any ovs-ofctl use case that would justify the use of the new OFPMP_FLOW_STATS request/reply. The removed data in the reply compared to the full flow description are mainly the instructions, the full match is still there to identify each flow. So cutting down the transferred data volume can hardly be the reason (Note, this may still be different for real OF 1.5 controllers).
>
> If you believe we should have an ovs-ofctl command anyhow, e.g. for testing purposes, I suggest to introduce a new command or add an option to dump-flows to force use of this particular MP message. The output would be limited to flow match and stats in that case.
>

As per our understanding and from previous review comments we treated OF1.5+ has two different ways to request and get replies for Flow Stats: FLOW_DESC and FLOW_STATS (which will be even used for Flow Stats Trigger). And we've supported this with the help of two commands

OFPMP_FLOW_DESC - ovs-ofctl dump-flow-desc -O OpenFlow15
OFPMP_FLOW_STATS - ovs-ofctl dump-flows -O OpenFlow15

[Jan] My argument still holds: "ovs-ofctl dump-flows" is the existing command to fetch and display the entire flow data from OVS. In OF 1.5+ it should use the OFPMP_FLOW_DESC primitive instead of the earlier OFPMP_FLOW used in older OF protocols.

If you want a way to exercise the new OFPMP_FLOW_STATS primitive you should introduce a new command "ovs-ofctl -Oopenflow15 dump-flow-stats" for that purpose or, alternatively, add an option to the dump-flows command to select using that primitive (and limit the output).

> The new command to dump aggregate flow stats is of course a welcome addition. It should work irrespectively of the used OpenFlow version with the OFPMP_AGGREGATE_STATS primitive using classic flow stats prior to OF 1.5 and OXS flow stats in OF 1.5.
>
> All in all I am NACK-ing this patch as it stands.
>
> Regards, Jan
>
> BTW: I have played a bit with the patch. The existing ovs-ofctl test cases appear to not break because the changes described in the commit message and the documentation are not effective. The legacy command format "ovs-ofctl dump-flows -O OpenFlow15 <bridge> [<match>]" still produces the complete flow dump including instructions/actions:
>

As OXS_OF_DURATION field is mandatory as per OF 1.5+ Spec for tracking the age of flow entry in the switch, we've kept it as a default field. The other fields OXS_OF_PACKET_COUNT and OXS_OF_BYTE_COUNT are kept as optional.

##Flow Status Dump:

## ovs-ofctl dump-flows -O OpenFlow15 br0 duration

 cookie=0x0, duration=235.211s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL

 cookie=0x0, duration=229.483s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL

 cookie=0x0, duration=225.539s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

 cookie=0x0, duration=3240.675s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

The OXS field value reflection is based on the flow match

## ovs-ofctl dump-flows -O OpenFlow15 br0 packet_count

 cookie=0x0, duration=176.872s, table=0, n_packets=1102, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL

 cookie=0x0, duration=171.144s, table=0, n_packets=544, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL

 cookie=0x0, duration=167.200s, table=0, n_packets=408, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

 cookie=0x0, duration=3182.336s, table=0, n_packets=53592657, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL
## ovs-ofctl dump-flows -O OpenFlow15 br0 packet_count,idle_time

 cookie=0x0, duration=143.448s, table=0, n_packets=1102, n_bytes=0, idle_age=5, hard_age=0, icmp actions=NORMAL

 cookie=0x0, duration=137.720s, table=0, n_packets=544, n_bytes=0, idle_age=5, hard_age=0, udp,tp_dst=200 actions=NORMAL

 cookie=0x0, duration=133.776s, table=0, n_packets=408, n_bytes=0, idle_age=5, hard_age=0, tcp,tp_src=100 actions=NORMAL

 cookie=0x0, duration=3148.912s, table=0, n_packets=53592655, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL

[Jan] Irrespective of the discrepancy of behavior in my test environment, I would like to ask what you consider the purpose of adding one or more OXS types to the dump-flows (and dump-flow-desc) commands. I could understand if the client could filter the requested flow stats but, as per the OF 1.5 spec, the OFPMP_FLOW_STATS request cannot selectively fetch a subset of the total flow stats. So the switch will always report all available statistics back.

The only thing the patch currently does is to discard not explicitly listed counters (except for duration, why?) on the receiver side and print zero values instead. What is the point?

For unit tests we typically apply some output filters to suppress non-deterministic or irrelevant data of a command outputs. Is that the use case you were thinking of? There is a "--no-stats" option already in ovs-ovctl that suppresses all flow stats if the user is not interested in those.

Unless there is a real use case that I am overlooking, I think you should just remove the OXS fields option from the affected commands.

> When specifying it in addition to some filter match it is rejected:
>
> # ovs-ofctl -Oopenflow15 dump-flows br0 in_port="br0-ns1" packet_count
> ovs-ofctl: 'dump-flows' command takes at most 2 arguments

There is a slight syntax mismatch, please find the below dumps with match field:

##Dumps with Match Field:
## ovs-ofctl dump-flows -O OpenFlow15 br0 tcp,packet_count
 cookie=0x0, duration=549s, table=0, n_packets=42146, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 tcp,byte_count
 cookie=0x0, duration=565.577s, table=0, n_packets=0, n_bytes=2275884, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 udp,packet_count
 cookie=0x0, duration=600.984s, table=0, n_packets=56200, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL

## ovs-ofctl dump-flows -O OpenFlow15 br0 icmp,packet_count
 cookie=0x0, duration=614.544s, table=0, n_packets=112611, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL

## ovs-ofctl dump-flow-desc -O OpenFlow15 br0 icmp,packet_count
OFPST_FLOW_DESC reply (OF1.5) (xid=0x4):
 cookie=0x0, duration=625.217s, table=0, n_packets=112611, n_bytes=0, check_overlap reset_counts idle_age=0, hard_age=0, icmp actions=NORMAL

## ovs-ofctl dump-aggregate -O OpenFlow15 br0 icmp,packet_count
OFPST_AGGREGATE reply (OF1.5) (xid=0x4): packet_count=112611 byte_count=0 flow_count=1

[Jan] This syntax is even more confusing as it mixes match fields and OXS filters. But that problem goes away when we get rid of the OXS filter option.
Satyavalli Rama Jan. 31, 2018, 11:59 a.m. | #7
Hi Ben,

Are you also agreeing with the Jan's comments.

Thanks & Regards
Satya Valli
Tata Consultancy Services
Mailto: satyavalli.rama@tcs.com
Website: http://www.tcs.com
Ben Pfaff Feb. 1, 2018, 10:13 p.m. | #8
At the very minimum I can't review a patch that breaks tests.

On Wed, Jan 31, 2018 at 05:29:12PM +0530, Satyavalli Rama wrote:
> Hi Ben,
> 
> Are you also agreeing with the Jan's comments.
> 
> Thanks & Regards
> Satya Valli
> Tata Consultancy Services
> Mailto: satyavalli.rama@tcs.com
> Website: http://www.tcs.com
> ____________________________________________
> Experience certainty.	IT Services
> Business Solutions
> Consulting
> ____________________________________________
> 
> 
> -----Jan Scheurich <jan.scheurich@ericsson.com> wrote: -----
> To: Satyavalli Rama <satyavalli.rama@tcs.com>, Ben Pfaff <blp@ovn.org>
> From: Jan Scheurich <jan.scheurich@ericsson.com>
> Date: 01/08/2018 06:31PM
> Cc: SatyaValli <satyavalli.rama@gmail.com>, "dev@openvswitch.org" <dev@openvswitch.org>, "manasa.cherukupally@tcs.com" <manasa.cherukupally@tcs.com>, "p.pavani1@tcs.com" <p.pavani1@tcs.com>, Harivelam Lavanya <harivelam.lavanya@tcs.com>, "muttamsetty.surya@tcs.com" <muttamsetty.surya@tcs.com>
> Subject: RE: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
> 
> Hi Satyavalli,
>  
> Please find my responses below.
>  
> Regards, Jan
>  
> From: Satyavalli Rama [mailto:satyavalli.rama@tcs.com] 
> Sent: Friday, 05 January, 2018 12:25
> To: Ben Pfaff <blp@ovn.org>; Jan Scheurich <jan.scheurich@ericsson.com>
> Cc: SatyaValli <satyavalli.rama@gmail.com>; dev@openvswitch.org; manasa.cherukupally@tcs.com; p.pavani1@tcs.com; Harivelam Lavanya <harivelam.lavanya@tcs.com>; muttamsetty.surya@tcs.com
> Subject: Re: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
>  
> Hi Jan and Ben,
>  
> Please find the inline responses.
>  
> 
> -----Ben Pfaff <blp@ovn.org> wrote: -----
> To: Jan Scheurich <jan.scheurich@ericsson.com>
> From: Ben Pfaff <blp@ovn.org>
> Date: 01/05/2018 02:35AM
> Cc: SatyaValli <satyavalli.rama@gmail.com>, "dev@openvswitch.org" <dev@openvswitch.org>, Manasa Cherukupally <manasa.cherukupally@tcs.com>, Pavani Panthagada <p.pavani1@tcs.com>, Lavanya Harivelam <harivelam.lavanya@tcs.com>, Surya Muttamsetty <muttamsetty.surya@tcs.com>, SatyaValli <satyavalli.rama@tcs.com>
> Subject: Re: [ovs-dev] [PATCH V2] OF1.5/EXT-334 OXS/Extensible Flow Entry Statistics Support
> 
> On Wed, Jan 03, 2018 at 04:24:06PM +0000, Jan Scheurich wrote:
> > > > >
> > > > > 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
> > > >
> > > > This would break backward compatibility for one of the most frequently used OVS CLI commands. Why don't you introduce a new
> > > command such as "ovs-ofctl dump-flow-stats" for the new OXS stats?
> > > 
> > > I think you might be misinterpreting the meaning here.  It doesn't
> > > appear to break compatibility, at least not in a major way, since it
> > > doesn't do a lot of updates to the tests that would otherwise be
> > > required.
> > 
> > Perhaps I am missing the point of some of these changes. I understand that OVS needs to support the new extensible OXS flow stats syntax in OpenFlow 1.5 and the differentiated MP request/reply pairs OFPMP_FLOW_DESC (replacing the former OFPMP_FLOW) and OFPMP_FLOW_STATS (just fetching flow stats per flow w/o the rest of the flow data).
> > 
> > But I don't understand why this should have any impact on the existing CLI command "ovs-ofctl dump-flows" and its output. This tool expressly fetches and displays the complete flow dump from OVS, including match, instructions/actions and statistics. When using OF 1.5 it should transparently apply OFPMP_FLOW_DESC MP request/reply to fetch the data, up to OF 1.4 it should use the original OFPMP_FLOW.
> > 
> > I can't see any ovs-ofctl use case that would justify the use of the new OFPMP_FLOW_STATS request/reply. The removed data in the reply compared to the full flow description are mainly the instructions, the full match is still there to identify each flow. So cutting down the transferred data volume can hardly be the reason (Note, this may still be different for real OF 1.5 controllers).
> > 
> > If you believe we should have an ovs-ofctl command anyhow, e.g. for testing purposes, I suggest to introduce a new command or add an option to dump-flows to force use of this particular MP message. The output would be limited to flow match and stats in that case.
> > 
>  
> As per our understanding and from previous review comments we treated OF1.5+ has two different ways to request and get replies for Flow Stats: FLOW_DESC and FLOW_STATS (which will be even used for Flow Stats Trigger). And we've supported this with the help of two commands
>  
> OFPMP_FLOW_DESC - ovs-ofctl dump-flow-desc -O OpenFlow15
> OFPMP_FLOW_STATS - ovs-ofctl dump-flows -O OpenFlow15
>  
> [Jan] My argument still holds: &#8220;ovs-ofctl dump-flows&#8221; is the existing command to fetch and display the entire flow data from OVS. In OF 1.5+ it should use the OFPMP_FLOW_DESC primitive instead of the earlier OFPMP_FLOW used in older OF protocols.
>  
> If you want a way to exercise the new OFPMP_FLOW_STATS primitive you should introduce a new command &#8220;ovs-ofctl -Oopenflow15 dump-flow-stats&#8221; for that purpose or, alternatively, add an option to the dump-flows command to select using that primitive (and limit the output).
> 
> > The new command to dump aggregate flow stats is of course a welcome addition. It should work irrespectively of the used OpenFlow version with the OFPMP_AGGREGATE_STATS primitive using classic flow stats prior to OF 1.5 and OXS flow stats in OF 1.5.
> > 
> > All in all I am NACK-ing this patch as it stands.
> > 
> > Regards, Jan
> > 
> > BTW: I have played a bit with the patch. The existing ovs-ofctl test cases appear to not break because the changes described in the commit message and the documentation are not effective. The legacy command format "ovs-ofctl dump-flows -O OpenFlow15 <bridge> [<match>]" still produces the complete flow dump including instructions/actions:
> > 
> As OXS_OF_DURATION field is mandatory as per OF 1.5+ Spec for tracking the age of flow entry in the switch, we've kept it as a default field. The other fields OXS_OF_PACKET_COUNT and OXS_OF_BYTE_COUNT are kept as optional.
> 
> ##Flow Status Dump:
> 
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 duration
> 
>  cookie=0x0, duration=235.211s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL
> 
>  cookie=0x0, duration=229.483s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL
> 
>  cookie=0x0, duration=225.539s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL
> 
>  cookie=0x0, duration=3240.675s, table=0, n_packets=0, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL
> 
> The OXS field value reflection is based on the flow match
> 
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 packet_count
> 
>  cookie=0x0, duration=176.872s, table=0, n_packets=1102, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL
> 
>  cookie=0x0, duration=171.144s, table=0, n_packets=544, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL
> 
>  cookie=0x0, duration=167.200s, table=0, n_packets=408, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL
> 
>  cookie=0x0, duration=3182.336s, table=0, n_packets=53592657, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL
> 
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 packet_count,idle_time
>  
>  cookie=0x0, duration=143.448s, table=0, n_packets=1102, n_bytes=0, idle_age=5, hard_age=0, icmp actions=NORMAL
>  
>  cookie=0x0, duration=137.720s, table=0, n_packets=544, n_bytes=0, idle_age=5, hard_age=0, udp,tp_dst=200 actions=NORMAL
>  
>  cookie=0x0, duration=133.776s, table=0, n_packets=408, n_bytes=0, idle_age=5, hard_age=0, tcp,tp_src=100 actions=NORMAL
>  
>  cookie=0x0, duration=3148.912s, table=0, n_packets=53592655, n_bytes=0, idle_age=0, hard_age=0, priority=0 actions=NORMAL
>  
> [Jan] Irrespective of the discrepancy of behavior in my test environment, I would like to ask what you consider the purpose of adding one or more OXS types to the dump-flows (and dump-flow-desc) commands. I could understand if the client could filter the requested flow stats but, as per the OF 1.5 spec, the OFPMP_FLOW_STATS request cannot selectively fetch a subset of the total flow stats. So the switch will always report all available statistics back.
>  
> The only thing the patch currently does is to discard not explicitly listed counters (except for duration, why?) on the receiver side and print zero values instead. What is the point?
>  
> For unit tests we typically apply some output filters to suppress non-deterministic or irrelevant data of a command outputs. Is that the use case you were thinking of? There is a &#8220;--no-stats&#8221; option already in ovs-ovctl that suppresses all flow stats if the user is not interested in those.
>  
> Unless there is a real use case that I am overlooking, I think you should just remove the OXS fields option from the affected commands.  
>  
> > When specifying it in addition to some filter match it is rejected:
> > 
> > # ovs-ofctl -Oopenflow15 dump-flows br0 in_port="br0-ns1" packet_count
> > ovs-ofctl: 'dump-flows' command takes at most 2 arguments
>  
> There is a slight syntax mismatch, please find the below dumps with match field:
>  
> ##Dumps with Match Field:
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 tcp,packet_count
>  cookie=0x0, duration=549s, table=0, n_packets=42146, n_bytes=0, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL
>  
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 tcp,byte_count
>  cookie=0x0, duration=565.577s, table=0, n_packets=0, n_bytes=2275884, idle_age=0, hard_age=0, tcp,tp_src=100 actions=NORMAL
>  
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 udp,packet_count
>  cookie=0x0, duration=600.984s, table=0, n_packets=56200, n_bytes=0, idle_age=0, hard_age=0, udp,tp_dst=200 actions=NORMAL
>  
> ## ovs-ofctl dump-flows -O OpenFlow15 br0 icmp,packet_count
>  cookie=0x0, duration=614.544s, table=0, n_packets=112611, n_bytes=0, idle_age=0, hard_age=0, icmp actions=NORMAL
>  
> ## ovs-ofctl dump-flow-desc -O OpenFlow15 br0 icmp,packet_count
> OFPST_FLOW_DESC reply (OF1.5) (xid=0x4):
>  cookie=0x0, duration=625.217s, table=0, n_packets=112611, n_bytes=0, check_overlap reset_counts idle_age=0, hard_age=0, icmp actions=NORMAL
>  
> ## ovs-ofctl dump-aggregate -O OpenFlow15 br0 icmp,packet_count
> OFPST_AGGREGATE reply (OF1.5) (xid=0x4): packet_count=112611 byte_count=0 flow_count=1
>  
> [Jan] This syntax is even more confusing as it mixes match fields and OXS filters. But that problem goes away when we get rid of the OXS filter option.
>  
> =====-----=====-----=====
> Notice: The information contained in this e-mail
> message and/or attachments to it may contain 
> confidential or privileged information. If you are 
> not the intended recipient, any dissemination, use, 
> review, distribution, printing or copying of the 
> information contained in this e-mail message 
> and/or attachments to it are strictly prohibited. If 
> you have received this communication in error, 
> please notify us by reply e-mail or telephone and 
> immediately and permanently delete the message 
> and any attachments. Thank you
> 
>

Patch

diff --git a/NEWS b/NEWS
index af98c2f..ec4501c 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,15 @@  Post-v2.8.0
 v2.8.0 - 31 Aug 2017
 --------------------
    - 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 can now accept and display port 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 013a8f3..e78fe95 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 *,
                     enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
@@ -70,7 +71,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 *,
                                        enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index a9e57ed..cab7025 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -365,9 +365,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;
@@ -408,7 +419,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 {
@@ -419,7 +431,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 effe5b5..a7cd187 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -199,6 +199,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 bacbaa6..33fd81f 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,
                 enum ofputil_protocol *usable_protocols)
 {
@@ -412,10 +437,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);
@@ -604,14 +632,15 @@  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,
               enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
     char *error;
 
-    error = parse_ofp_str__(fm, command, string, port_map, usable_protocols);
+    error = parse_ofp_str__(fm, command, oxs_field_set, string, port_map,
+                            usable_protocols);
     if (error) {
         fm->ofpacts = NULL;
         fm->ofpacts_len = 0;
@@ -1079,7 +1108,7 @@  parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
                        const struct ofputil_port_map *port_map, int command,
                        enum ofputil_protocol *usable_protocols)
 {
-    char *error = parse_ofp_str(fm, command, string, port_map,
+    char *error = parse_ofp_str(fm, command, NULL, string, port_map,
                                 usable_protocols);
 
     if (!error) {
@@ -1297,27 +1326,42 @@  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,
                                  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, usable_protocols);
+    error = parse_ofp_str(&fm, -1, &oxs_field_set, string, port_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 151d618..e54bb19 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -3613,6 +3613,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);
         ofp_print_flow_stats_request(string, oh, port_map);
         break;
@@ -3637,6 +3638,7 @@  ofp_to_string__(const struct ofp_header *oh,
         break;
 
     case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_FLOW_DESC_REPLY:
         ofp_print_stats(string, oh);
         ofp_print_flow_stats_reply(string, oh, port_map);
         break;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 47f30c7..07769db 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -51,9 +51,11 @@ 
 #include "unaligned.h"
 #include "util.h"
 #include "uuid.h"
+#include "ox-stat.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_util);
 
+
 /* Rate limit for OpenFlow message parse errors.  These always indicate a bug
  * in the peer and so there's not much point in showing a lot of them. */
 static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -2335,6 +2337,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,
@@ -2784,6 +2823,17 @@  ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
         return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
                                                    vl_mff_map);
 
+    case OFPRAW_OFPST15_FLOW_REQUEST:
+        return ofputil_decode_ofpst15_flow_request(fsr, &b, false, false,
+                                                   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);
@@ -2807,14 +2857,38 @@  ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
 {
     struct ofpbuf *msg;
     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
@@ -2914,6 +2988,101 @@  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;
+        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;
@@ -3075,7 +3244,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);
@@ -3088,7 +3258,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);
@@ -3181,7 +3386,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;
@@ -3190,6 +3395,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);
@@ -3203,6 +3414,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;
 }
@@ -3212,12 +3424,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;
 }
@@ -3231,7 +3453,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;
 
@@ -3314,12 +3554,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,
@@ -10442,6 +10702,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:
@@ -10470,6 +10731,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 84eb18e..3a283e3 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -4357,7 +4357,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);
@@ -4488,7 +4489,6 @@  handle_aggregate_stats_request(struct ofconn *ofconn,
     if (error) {
         return error;
     }
-
     memset(&stats, 0, sizeof stats);
     unknown_packets = unknown_bytes = false;
 
@@ -4525,7 +4525,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;
@@ -8119,6 +8120,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:
@@ -8196,6 +8198,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 17682aa..81b607b 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 "\
@@ -1383,6 +1399,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 "\
@@ -1462,6 +1494,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 "\
@@ -1498,6 +1558,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 "\
@@ -1531,6 +1605,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 9a51a13..b545cf6 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7107,6 +7107,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 54 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 54
+
+    send_packet
+    warp_and_dump_OF $1 2 2 108
+    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 108
+
+    send_packet
+    warp_and_dump_OF $1 4 3 162
+    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 54 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 54 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow])
+    warp_and_dump_OF $1 5 1 54 reset_counts
+
+    # Add flow having reset_counts flag without reset_counts
+    send_packet
+    warp_and_dump_OF $1 6 2 108 reset_counts
+    AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow])
+    warp_and_dump_OF $1 1 2 108
+
+    # Modify flow w/o reset_counts flag with a flow_mod having reset_counts
+    send_packet
+    warp_and_dump_OF $1 2 3 162
+    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 74a30cb..8094c44 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1417,8 +1417,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 5cdf48c..cc04f74 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -212,11 +212,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
@@ -229,12 +231,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
@@ -2337,6 +2351,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 953184d..f23bb42 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -620,10 +620,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;
@@ -634,7 +632,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)), verbosity + 1);
 
@@ -1258,7 +1255,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)
 {
@@ -1270,7 +1267,7 @@  prepare_dump_flows(int argc, char *argv[], bool aggregate,
     const char *match = argc > 2 ? argv[2] : "";
     const struct ofputil_port_map *port_map
         = *match ? ports_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, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
@@ -1282,13 +1279,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);
 }
@@ -1359,18 +1356,25 @@  compare_flows(const void *afs_, const void *bfs_)
     return 0;
 }
 
+
+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_ports() && 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;
@@ -1401,7 +1405,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
@@ -3436,8 +3440,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,
-                              &usable);
+        error = parse_ofp_str(&fm, OFPFC_ADD, NULL, ds_cstr(&s),
+                              state->port_map, &usable);
         if (error) {
             ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
         }
@@ -4284,7 +4288,7 @@  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, &usable_protocols);
+    error_s = parse_ofp_str(&fm, -1, NULL, string_s, NULL, &usable_protocols);
     if (error_s) {
         ovs_fatal(0, "%s", error_s);
     }
@@ -4507,6 +4511,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",