diff mbox series

[ovs-dev,v1,2/2] ofp-monitor: Support flow monitoring for OpenFlow 1.3, 1.4+

Message ID 20210730023620.1942-1-vdasari@gmail.com
State Superseded
Headers show
Series None | expand

Commit Message

Vasu Dasari July 30, 2021, 2:36 a.m. UTC
Extended OpenFlow monitoring support
* OpenFlow 1.3 with ONF extensions
* OpenFlow 1.4+ as defined in OpenFlow specification 1.4+.

ONF extensions are similar to Nicira extensions except for onf_flow_monitor_request{}
where out_port is defined as 32-bit number OF(1.1) number, oxm match formats are
used in update and request messages.

Flow monitoring support in 1.4+ is slightly different from Nicira and ONF
extensions.
 * More flow monitoring flags are defined.
 * Monitor add/modify/delete command is intruduced in flow_monitor
   request message.
 * Addition of out_group as part of flow_monitor request message

Description of changes:
1. Generate ofp-msgs.inc to be able to support 1.3, 1.4+ flow Monitoring messages.
    include/openvswitch/ofp-msgs.h

2. Modify openflow header files with protocol specific headers.
    include/openflow/openflow-1.3.h
    include/openflow/openflow-1.4.h

3. Modify OvS abstraction of openflow headers. ofp-monitor.h leverages  enums
   from on nicira extensions for creating protocol abstraction headers. OF(1.4+)
   enums are superset of nicira extensions.
    include/openvswitch/ofp-monitor.h

4. Changes to these files reflect encoding and decoding of new protocol messages.
    lib/ofp-monitor.c

5. Changes to mmodules using ofp-monitor APIs. Most of the changes here are to
   migrate enums from nicira to OF 1.4+ versions.
    ofproto/connmgr.c
    ofproto/connmgr.h
    ofproto/ofproto-provider.h
    ofproto/ofproto.c

6. Extended protocol decoding tests to verify all protocol versions
        FLOW_MONITOR_CANCEL
        FLOW_MONITOR_PAUSED
        FLOW_MONITOR_RESUMED
        FLOW_MONITOR request
        FLOW_MONITOR reply
    tests/ofp-print.at

7. Modify flow monitoring tests to be able executed by all protocol versions.
    tests/ofproto.at

7. Modified documentation highlighting the change
    utilities/ovs-ofctl.8.in
    NEWS

Signed-off-by: Vasu Dasari <vdasari@gmail.com>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2021-June/383915.html
---
v1:
 - Fixed 0-day Robot errors

---
 NEWS                              |   6 +-
 include/openflow/openflow-1.3.h   |  89 ++++
 include/openflow/openflow-1.4.h   |  93 +++-
 include/openvswitch/ofp-monitor.h |   9 +-
 include/openvswitch/ofp-msgs.h    |  39 +-
 lib/ofp-monitor.c                 | 844 ++++++++++++++++++++++++------
 lib/ofp-print.c                   |  24 +-
 ofproto/connmgr.c                 |  47 +-
 ofproto/connmgr.h                 |   6 +-
 ofproto/ofproto-provider.h        |   4 +-
 ofproto/ofproto.c                 |  89 +++-
 tests/ofp-print.at                | 122 ++++-
 tests/ofproto.at                  | 176 +++++--
 utilities/ovs-ofctl.8.in          |   3 +
 utilities/ovs-ofctl.c             |   6 +
 15 files changed, 1265 insertions(+), 292 deletions(-)

Comments

Vasu Dasari Aug. 24, 2021, 4:29 p.m. UTC | #1
Hi Ben/Ashish,

When you get a chance, Can you please take a look at my code?

-Vasu

*Vasu Dasari*


On Thu, Jul 29, 2021 at 10:36 PM Vasu Dasari <vdasari@gmail.com> wrote:

> Extended OpenFlow monitoring support
> * OpenFlow 1.3 with ONF extensions
> * OpenFlow 1.4+ as defined in OpenFlow specification 1.4+.
>
> ONF extensions are similar to Nicira extensions except for
> onf_flow_monitor_request{}
> where out_port is defined as 32-bit number OF(1.1) number, oxm match
> formats are
> used in update and request messages.
>
> Flow monitoring support in 1.4+ is slightly different from Nicira and ONF
> extensions.
>  * More flow monitoring flags are defined.
>  * Monitor add/modify/delete command is intruduced in flow_monitor
>    request message.
>  * Addition of out_group as part of flow_monitor request message
>
> Description of changes:
> 1. Generate ofp-msgs.inc to be able to support 1.3, 1.4+ flow Monitoring
> messages.
>     include/openvswitch/ofp-msgs.h
>
> 2. Modify openflow header files with protocol specific headers.
>     include/openflow/openflow-1.3.h
>     include/openflow/openflow-1.4.h
>
> 3. Modify OvS abstraction of openflow headers. ofp-monitor.h leverages
> enums
>    from on nicira extensions for creating protocol abstraction headers.
> OF(1.4+)
>    enums are superset of nicira extensions.
>     include/openvswitch/ofp-monitor.h
>
> 4. Changes to these files reflect encoding and decoding of new protocol
> messages.
>     lib/ofp-monitor.c
>
> 5. Changes to mmodules using ofp-monitor APIs. Most of the changes here
> are to
>    migrate enums from nicira to OF 1.4+ versions.
>     ofproto/connmgr.c
>     ofproto/connmgr.h
>     ofproto/ofproto-provider.h
>     ofproto/ofproto.c
>
> 6. Extended protocol decoding tests to verify all protocol versions
>         FLOW_MONITOR_CANCEL
>         FLOW_MONITOR_PAUSED
>         FLOW_MONITOR_RESUMED
>         FLOW_MONITOR request
>         FLOW_MONITOR reply
>     tests/ofp-print.at
>
> 7. Modify flow monitoring tests to be able executed by all protocol
> versions.
>     tests/ofproto.at
>
> 7. Modified documentation highlighting the change
>     utilities/ovs-ofctl.8.in
>     NEWS
>
> Signed-off-by: Vasu Dasari <vdasari@gmail.com>
> Reported-at:
> https://mail.openvswitch.org/pipermail/ovs-dev/2021-June/383915.html
> ---
> v1:
>  - Fixed 0-day Robot errors
>
> ---
>  NEWS                              |   6 +-
>  include/openflow/openflow-1.3.h   |  89 ++++
>  include/openflow/openflow-1.4.h   |  93 +++-
>  include/openvswitch/ofp-monitor.h |   9 +-
>  include/openvswitch/ofp-msgs.h    |  39 +-
>  lib/ofp-monitor.c                 | 844 ++++++++++++++++++++++++------
>  lib/ofp-print.c                   |  24 +-
>  ofproto/connmgr.c                 |  47 +-
>  ofproto/connmgr.h                 |   6 +-
>  ofproto/ofproto-provider.h        |   4 +-
>  ofproto/ofproto.c                 |  89 +++-
>  tests/ofp-print.at                | 122 ++++-
>  tests/ofproto.at                  | 176 +++++--
>  utilities/ovs-ofctl.8.in          |   3 +
>  utilities/ovs-ofctl.c             |   6 +
>  15 files changed, 1265 insertions(+), 292 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 02884b774..47ad9de2a 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -25,8 +25,10 @@ v2.16.0 - xx xxx xxxx
>     - In ovs-vsctl and vtep-ctl, the "find" command now accept new
>       operators {in} and {not-in}.
>     - OpenFlow:
> -     * Extend Flow Monitoring support for OpenFlow 1.0-1.2 with Nicira
> -       Extensions
> +     * Extended Flow Monitoring support for all supported OpenFlow
> versions
> +         OpenFlow versions 1.0-1.2 with Nicira Extensions
> +         OpenFlow versions 1.3 with Open Network Foundation extension
> +         OpenFlow versions 1.4+, as defined in the OpenFlow specification
>     - Userspace datapath:
>       * Auto load balancing of PMDs now partially supports cross-NUMA
> polling
>         cases, e.g if all PMD threads are running on the same NUMA node.
> diff --git a/include/openflow/openflow-1.3.h
> b/include/openflow/openflow-1.3.h
> index c48a8ea7f..1a818dbb4 100644
> --- a/include/openflow/openflow-1.3.h
> +++ b/include/openflow/openflow-1.3.h
> @@ -374,4 +374,93 @@ struct ofp13_async_config {
>  };
>  OFP_ASSERT(sizeof(struct ofp13_async_config) == 24);
>
> +struct onf_flow_monitor_request {
> +    ovs_be32   id;            /* Controller-assigned ID for this monitor.
> */
> +    ovs_be16   flags;         /* ONFFMF_*. */
> +    ovs_be16   match_len;     /* Length of oxm_fields. */
> +    ovs_be32   out_port;      /* Required output port, if not OFPP_NONE.
> */
> +    uint8_t    table_id;      /* One table’s ID or 0xff for all tables. */
> +    uint8_t    zeros[3];      /* Align to 64 bits (must be zero). */
> +    /* Followed by an ofp11_match structure. */
> +};
> +OFP_ASSERT(sizeof(struct onf_flow_monitor_request) == 16);
> +
> +/* Header for experimenter requests and replies. */
> +struct onf_experimenter_header {
> +    struct ofp_header header;
> +    ovs_be32   vendor;        /* ONF_EXPERIMENTER_ID. */
> +    ovs_be32   subtype;       /* One of ONFT_*. */
> +};
> +OFP_ASSERT(sizeof(struct onf_experimenter_header) == 16);
> +
> +enum onf_flow_monitor_msg_type {
> +    ONFT_FLOW_MONITOR_CANCEL = 1870,
> +    ONFT_FLOW_MONITOR_PAUSED = 1871,
> +    ONFT_FLOW_MONITOR_RESUMED = 1872
> +};
> +
> +/* ’flags’ bits in struct onf_flow_monitor_request. */
> +enum onf_flow_monitor_flags {
> +    /* When to send updates. */
> +    ONFFMF_INITIAL = 1 << 0,  /* Initially matching flows. */
> +    ONFFMF_ADD = 1 << 1,      /* New matching flows as they are added. */
> +    ONFFMF_DELETE = 1 << 2,   /* Old matching flows as they are removed.
> */
> +    ONFFMF_MODIFY = 1 << 3,   /* Matching flows as they are changed. */
> +
> +    /* What to include in updates. */
> +    ONFFMF_ACTIONS = 1 << 4,  /* If set, actions are included. */
> +    ONFFMF_OWN = 1 << 5,      /* If set, include own changes in full. */
> +};
> +
> +/* ONFST_FLOW_MONITOR reply header. */
> +struct onf_flow_update_header {
> +    ovs_be16   length;        /* Length of this entry. */
> +    ovs_be16   event;         /* One of ONFFME_*. */
> +    /* ...other data depending on ’event’... */
> +};
> +OFP_ASSERT(sizeof(struct onf_flow_update_header) == 4);
> +
> +/* ’event’ values in struct onf_flow_update_header. */
> +enum onf_flow_update_event {
> +    /* struct onf_flow_update_full. */
> +    ONFFME_ADDED = 0,         /* Flow was added. */
> +    ONFFME_DELETED = 1,       /* Flow was deleted. */
> +    ONFFME_MODIFIED = 2,      /* Flow (generally its actions) was
> changed. */
> +
> +    /* struct onf_flow_update_abbrev. */
> +    ONFFME_ABBREV = 3,        /* Abbreviated reply. */
> +};
> +
> +/* ONFST_FLOW_MONITOR reply for ONFFME_ADDED, ONFFME_DELETED, and
> +* ONFFME_MODIFIED. */
> +struct onf_flow_update_full {
> +    ovs_be16   length;        /* Length is 24. */
> +    ovs_be16   event;         /* One of ONFFME_*. */
> +    ovs_be16   reason;        /* OFPRR_* for ONFFME_DELETED, else zero. */
> +    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   match_len;     /* Length of oxm_fields. */
> +    uint8_t    table_id;      /* ID of flow’s table. */
> +    uint8_t    pad;           /* Reserved, currently zeroed. */
> +    ovs_be64   cookie;        /* Opaque controller-issued identifier. */
> +    /* Followed by:
> +     *   - Exactly match_len (possibly 0) bytes containing the
> oxm_fields, then
> +     *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7)
> bytes of
> +     *     all-zero bytes, then
> +     *   - Instructions to fill out the remainder ’length’ bytes (always a
> +     *     multiple of 8). If ONFFMF_ACTIONS was not specified, or
> ’event’ is
> +     *     ONFFME_DELETED, no actions are included.
> +     */
> +};
> +OFP_ASSERT(sizeof(struct onf_flow_update_full) == 24);
> +
> +/* ONFST_FLOW_MONITOR reply for ONFFME_ABBREV. */
> +struct onf_flow_update_abbrev {
> +    ovs_be16   length;     /* Length is 8. */
> +    ovs_be16   event;      /* ONFFME_ABBREV. */
> +    ovs_be32   xid;        /* Controller-specified xid from flow_mod. */
> +};
> +OFP_ASSERT(sizeof(struct onf_flow_update_abbrev) == 8);
> +
>  #endif /* openflow/openflow-1.3.h */
> diff --git a/include/openflow/openflow-1.4.h
> b/include/openflow/openflow-1.4.h
> index be191180b..8e6a163fd 100644
> --- a/include/openflow/openflow-1.4.h
> +++ b/include/openflow/openflow-1.4.h
> @@ -358,27 +358,100 @@ OFP_ASSERT(sizeof(struct
> ofp14_flow_monitor_request) == 16);
>
>  /* Flow monitor commands */
>  enum ofp14_flow_monitor_command {
> -    OFPFMC14_ADD = 0, /* New flow monitor. */
> -    OFPFMC14_MODIFY = 1, /* Modify existing flow monitor. */
> -    OFPFMC14_DELETE = 2, /* Delete/cancel existing flow monitor. */
> +    OFPFMC_ADD = 0, /* New flow monitor. */
> +    OFPFMC_MODIFY = 1, /* Modify existing flow monitor. */
> +    OFPFMC_DELETE = 2, /* Delete/cancel existing flow monitor. */
>  };
>
>  /* 'flags' bits in struct of_flow_monitor_request. */
>  enum ofp14_flow_monitor_flags {
>      /* When to send updates. */
>      /* Common to NX and OpenFlow 1.4 */
> -    OFPFMF14_INITIAL = 1 << 0,     /* Initially matching flows. */
> -    OFPFMF14_ADD = 1 << 1,         /* New matching flows as they are
> added. */
> -    OFPFMF14_REMOVED = 1 << 2,     /* Old matching flows as they are
> removed. */
> -    OFPFMF14_MODIFY = 1 << 3,      /* Matching flows as they are changed.
> */
> +    OFPFMF_INITIAL = 1 << 0,     /* Initially matching flows. */
> +    OFPFMF_ADD = 1 << 1,         /* New matching flows as they are added.
> */
> +    OFPFMF_REMOVED = 1 << 2,     /* Old matching flows as they are
> removed. */
> +    OFPFMF_MODIFY = 1 << 3,      /* Matching flows as they are changed. */
>
>      /* What to include in updates. */
>      /* Common to NX and OpenFlow 1.4 */
> -    OFPFMF14_INSTRUCTIONS = 1 << 4, /* If set, instructions are included.
> */
> -    OFPFMF14_NO_ABBREV = 1 << 5,    /* If set, include own changes in
> full. */
> +    OFPFMF_INSTRUCTIONS = 1 << 4, /* If set, instructions are included. */
> +    OFPFMF_NO_ABBREV = 1 << 5,    /* If set, include own changes in full.
> */
>      /* OpenFlow 1.4 */
> -    OFPFMF14_ONLY_OWN = 1 << 6,     /* If set, don't include other
> controllers.
> +    OFPFMF_ONLY_OWN = 1 << 6,     /* If set, don't include other
> controllers.
>                                       */
>  };
>
> +/* OFPMP_FLOW_MONITOR reply header.
> + *
> + * The body of an OFPMP_FLOW_MONITOR reply is an array of variable-length
> + * structures, each of which begins with this header.  The ’length’
> member may
> + * be used to traverse the array, and the ’event’ member may be used to
> + * determine the particular structure.
> + * Every instance is a multiple of 8 bytes long. */
> +struct ofp_flow_update_header {
> +    ovs_be16 length;                /* Length of this entry. */
> +    ovs_be16 event;                 /* One of OFPFME_*. */
> +    /* ...other data depending on ’event’... */
> +};
> +OFP_ASSERT(sizeof(struct ofp_flow_update_header) == 4);
> +
> +/* ’event’ values in struct ofp_flow_update_header. */
> +enum ofp_flow_update_event {
> +    /* struct ofp_flow_update_full. */
> +    OFPFME_INITIAL = 0,          /* Flow present when flow monitor
> created. */
> +    OFPFME_ADDED = 1,            /* Flow was added. */
> +    OFPFME_REMOVED = 2,          /* Flow was removed. */
> +    OFPFME_MODIFIED = 3,         /* Flow instructions were changed. */
> +
> +    /* struct ofp_flow_update_abbrev. */
> +    OFPFME_ABBREV = 4,           /* Abbreviated reply. */
> +
> +    /* struct ofp_flow_update_header. */
> +    OFPFME_PAUSED = 5,           /* Monitoring paused (out of buffer
> space). */
> +    OFPFME_RESUMED = 6,          /* Monitoring resumed. */
> +};
> +
> +/* OFPMP_FLOW_MONITOR reply for OFPFME_INITIAL, OFPFME_ADDED,
> OFPFME_REMOVED,
> + * and OFPFME_MODIFIED. */
> +struct ofp_flow_update_full {
> +    ovs_be16 length;           /* Length is 32 + match + instructions. */
> +    ovs_be16 event;            /* One of OFPFME_*. */
> +    uint8_t table_id;          /* ID of flow’s table. */
> +    uint8_t reason;            /* OFPRR_* for OFPFME_REMOVED, else zero.
> */
> +    ovs_be16 idle_timeout;     /* Number of seconds idle before
> expiration. */
> +    ovs_be16 hard_timeout;     /* Number of seconds before expiration. */
> +    ovs_be16 priority;         /* Priority of the entry. */
> +    uint8_t zeros[4];          /* Reserved, currently zeroed. */
> +    ovs_be64 cookie;           /* Opaque controller-issued identifier. */
> +    /* Instruction set.
> +     *      If OFPFMF_INSTRUCTIONS was not specified, or ’event’ is
> +     *     OFPFME_REMOVED, no instructions are included.
> +     */
> +};
> +OFP_ASSERT(sizeof(struct ofp_flow_update_full) == 24);
> +
> +/* OFPMP_FLOW_MONITOR reply for OFPFME_ABBREV.
> + *
> + * When the controller does not specify OFPFMF_NO_ABBREV in a monitor
> request,
> + * any flow tables changes due to the controller’s own requests (on the
> same
> + * OpenFlow channel) will be abbreviated, when possible, to this form,
> which
> + * simply specifies the ’xid’ of the OpenFlow request (e.g. an
> OFPT_FLOW_MOD)
> + * that caused the change.
> + * Some changes cannot be abbreviated and will be sent in full.
> + */
> +struct ofp_flow_update_abbrev {
> +    ovs_be16 length;     /* Length is 8. */
> +    ovs_be16 event;      /* OFPFME_ABBREV. */
> +    ovs_be32 xid;        /* Controller-specified xid from flow_mod. */
> +};
> +OFP_ASSERT(sizeof(struct ofp_flow_update_abbrev) == 8);
> +
> +/* OFPMP_FLOW_MONITOR reply for OFPFME_PAUSED and OFPFME_RESUMED.*/
> +struct ofp_flow_update_paused {
> +    ovs_be16 length;      /* Length is 8. */
> +    ovs_be16 event;       /* One of OFPFME_*. */
> +    uint8_t zeros[4];     /* Reserved, currently zeroed. */
> +};
> +OFP_ASSERT(sizeof(struct ofp_flow_update_paused) == 8);
> +
>  #endif /* openflow/openflow-1.4.h */
> diff --git a/include/openvswitch/ofp-monitor.h
> b/include/openvswitch/ofp-monitor.h
> index 835efd0f3..7c7cfcff4 100644
> --- a/include/openvswitch/ofp-monitor.h
> +++ b/include/openvswitch/ofp-monitor.h
> @@ -61,8 +61,10 @@ void ofputil_flow_removed_format(struct ds *,
>  /* Abstract nx_flow_monitor_request. */
>  struct ofputil_flow_monitor_request {
>      uint32_t id;
> -    enum nx_flow_monitor_flags flags;
> +    enum ofp14_flow_monitor_command command;
> +    enum ofp14_flow_monitor_flags flags;
>      ofp_port_t out_port;
> +    uint32_t out_group;
>      uint8_t table_id;
>      struct match match;
>  };
> @@ -85,7 +87,7 @@ char *parse_flow_monitor_request(struct
> ofputil_flow_monitor_request *,
>
>  /* Abstract nx_flow_update. */
>  struct ofputil_flow_update {
> -    enum nx_flow_update_event event;
> +    enum ofp_flow_update_event event;
>
>      /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */
>      enum ofp_flow_removed_reason reason;
> @@ -119,6 +121,9 @@ uint32_t ofputil_decode_flow_monitor_cancel(const
> struct ofp_header *);
>  struct ofpbuf *ofputil_encode_flow_monitor_cancel(
>      uint32_t id, enum ofputil_protocol protocol);
>
> +struct ofpbuf * ofputil_encode_flow_monitor_pause(
> +    enum ofp_flow_update_event command, enum ofputil_protocol protocol);
> +
>  struct ofputil_requestforward {
>      ovs_be32 xid;
>      /* Also used for OF 1.0-1.3 when using Nicira Extension: */
> diff --git a/include/openvswitch/ofp-msgs.h
> b/include/openvswitch/ofp-msgs.h
> index c5fde0270..921a937e5 100644
> --- a/include/openvswitch/ofp-msgs.h
> +++ b/include/openvswitch/ofp-msgs.h
> @@ -453,14 +453,33 @@ enum ofpraw {
>
>      /* OFPST 1.4+ (16): uint8_t[8][]. */
>      OFPRAW_OFPST14_FLOW_MONITOR_REQUEST,
> +    /* ONFST 1.3 (1870): uint8_t[8][]. */
> +    OFPRAW_ONFST13_FLOW_MONITOR_REQUEST,
>      /* NXST 1.0-1.2 (2): uint8_t[8][]. */
>      OFPRAW_NXST_FLOW_MONITOR_REQUEST,
>
>      /* OFPST 1.4+ (16): uint8_t[8][]. */
>      OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
> +    /* ONFST 1.3 (1870): uint8_t[8][]. */
> +    OFPRAW_ONFST13_FLOW_MONITOR_REPLY,
>      /* NXST 1.0-1.2 (2): uint8_t[8][]. */
>      OFPRAW_NXST_FLOW_MONITOR_REPLY,
>
> +    /* ONFT 1.3 (1870): struct nx_flow_monitor_cancel. */
> +    OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
> +    /* NXT 1.0-1.2 (21): struct nx_flow_monitor_cancel. */
> +    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
> +
> +    /* ONFT 1.3 (1871): void. */
> +    OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
> +    /* NXT 1.0-1.2 (22): void. */
> +    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
> +
> +    /* ONFT 1.3 (1872): void. */
> +    OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
> +    /* NXT 1.0-1.2 (23): void. */
> +    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
> +
>  /* Nicira extension messages.
>   *
>   * Nicira extensions that correspond to standard OpenFlow messages are
> listed
> @@ -481,15 +500,6 @@ enum ofpraw {
>      /* NXT 1.0+ (20): struct nx_controller_id. */
>      OFPRAW_NXT_SET_CONTROLLER_ID,
>
> -    /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */
> -    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
> -
> -    /* NXT 1.0+ (22): void. */
> -    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
> -
> -    /* NXT 1.0+ (23): void. */
> -    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
> -
>      /* NXT 1.0+ (24): struct nx_tlv_table_mod, struct nx_tlv_map[]. */
>      OFPRAW_NXT_TLV_TABLE_MOD,
>
> @@ -741,8 +751,10 @@ enum ofptype {
>                                        * OFPRAW_OFPST14_PORT_DESC_REPLY. */
>
>      OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /*
> OFPRAW_OFPST14_FLOW_MONITOR_REQUEST.
> +                                         *
> OFPRAW_ONFST13_FLOW_MONITOR_REQUEST.
>                                           *
> OFPRAW_NXST_FLOW_MONITOR_REQUEST. */
>      OFPTYPE_FLOW_MONITOR_STATS_REPLY,   /*
> OFPRAW_OFPST14_FLOW_MONITOR_REPLY.
> +                                         *
> OFPRAW_ONFST13_FLOW_MONITOR_REPLY.
>                                           *
> OFPRAW_NXST_FLOW_MONITOR_REPLY. */
>
>      /* Nicira extensions. */
> @@ -762,9 +774,12 @@ enum ofptype {
>      OFPTYPE_CT_FLUSH_ZONE,            /* OFPRAW_NXT_CT_FLUSH_ZONE. */
>
>      /* Flow monitor extension. */
> -    OFPTYPE_FLOW_MONITOR_CANCEL,        /*
> OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
> -    OFPTYPE_FLOW_MONITOR_PAUSED,        /*
> OFPRAW_NXT_FLOW_MONITOR_PAUSED. */
> -    OFPTYPE_FLOW_MONITOR_RESUMED,       /*
> OFPRAW_NXT_FLOW_MONITOR_RESUMED. */
> +    OFPTYPE_FLOW_MONITOR_CANCEL,  /* OFPRAW_NXT_FLOW_MONITOR_CANCEL.
> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_CANCEL. */
> +    OFPTYPE_FLOW_MONITOR_PAUSED,  /* OFPRAW_NXT_FLOW_MONITOR_PAUSED.
> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_PAUSED. */
> +    OFPTYPE_FLOW_MONITOR_RESUMED, /* OFPRAW_NXT_FLOW_MONITOR_RESUMED.
> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_RESUMED */
>  };
>
>  /* Decoding messages into OFPTYPE_* values. */
> diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
> index 51f01b100..1756c9e8e 100644
> --- a/lib/ofp-monitor.c
> +++ b/lib/ofp-monitor.c
> @@ -328,6 +328,98 @@ ofputil_flow_removed_format(struct ds *s,
>      ds_put_format(s, " pkts%"PRIu64" bytes%"PRIu64"\n",
>                    fr->packet_count, fr->byte_count);
>  }
> +
> +static uint16_t
> +nx_to_ofp_flow_monitor_flags(uint16_t flags)
> +{
> +    uint16_t oxm_flags = 0;
> +
> +    if (flags & NXFMF_INITIAL) {
> +        oxm_flags |= OFPFMF_INITIAL;
> +    }
> +    if (flags & NXFMF_ADD) {
> +        oxm_flags |= OFPFMF_ADD;
> +    }
> +    if (flags & NXFMF_DELETE) {
> +        oxm_flags |= OFPFMF_REMOVED;
> +    }
> +    if (flags & NXFMF_MODIFY) {
> +        oxm_flags |= OFPFMF_MODIFY;
> +    }
> +    if (flags & NXFMF_ACTIONS) {
> +        oxm_flags |= OFPFMF_INSTRUCTIONS;
> +    }
> +    if (flags & NXFMF_OWN) {
> +        oxm_flags |= OFPFMF_ONLY_OWN;
> +    }
> +
> +    return oxm_flags;
> +}
> +
> +static uint16_t
> +ofp_to_nx_flow_monitor_flags(uint16_t flags)
> +{
> +    uint16_t nx_flags = 0;
> +
> +    if (flags & OFPFMF_INITIAL) {
> +        nx_flags |= NXFMF_INITIAL;
> +    }
> +    if (flags & OFPFMF_ADD) {
> +        nx_flags |= NXFMF_ADD;
> +    }
> +    if (flags & OFPFMF_REMOVED) {
> +        nx_flags |= NXFMF_DELETE;
> +    }
> +    if (flags & OFPFMF_MODIFY) {
> +        nx_flags |= NXFMF_MODIFY;
> +    }
> +    if (flags & OFPFMF_INSTRUCTIONS) {
> +        nx_flags |= NXFMF_ACTIONS;
> +    }
> +    if (flags & OFPFMF_ONLY_OWN) {
> +        nx_flags |= NXFMF_OWN;
> +    }
> +
> +    return nx_flags;
> +}
> +
> +static enum ofp_flow_update_event
> +nx_to_ofp_flow_update_event(enum nx_flow_update_event event)
> +{
> +    switch (event) {
> +    case NXFME_ADDED:
> +        return OFPFME_ADDED;
> +    case NXFME_DELETED:
> +        return OFPFME_REMOVED;
> +    case NXFME_MODIFIED:
> +        return OFPFME_MODIFIED;
> +    case NXFME_ABBREV:
> +        return OFPFME_ABBREV;
> +     default:
> +        OVS_NOT_REACHED();
> +    }
> +}
> +
> +static enum nx_flow_update_event
> +ofp_to_nx_flow_update_event(enum ofp_flow_update_event event)
> +{
> +    switch (event) {
> +    case OFPFME_INITIAL:
> +    case OFPFME_ADDED:
> +        return NXFME_ADDED;
> +    case OFPFME_REMOVED:
> +        return NXFME_DELETED;
> +    case OFPFME_MODIFIED:
> +        return NXFME_MODIFIED;
> +    case OFPFME_ABBREV:
> +        return NXFME_ABBREV;
> +    default:
> +    case OFPFME_PAUSED:
> +    case OFPFME_RESUMED:
> +        OVS_NOT_REACHED();
> +    }
> +}
> +
>
>  /* ofputil_flow_monitor_request */
>
> @@ -345,43 +437,129 @@ int
>  ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request
> *rq,
>                                      struct ofpbuf *msg)
>  {
> -    struct nx_flow_monitor_request *nfmr;
>      uint16_t flags;
> +    enum ofperr error;
> +    enum ofpraw raw;
>
> -    if (!msg->header) {
> -        ofpraw_pull_assert(msg);
> +    error = (msg->header ? ofpraw_decode(&raw, msg->header)
> +             : ofpraw_pull(&raw, msg));
> +    if (error) {
> +        return error;
>      }
>
>      if (!msg->size) {
>          return EOF;
>      }
>
> -    nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
> -    if (!nfmr) {
> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
> -                     "leftover bytes at end", msg->size);
> -        return OFPERR_OFPBRC_BAD_LEN;
> -    }
> +    switch ((int) raw) {
> +    case OFPRAW_NXST_FLOW_MONITOR_REQUEST: {
> +        struct nx_flow_monitor_request *nfmr;
>
> -    flags = ntohs(nfmr->flags);
> -    if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
> -        || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
> -                     | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
> flags);
> -        return OFPERR_OFPMOFC_BAD_FLAGS;
> +        nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
> +        if (!nfmr) {
> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
> +                    "leftover bytes at end", msg->size);
> +            return OFPERR_OFPBRC_BAD_LEN;
> +        }
> +
> +        flags = ntohs(nfmr->flags);
> +        if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
> +            || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
> +                         | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
> +                         flags);
> +            return OFPERR_OFPMOFC_BAD_FLAGS;
> +        }
> +
> +        if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
> +            return OFPERR_NXBRC_MUST_BE_ZERO;
> +        }
> +
> +        rq->id = ntohl(nfmr->id);
> +        rq->command = OFPFMC_ADD;
> +        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
> +        rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
> +        rq->table_id = nfmr->table_id;
> +        rq->out_group = OFPG_ANY;
> +
> +        return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match,
> NULL,
> +                NULL, false, NULL, NULL);
>      }
> +    case OFPRAW_ONFST13_FLOW_MONITOR_REQUEST: {
> +        struct onf_flow_monitor_request *ofmr;
> +
> +        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
> +        if (!ofmr) {
> +            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR request has %"PRIu32" "
> +                         "leftover bytes at end", msg->size);
> +            return OFPERR_OFPBRC_BAD_LEN;
> +        }
> +
> +        flags = ntohs(ofmr->flags);
> +        if (!(flags & (ONFFMF_ADD | ONFFMF_DELETE | ONFFMF_MODIFY))
> +            || flags & ~(ONFFMF_INITIAL | ONFFMF_ADD | ONFFMF_DELETE
> +                         | ONFFMF_MODIFY | ONFFMF_ACTIONS | ONFFMF_OWN)) {
> +            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR has bad flags %#"PRIx16,
> +                         flags);
> +            return OFPERR_OFPMOFC_BAD_FLAGS;
> +        }
> +
> +        if (!is_all_zeros(ofmr->zeros, sizeof ofmr->zeros)) {
> +            return OFPERR_NXBRC_MUST_BE_ZERO;
> +        }
> +
> +        rq->id = ntohl(ofmr->id);
> +        rq->command = OFPFMC_ADD;
> +        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
> +        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
> +        if (error) {
> +            return error;
> +        }
> +        rq->table_id = ofmr->table_id;
> +        rq->out_group = OFPG_ANY;
>
> -    if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
> -        return OFPERR_NXBRC_MUST_BE_ZERO;
> +        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
> NULL);
>      }
> +    case OFPRAW_OFPST14_FLOW_MONITOR_REQUEST: {
> +        struct ofp14_flow_monitor_request *ofmr;
> +
> +        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
> +        if (!ofmr) {
> +            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR request has %"PRIu32" "
> +                    "leftover bytes at end", msg->size);
> +            return OFPERR_OFPBRC_BAD_LEN;
> +        }
>
> -    rq->id = ntohl(nfmr->id);
> -    rq->flags = flags;
> -    rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
> -    rq->table_id = nfmr->table_id;
> +        flags = ntohs(ofmr->flags);
> +        rq->id = ntohl(ofmr->monitor_id);
> +        rq->command = ofmr->command;
> +
> +        if (ofmr->command == OFPFMC_DELETE) {
> +            return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
> NULL);
> +        }
>
> -    return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
> -                         NULL, false, NULL, NULL);
> +        if (!(flags & (OFPFMF_ADD | OFPFMF_REMOVED | OFPFMF_MODIFY))
> +                || flags & ~(OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED
> +                    | OFPFMF_MODIFY | OFPFMF_INSTRUCTIONS |
> OFPFMF_ONLY_OWN)) {
> +            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR has bad flags %#"PRIx16,
> +                         flags);
> +            return OFPERR_OFPMOFC_BAD_FLAGS;
> +        }
> +
> +        rq->command = ofmr->command;
> +        rq->flags = flags;
> +        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
> +        if (error) {
> +            return error;
> +        }
> +        rq->out_group = ntohl(ofmr->out_group);
> +        rq->table_id = ofmr->table_id;
> +
> +        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
> NULL);
> +    }
> +    default:
> +        OVS_NOT_REACHED();
> +    }
>  }
>
>  void
> @@ -389,66 +567,143 @@ ofputil_append_flow_monitor_request(
>      const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg,
>      enum ofputil_protocol protocol)
>  {
> -    struct nx_flow_monitor_request *nfmr;
>      size_t start_ofs;
>      int match_len;
>      enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>
>      if (!msg->size) {
> -        ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version, msg);
> -    }
> +        switch (version) {
> +        case OFP10_VERSION:
> +        case OFP11_VERSION:
> +        case OFP12_VERSION: {
> +            struct nx_flow_monitor_request *nfmr;
> +
> +            if (!msg->size) {
> +                ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version,
> msg);
> +            }
>
> -    start_ofs = msg->size;
> -    ofpbuf_put_zeros(msg, sizeof *nfmr);
> -    match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
> -
> -    nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
> -    nfmr->id = htonl(rq->id);
> -    nfmr->flags = htons(rq->flags);
> -    nfmr->out_port = htons(ofp_to_u16(rq->out_port));
> -    nfmr->match_len = htons(match_len);
> -    nfmr->table_id = rq->table_id;
> +            start_ofs = msg->size;
> +            ofpbuf_put_zeros(msg, sizeof *nfmr);
> +            match_len = nx_put_match(msg, &rq->match, htonll(0),
> htonll(0));
> +
> +            nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
> +            nfmr->id = htonl(rq->id);
> +            nfmr->flags = htons(ofp_to_nx_flow_monitor_flags(rq->flags));
> +            nfmr->out_port = htons(ofp_to_u16(rq->out_port));
> +            nfmr->match_len = htons(match_len);
> +            nfmr->table_id = rq->table_id;
> +            break;
> +        }
> +        case OFP13_VERSION: {
> +            struct onf_flow_monitor_request *ofmr;
> +
> +            if (!msg->size) {
> +                ofpraw_put(OFPRAW_ONFST13_FLOW_MONITOR_REQUEST, version,
> msg);
> +            }
> +
> +            start_ofs = msg->size;
> +            ofpbuf_put_zeros(msg, sizeof *ofmr);
> +            match_len = oxm_put_match(msg, &rq->match, version);
> +
> +            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
> +            ofmr->id = htonl(rq->id);
> +            ofmr->flags = htons(ofp_to_nx_flow_monitor_flags(rq->flags));
> +            ofmr->match_len = htons(match_len);
> +            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
> +            ofmr->table_id = rq->table_id;
> +            break;
> +        }
> +        case OFP14_VERSION:
> +        case OFP15_VERSION: {
> +            struct ofp14_flow_monitor_request *ofmr;
> +
> +            if (!msg->size) {
> +                ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version,
> msg);
> +            }
> +
> +            start_ofs = msg->size;
> +            ofpbuf_put_zeros(msg, sizeof *ofmr);
> +            oxm_put_match(msg, &rq->match, version);
> +
> +            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
> +            ofmr->monitor_id = htonl(rq->id);
> +            ofmr->command = OFPFMC_ADD;
> +            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
> +            ofmr->out_group = htonl(rq->out_group);
> +            ofmr->flags = htons(rq->flags);
> +            ofmr->table_id = rq->table_id;
> +            break;
> +        }
> +        default:
> +            OVS_NOT_REACHED();
> +        }
> +    }
>  }
>
>  static const char *
> -nx_flow_monitor_flags_to_name(uint32_t bit)
> +ofp_flow_monitor_flags_to_name(uint32_t bit)
>  {
> -    enum nx_flow_monitor_flags fmf = bit;
> +    enum ofp14_flow_monitor_flags fmf = bit;
>
>      switch (fmf) {
> -    case NXFMF_INITIAL: return "initial";
> -    case NXFMF_ADD: return "add";
> -    case NXFMF_DELETE: return "delete";
> -    case NXFMF_MODIFY: return "modify";
> -    case NXFMF_ACTIONS: return "actions";
> -    case NXFMF_OWN: return "own";
> +    case OFPFMF_INITIAL: return "initial";
> +    case OFPFMF_ADD: return "add";
> +    case OFPFMF_REMOVED: return "delete";
> +    case OFPFMF_MODIFY: return "modify";
> +    case OFPFMF_INSTRUCTIONS: return "actions";
> +    case OFPFMF_NO_ABBREV: return "no-abbrev";
> +    case OFPFMF_ONLY_OWN: return "own";
>      }
>
>      return NULL;
>  }
>
> +static const char *
> +ofp_flow_monitor_command_to_string(enum ofp14_flow_monitor_command
> command)
> +{
> +    switch (command) {
> +    case OFPFMC_ADD: return "add";
> +    case OFPFMC_MODIFY: return "modify";
> +    case OFPFMC_DELETE: return "delete";
> +    default:
> +        OVS_NOT_REACHED();
> +    }
> +}
> +
>  void
>  ofputil_flow_monitor_request_format(
>      struct ds *s, const struct ofputil_flow_monitor_request *request,
>      const struct ofputil_port_map *port_map,
>      const struct ofputil_table_map *table_map)
>  {
> +    if (request->command == OFPFMC_DELETE) {
> +        ds_put_format(s, "\n id=%"PRIu32" command=%s", request->id,
> +
> ofp_flow_monitor_command_to_string(request->command));
> +        return;
> +    }
>      ds_put_format(s, "\n id=%"PRIu32" flags=", request->id);
> -    ofp_print_bit_names(s, request->flags, nx_flow_monitor_flags_to_name,
> ',');
> +    ofp_print_bit_names(s, request->flags,
> +                        ofp_flow_monitor_flags_to_name, ',');
>
>      if (request->out_port != OFPP_NONE) {
>          ds_put_cstr(s, " out_port=");
>          ofputil_format_port(request->out_port, port_map, s);
>      }
>
> +    if (request->out_group && (request->out_group != OFPG_ANY)) {
> +        ds_put_format(s, " out_group=%d", request->out_group);
> +    }
> +
>      if (request->table_id != 0xff) {
>          ds_put_format(s, " table=");
>          ofputil_format_table(request->table_id, table_map, s);
>      }
>
> -    ds_put_char(s, ' ');
> -    match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
> -    ds_chomp(s, ' ');
> +    if (request->command != OFPFMC_DELETE) {
> +        ds_put_char(s, ' ');
> +        match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
> +        ds_chomp(s, ' ');
> +    }
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> @@ -464,9 +719,10 @@ parse_flow_monitor_request__(struct
> ofputil_flow_monitor_request *fmr,
>
>      fmr->id = atomic_count_inc(&id);
>
> -    fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
> -                  | NXFMF_OWN | NXFMF_ACTIONS);
> +    fmr->flags = (OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED |
> OFPFMF_MODIFY
> +                  | OFPFMF_ONLY_OWN | OFPFMF_INSTRUCTIONS);
>      fmr->out_port = OFPP_NONE;
> +    fmr->out_group = OFPG_ANY;
>      fmr->table_id = 0xff;
>      match_init_catchall(&fmr->match);
>
> @@ -476,17 +732,19 @@ parse_flow_monitor_request__(struct
> ofputil_flow_monitor_request *fmr,
>          char *error = NULL;
>
>          if (!strcmp(name, "!initial")) {
> -            fmr->flags &= ~NXFMF_INITIAL;
> +            fmr->flags &= ~OFPFMF_INITIAL;
>          } else if (!strcmp(name, "!add")) {
> -            fmr->flags &= ~NXFMF_ADD;
> +            fmr->flags &= ~OFPFMF_ADD;
>          } else if (!strcmp(name, "!delete")) {
> -            fmr->flags &= ~NXFMF_DELETE;
> +            fmr->flags &= ~OFPFMF_REMOVED;
>          } else if (!strcmp(name, "!modify")) {
> -            fmr->flags &= ~NXFMF_MODIFY;
> +            fmr->flags &= ~OFPFMF_MODIFY;
>          } else if (!strcmp(name, "!actions")) {
> -            fmr->flags &= ~NXFMF_ACTIONS;
> +            fmr->flags &= ~OFPFMF_INSTRUCTIONS;
> +        } else if (!strcmp(name, "!abbrev")) {
> +            fmr->flags &= ~OFPFMF_NO_ABBREV;
>          } else if (!strcmp(name, "!own")) {
> -            fmr->flags &= ~NXFMF_OWN;
> +            fmr->flags &= ~OFPFMF_ONLY_OWN;
>          } else if (ofp_parse_protocol(name, &p)) {
>              match_set_dl_type(&fmr->match, htons(p->dl_type));
>              if (p->nw_proto) {
> @@ -511,6 +769,8 @@ parse_flow_monitor_request__(struct
> ofputil_flow_monitor_request *fmr,
>                  }
>              } else if (!strcmp(name, "out_port")) {
>                  fmr->out_port = u16_to_ofp(atoi(value));
> +            } else if (!strcmp(name, "out_group")) {
> +                fmr->out_group = atoi(value);
>              } else {
>                  return xasprintf("%s: unknown keyword %s", str_, name);
>              }
> @@ -562,101 +822,226 @@ int
>  ofputil_decode_flow_update(struct ofputil_flow_update *update,
>                             struct ofpbuf *msg, struct ofpbuf *ofpacts)
>  {
> -    struct nx_flow_update_header *nfuh;
>      unsigned int length;
>      struct ofp_header *oh;
> +    enum ofperr error;
> +    enum ofpraw raw;
>
>      if (!msg->header) {
>          ofpraw_pull_assert(msg);
>      }
>
> +    error = ofpraw_decode(&raw, msg->header);
> +    if (error) {
> +        return error;
> +    }
> +
>      ofpbuf_clear(ofpacts);
>      if (!msg->size) {
>          return EOF;
>      }
>
> -    if (msg->size < sizeof(struct nx_flow_update_header)) {
> -        goto bad_len;
> -    }
> -
>      oh = msg->header;
>
> -    nfuh = msg->data;
> -    update->event = ntohs(nfuh->event);
> -    length = ntohs(nfuh->length);
> -    if (length > msg->size || length % 8) {
> -        goto bad_len;
> -    }
> +    switch ((int) raw) {
> +    case OFPRAW_ONFST13_FLOW_MONITOR_REPLY:
> +    case OFPRAW_NXST_FLOW_MONITOR_REPLY: {
> +        struct nx_flow_update_header *nfuh;
>
> -    if (update->event == NXFME_ABBREV) {
> -        struct nx_flow_update_abbrev *nfua;
> +        if (msg->size < sizeof(struct nx_flow_update_header)) {
> +            goto bad_len;
> +        }
>
> -        if (length != sizeof *nfua) {
> +        nfuh = msg->data;
> +        update->event = nx_to_ofp_flow_update_event(ntohs(nfuh->event));
> +        length = ntohs(nfuh->length);
> +        if (length > msg->size || length % 8) {
>              goto bad_len;
>          }
>
> -        nfua = ofpbuf_pull(msg, sizeof *nfua);
> -        update->xid = nfua->xid;
> -        return 0;
> -    } else if (update->event == NXFME_ADDED
> -               || update->event == NXFME_DELETED
> -               || update->event == NXFME_MODIFIED) {
> -        struct nx_flow_update_full *nfuf;
> -        unsigned int actions_len;
> -        unsigned int match_len;
> -        enum ofperr error;
> +        if (update->event == OFPFME_ABBREV) {
> +            struct nx_flow_update_abbrev *nfua;
> +
> +            if (length != sizeof *nfua) {
> +                goto bad_len;
> +            }
> +
> +            nfua = ofpbuf_pull(msg, sizeof *nfua);
> +            update->xid = nfua->xid;
> +            return 0;
> +        } else if (update->event == OFPFME_ADDED
> +                   || update->event == OFPFME_REMOVED
> +                   || update->event == OFPFME_MODIFIED) {
> +            struct nx_flow_update_full *nfuf;
> +            unsigned int actions_len;
> +            unsigned int match_len;
> +
> +            if (length < sizeof *nfuf) {
> +                goto bad_len;
> +            }
>
> -        if (length < sizeof *nfuf) {
> +            nfuf = ofpbuf_pull(msg, sizeof *nfuf);
> +            match_len = ntohs(nfuf->match_len);
> +            if (sizeof *nfuf + match_len > length) {
> +                goto bad_len;
> +            }
> +
> +            update->reason = ntohs(nfuf->reason);
> +            update->idle_timeout = ntohs(nfuf->idle_timeout);
> +            update->hard_timeout = ntohs(nfuf->hard_timeout);
> +            update->table_id = nfuf->table_id;
> +            update->cookie = nfuf->cookie;
> +            update->priority = ntohs(nfuf->priority);
> +
> +            if (raw == OFPRAW_ONFST13_FLOW_MONITOR_REPLY) {
> +                uint16_t padded_match_len = 0;
> +                unsigned int instructions_len;
> +
> +                error = ofputil_pull_ofp11_match(
> +                    msg, NULL, NULL, &update->match, &padded_match_len);
> +                if (error) {
> +                    return error;
> +                }
> +
> +                instructions_len = length - sizeof *nfuf -
> padded_match_len;
> +                error = ofpacts_pull_openflow_instructions(
> +                    msg, instructions_len, oh->version, NULL, NULL,
> ofpacts);
> +                if (error) {
> +                    return error;
> +                }
> +            } else {
> +                error = nx_pull_match(msg, match_len, &update->match,
> NULL,
> +                                      NULL, false, NULL, NULL);
> +                if (error) {
> +                    return error;
> +                }
> +
> +                actions_len = length - sizeof *nfuf - ROUND_UP(match_len,
> 8);
> +                error = ofpacts_pull_openflow_actions(
> +                    msg, actions_len, oh->version, NULL, NULL, ofpacts);
> +                if (error) {
> +                    return error;
> +                }
> +            }
> +
> +            update->ofpacts = ofpacts->data;
> +            update->ofpacts_len = ofpacts->size;
> +            return 0;
> +        } else {
> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
> %"PRIu16,
> +                         ntohs(nfuh->event));
> +            return OFPERR_NXBRC_FM_BAD_EVENT;
> +        }
> +    }
> +    case OFPRAW_OFPST14_FLOW_MONITOR_REPLY: {
> +        struct ofp_flow_update_header *ofuh;
> +        uint16_t padded_match_len = 0;
> +
> +        if (msg->size < sizeof(struct ofp_flow_update_header)) {
>              goto bad_len;
>          }
>
> -        nfuf = ofpbuf_pull(msg, sizeof *nfuf);
> -        match_len = ntohs(nfuf->match_len);
> -        if (sizeof *nfuf + match_len > length) {
> +        ofuh = msg->data;
> +        update->event = ntohs(ofuh->event);
> +        length = ntohs(ofuh->length);
> +        if (length > msg->size || length % 8) {
>              goto bad_len;
>          }
>
> -        update->reason = ntohs(nfuf->reason);
> -        update->idle_timeout = ntohs(nfuf->idle_timeout);
> -        update->hard_timeout = ntohs(nfuf->hard_timeout);
> -        update->table_id = nfuf->table_id;
> -        update->cookie = nfuf->cookie;
> -        update->priority = ntohs(nfuf->priority);
> +        if (update->event == OFPFME_ABBREV) {
> +            struct ofp_flow_update_abbrev *ofua;
>
> -        error = nx_pull_match(msg, match_len, &update->match, NULL, NULL,
> -                              false, NULL, NULL);
> -        if (error) {
> -            return error;
> -        }
> +            if (length != sizeof *ofua) {
> +                goto bad_len;
> +            }
>
> -        actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
> -        error = ofpacts_pull_openflow_actions(msg, actions_len,
> oh->version,
> -                                              NULL, NULL, ofpacts);
> -        if (error) {
> -            return error;
> -        }
> +            ofua = ofpbuf_pull(msg, sizeof *ofua);
> +            update->xid = ofua->xid;
> +            return 0;
> +        } else if (update->event == OFPFME_PAUSED
> +                   || update->event == OFPFME_RESUMED) {
> +            struct ofp_flow_update_paused *ofup;
>
> -        update->ofpacts = ofpacts->data;
> -        update->ofpacts_len = ofpacts->size;
> -        return 0;
> -    } else {
> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
> -                     ntohs(nfuh->event));
> -        return OFPERR_NXBRC_FM_BAD_EVENT;
> -    }
> +            if (length != sizeof *ofup) {
> +                goto bad_len;
> +            }
> +
> +            ofup = ofpbuf_pull(msg, sizeof *ofup);
> +            return 0;
> +        } else if (update->event == OFPFME_INITIAL
> +                   || update->event == OFPFME_ADDED
> +                   || update->event == OFPFME_REMOVED
> +                   || update->event == OFPFME_MODIFIED) {
> +            struct ofp_flow_update_full *ofuf;
> +            unsigned int instructions_len;
> +
> +            if (length < sizeof *ofuf) {
> +                goto bad_len;
> +            }
>
> +            ofuf = ofpbuf_pull(msg, sizeof *ofuf);
> +            if (sizeof *ofuf > length) {
> +                goto bad_len;
> +            }
> +
> +            update->reason = ofuf->reason;
> +            update->idle_timeout = ntohs(ofuf->idle_timeout);
> +            update->hard_timeout = ntohs(ofuf->hard_timeout);
> +            update->table_id = ofuf->table_id;
> +            update->cookie = ofuf->cookie;
> +            update->priority = ntohs(ofuf->priority);
> +
> +            error = ofputil_pull_ofp11_match(
> +                msg, NULL, NULL, &update->match, &padded_match_len);
> +            if (error) {
> +                return error;
> +            }
> +
> +            instructions_len = length - sizeof *ofuf - padded_match_len;
> +            error = ofpacts_pull_openflow_instructions(
> +                msg, instructions_len, oh->version, NULL, NULL, ofpacts);
> +            if (error) {
> +                return error;
> +            }
> +
> +            update->ofpacts = ofpacts->data;
> +            update->ofpacts_len = ofpacts->size;
> +            return 0;
> +        } else {
> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
> %"PRIu16,
> +                         ntohs(ofuh->event));
> +            return OFPERR_NXBRC_FM_BAD_EVENT;
> +        }
> +    }
> +    default:
> +        OVS_NOT_REACHED();
> +    }
>  bad_len:
> -    VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has %"PRIu32" "
> -                 "leftover bytes at end", msg->size);
> +    VLOG_WARN_RL(&rl, "%s reply has %"PRIu32" leftover bytes at end",
> +                ofpraw_get_name(raw), msg->size);
>      return OFPERR_OFPBRC_BAD_LEN;
>  }
>
>  uint32_t
>  ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
>  {
> -    const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
> +    enum ofperr error;
> +    enum ofpraw raw;
>
> -    return ntohl(cancel->id);
> +    error = ofpraw_decode(&raw, oh);
> +    if (error) {
> +        return error;
> +    }
> +
> +    switch ((int) raw) {
> +    case OFPRAW_ONFT13_FLOW_MONITOR_CANCEL:
> +    case OFPRAW_NXT_FLOW_MONITOR_CANCEL: {
> +        const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
> +        return ntohl(cancel->id);
> +    }
> +    default:
> +        OVS_NOT_REACHED();
> +    }
>  }
>
>  struct ofpbuf *
> @@ -666,9 +1051,99 @@ ofputil_encode_flow_monitor_cancel(uint32_t id, enum
> ofputil_protocol protocol)
>      enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>      struct ofpbuf *msg;
>
> -    msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version, 0);
> -    nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
> -    nfmc->id = htonl(id);
> +    switch (version) {
> +    case OFP10_VERSION:
> +    case OFP11_VERSION:
> +    case OFP12_VERSION:
> +    case OFP13_VERSION: {
> +        if (version == OFP13_VERSION) {
> +            msg = ofpraw_alloc(OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
> version, 0);
> +        } else {
> +            msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version,
> 0);
> +        }
> +        nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
> +        nfmc->id = htonl(id);
> +        break;
> +    }
> +    case OFP14_VERSION:
> +    case OFP15_VERSION: {
> +        struct ofp14_flow_monitor_request *ofmr;
> +
> +        msg = ofpbuf_new(0);
> +
> +        ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version, msg);
> +
> +        size_t start_ofs = msg->size;
> +        ofpbuf_put_zeros(msg, sizeof *ofmr);
> +
> +        ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
> +        ofmr->monitor_id = htonl(id);
> +        ofmr->command = OFPFMC_DELETE;
> +        break;
> +    }
> +    default:
> +        OVS_NOT_REACHED();
> +    }
> +    return msg;
> +}
> +
> +struct ofpbuf *
> +ofputil_encode_flow_monitor_pause(enum ofp_flow_update_event command,
> +                                  enum ofputil_protocol protocol)
> +{
> +    struct ofpbuf *msg;
> +    enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
> +
> +    if (!(command == OFPFME_PAUSED || command == OFPFME_RESUMED)) {
> +        OVS_NOT_REACHED();
> +    }
> +
> +    switch (version) {
> +    case OFP10_VERSION:
> +    case OFP11_VERSION:
> +    case OFP12_VERSION:
> +        if (command == OFPFME_PAUSED) {
> +            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
> +                                   version, htonl(0), 0);
> +        } else {
> +            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED,
> +                                   version, htonl(0), 0);
> +        }
> +        break;
> +    case OFP13_VERSION:
> +        if (command == OFPFME_PAUSED) {
> +            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
> +                                   version, htonl(0), 0);
> +        } else {
> +            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
> +                                   version, htonl(0), 0);
> +        }
> +        break;
> +    case OFP14_VERSION:
> +    case OFP15_VERSION: {
> +        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY, version,
> +                                                   htonl(0), 1024);
> +        struct ofp_flow_update_header *ofuh;
> +        size_t start_ofs = msg->size;
> +
> +        struct ofp_flow_update_paused *ofup;
> +
> +        ofpbuf_put_zeros(msg, sizeof *ofup);
> +        ofup = ofpbuf_at_assert(msg, start_ofs, sizeof *ofup);
> +        ofup->event = htons(command);
> +        ofup->length = htons(8);
> +
> +        ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
> +        ofuh->length = htons(msg->size - start_ofs);
> +        ofuh->event = htons(command);
> +
> +        ofpmsg_update_length(msg);
> +        break;
> +    }
> +    default:
> +        OVS_NOT_REACHED();
> +    }
> +
>      return msg;
>  }
>
> @@ -679,8 +1154,25 @@ ofputil_start_flow_update(struct ovs_list *replies,
>      struct ofpbuf *msg;
>      enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>
> -    msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
> -                           htonl(0), 1024);
> +    switch (version) {
> +    case OFP10_VERSION:
> +    case OFP11_VERSION:
> +    case OFP12_VERSION:
> +        msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
> +                               htonl(0), 1024);
> +        break;
> +    case OFP13_VERSION:
> +        msg = ofpraw_alloc_xid(OFPRAW_ONFST13_FLOW_MONITOR_REPLY, version,
> +                               htonl(0), 1024);
> +        break;
> +    case OFP14_VERSION:
> +    case OFP15_VERSION:
> +        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY, version,
> +                               htonl(0), 1024);
> +        break;
> +    default:
> +        OVS_NOT_REACHED();
> +    }
>
>      ovs_list_init(replies);
>      ovs_list_push_back(replies, &msg->list_node);
> @@ -695,7 +1187,6 @@ ofputil_append_flow_update(const struct
> ofputil_flow_update *update,
>          CONST_CAST(struct ofputil_flow_update *, update);
>      const struct tun_table *orig_tun_table;
>      enum ofp_version version = ofpmp_version(replies);
> -    struct nx_flow_update_header *nfuh;
>      struct ofpbuf *msg;
>      size_t start_ofs;
>
> @@ -705,32 +1196,80 @@ ofputil_append_flow_update(const struct
> ofputil_flow_update *update,
>      msg = ofpbuf_from_list(ovs_list_back(replies));
>      start_ofs = msg->size;
>
> -    if (update->event == NXFME_ABBREV) {
> -        struct nx_flow_update_abbrev *nfua;
> +    switch (version) {
> +        case OFP10_VERSION:
> +        case OFP11_VERSION:
> +        case OFP12_VERSION:
> +        case OFP13_VERSION: {
> +             struct nx_flow_update_header *nfuh;
>
> -        nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
> -        nfua->xid = update->xid;
> -    } else {
> -        struct nx_flow_update_full *nfuf;
> -        int match_len;
> +            if (update->event == OFPFME_ABBREV) {
> +                struct nx_flow_update_abbrev *nfua;
>
> -        ofpbuf_put_zeros(msg, sizeof *nfuf);
> -        match_len = nx_put_match(msg, &update->match, htonll(0),
> htonll(0));
> -        ofpacts_put_openflow_actions(update->ofpacts,
> update->ofpacts_len, msg,
> -                                     version);
> -        nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
> -        nfuf->reason = htons(update->reason);
> -        nfuf->priority = htons(update->priority);
> -        nfuf->idle_timeout = htons(update->idle_timeout);
> -        nfuf->hard_timeout = htons(update->hard_timeout);
> -        nfuf->match_len = htons(match_len);
> -        nfuf->table_id = update->table_id;
> -        nfuf->cookie = update->cookie;
> -    }
> +                nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
> +                nfua->xid = update->xid;
> +            } else {
> +                struct nx_flow_update_full *nfuf;
> +                int match_len;
> +
> +                ofpbuf_put_zeros(msg, sizeof *nfuf);
> +                if (version == OFP13_VERSION) {
> +                    match_len = oxm_put_match(msg, &update->match,
> version);
> +                    ofpacts_put_openflow_instructions(
> +                        update->ofpacts, update->ofpacts_len, msg,
> version);
> +                } else {
> +                    match_len = nx_put_match(msg, &update->match,
> +                                             htonll(0), htonll(0));
> +                    ofpacts_put_openflow_actions(
> +                        update->ofpacts, update->ofpacts_len, msg,
> version);
> +                }
> +                nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
> +                nfuf->reason = htons(update->reason);
> +                nfuf->priority = htons(update->priority);
> +                nfuf->idle_timeout = htons(update->idle_timeout);
> +                nfuf->hard_timeout = htons(update->hard_timeout);
> +                nfuf->match_len = htons(match_len);
> +                nfuf->table_id = update->table_id;
> +                nfuf->cookie = update->cookie;
> +            }
> +
> +            nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
> +            nfuh->length = htons(msg->size - start_ofs);
> +            nfuh->event =
> htons(ofp_to_nx_flow_update_event(update->event));
> +            break;
> +        }
> +        case OFP14_VERSION:
> +        case OFP15_VERSION: {
> +            struct ofp_flow_update_header *ofuh;
>
> -    nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
> -    nfuh->length = htons(msg->size - start_ofs);
> -    nfuh->event = htons(update->event);
> +            if (update->event == OFPFME_ABBREV) {
> +                struct ofp_flow_update_abbrev *ofua;
> +
> +                ofua = ofpbuf_put_zeros(msg, sizeof *ofua);
> +                ofua->xid = update->xid;
> +            } else {
> +                struct ofp_flow_update_full *ofuf;
> +
> +                ofpbuf_put_zeros(msg, sizeof *ofuf);
> +                oxm_put_match(msg, &update->match, version);
> +                ofpacts_put_openflow_instructions(update->ofpacts,
> +                                                  update->ofpacts_len,
> +                                                  msg, version);
> +                ofuf = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuf);
> +                ofuf->reason = update->reason;
> +                ofuf->priority = htons(update->priority);
> +                ofuf->idle_timeout = htons(update->idle_timeout);
> +                ofuf->hard_timeout = htons(update->hard_timeout);
> +                ofuf->table_id = update->table_id;
> +                ofuf->cookie = update->cookie;
> +            }
> +
> +            ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
> +            ofuh->length = htons(msg->size - start_ofs);
> +            ofuh->event = htons(update->event);
> +            break;
> +        }
> +    }
>
>      ofpmp_postappend(replies, start_ofs);
>      update_->match.flow.tunnel.metadata.tab = orig_tun_table;
> @@ -746,24 +1285,37 @@ ofputil_flow_update_format(struct ds *s,
>
>      ds_put_cstr(s, "\n event=");
>      switch (update->event) {
> -    case NXFME_ADDED:
> +    case OFPFME_INITIAL:
> +        ds_put_cstr(s, "INITIAL");
> +        break;
> +
> +    case OFPFME_ADDED:
>          ds_put_cstr(s, "ADDED");
>          break;
>
> -    case NXFME_DELETED:
> +    case OFPFME_REMOVED:
>          ds_put_format(s, "DELETED reason=%s",
>                        ofp_flow_removed_reason_to_string(update->reason,
>                                                          reasonbuf,
>                                                          sizeof
> reasonbuf));
>          break;
>
> -    case NXFME_MODIFIED:
> +    case OFPFME_MODIFIED:
>          ds_put_cstr(s, "MODIFIED");
>          break;
>
> -    case NXFME_ABBREV:
> +    case OFPFME_ABBREV:
>          ds_put_format(s, "ABBREV xid=0x%"PRIx32, ntohl(update->xid));
>          return;
> +
> +    case OFPFME_PAUSED:
> +        ds_put_cstr(s, "PAUSED");
> +        return;
> +
> +    case OFPFME_RESUMED:
> +        ds_put_cstr(s, "RESUMED");
> +        return;
> +
>      }
>
>      ds_put_format(s, " table=");
> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
> index b0facbf9f..bd37fa17a 100644
> --- a/lib/ofp-print.c
> +++ b/lib/ofp-print.c
> @@ -744,10 +744,10 @@ ofp_print_nxt_flow_monitor_cancel(struct ds *string,
>  }
>
>  static enum ofperr
> -ofp_print_nxst_flow_monitor_request(struct ds *string,
> -                                    const struct ofp_header *oh,
> -                                    const struct ofputil_port_map
> *port_map,
> -                                    const struct ofputil_table_map
> *table_map)
> +ofp_print_flow_monitor_request(struct ds *string,
> +                               const struct ofp_header *oh,
> +                               const struct ofputil_port_map *port_map,
> +                               const struct ofputil_table_map *table_map)
>  {
>      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
>      for (;;) {
> @@ -765,10 +765,10 @@ ofp_print_nxst_flow_monitor_request(struct ds
> *string,
>  }
>
>  static enum ofperr
> -ofp_print_nxst_flow_monitor_reply(struct ds *string,
> -                                  const struct ofp_header *oh,
> -                                  const struct ofputil_port_map *port_map,
> -                                  const struct ofputil_table_map
> *table_map)
> +ofp_print_flow_monitor_reply(struct ds *string,
> +                             const struct ofp_header *oh,
> +                             const struct ofputil_port_map *port_map,
> +                             const struct ofputil_table_map *table_map)
>  {
>      uint64_t ofpacts_stub[1024 / 8];
>      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> @@ -1147,12 +1147,12 @@ ofp_to_string__(const struct ofp_header *oh,
>          break;
>
>      case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
> -        return ofp_print_nxst_flow_monitor_request(string, msg, port_map,
> -                                                   table_map);
> +        return ofp_print_flow_monitor_request(string, msg, port_map,
> +                                              table_map);
>
>      case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
> -        return ofp_print_nxst_flow_monitor_reply(string, msg, port_map,
> -                                                 table_map);
> +        return ofp_print_flow_monitor_reply(string, msg, port_map,
> +                                            table_map);
>
>      case OFPTYPE_BUNDLE_CONTROL:
>          return ofp_print_bundle_ctrl(string, msg);
> diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
> index c14834f84..325f45966 100644
> --- a/ofproto/connmgr.c
> +++ b/ofproto/connmgr.c
> @@ -2099,6 +2099,7 @@ ofmonitor_create(const struct
> ofputil_flow_monitor_request *request,
>      m->id = request->id;
>      m->flags = request->flags;
>      m->out_port = request->out_port;
> +    m->out_group = request->out_group;
>      m->table_id = request->table_id;
>      minimatch_init(&m->match, &request->match);
>
> @@ -2134,7 +2135,7 @@ ofmonitor_destroy(struct ofmonitor *m)
>
>  void
>  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
> -                 enum nx_flow_update_event event,
> +                 enum ofp_flow_update_event event,
>                   enum ofp_flow_removed_reason reason,
>                   const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid,
>                   const struct rule_actions *old_actions)
> @@ -2144,39 +2145,42 @@ ofmonitor_report(struct connmgr *mgr, struct rule
> *rule,
>          return;
>      }
>
> -    enum nx_flow_monitor_flags update;
> +    enum ofp14_flow_monitor_flags update;
>      switch (event) {
> -    case NXFME_ADDED:
> -        update = NXFMF_ADD;
> +    case OFPFME_ADDED:
> +        update = OFPFMF_ADD;
>          rule->add_seqno = rule->modify_seqno = monitor_seqno++;
>          break;
>
> -    case NXFME_DELETED:
> -        update = NXFMF_DELETE;
> +    case OFPFME_REMOVED:
> +        update = OFPFMF_REMOVED;
>          break;
>
> -    case NXFME_MODIFIED:
> -        update = NXFMF_MODIFY;
> +    case OFPFME_MODIFIED:
> +        update = OFPFMF_MODIFY;
>          rule->modify_seqno = monitor_seqno++;
>          break;
>
>      default:
> -    case NXFME_ABBREV:
> +    case OFPFME_INITIAL:
> +    case OFPFME_PAUSED:
> +    case OFPFME_RESUMED:
> +    case OFPFME_ABBREV:
>          OVS_NOT_REACHED();
>      }
>
>      struct ofconn *ofconn;
>      LIST_FOR_EACH (ofconn, connmgr_node, &mgr->conns) {
>          if (ofconn->monitor_paused) {
> -            /* Only send NXFME_DELETED notifications for flows that were
> added
> +            /* Only send OFPFME_REMOVED notifications for flows that were
> added
>               * before we paused. */
> -            if (event != NXFME_DELETED
> +            if (event != OFPFME_REMOVED
>                  || rule->add_seqno > ofconn->monitor_paused) {
>                  continue;
>              }
>          }
>
> -        enum nx_flow_monitor_flags flags = 0;
> +        enum ofp14_flow_monitor_flags flags = 0;
>          struct ofmonitor *m;
>          HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
>              if (m->flags & update
> @@ -2186,6 +2190,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
> *rule,
>                          && ofpacts_output_to_port(old_actions->ofpacts,
>
>  old_actions->ofpacts_len,
>                                                    m->out_port)))
> +                && ofproto_rule_has_out_group(rule, m->out_group)
>                  && cls_rule_is_loose_match(&rule->cr, &m->match)) {
>                  flags |= m->flags;
>              }
> @@ -2198,12 +2203,12 @@ ofmonitor_report(struct connmgr *mgr, struct rule
> *rule,
>                  ofconn->sent_abbrev_update = false;
>              }
>
> -            if (flags & NXFMF_OWN || ofconn != abbrev_ofconn
> +            if (flags & OFPFMF_ONLY_OWN || ofconn != abbrev_ofconn
>                  || ofconn->monitor_paused) {
>                  struct ofputil_flow_update fu;
>
>                  fu.event = event;
> -                fu.reason = event == NXFME_DELETED ? reason : 0;
> +                fu.reason = event == OFPFME_REMOVED ? reason : 0;
>                  fu.table_id = rule->table_id;
>                  fu.cookie = rule->flow_cookie;
>                  minimatch_expand(&rule->cr.match, &fu.match);
> @@ -2214,7 +2219,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
> *rule,
>                  fu.hard_timeout = rule->hard_timeout;
>                  ovs_mutex_unlock(&rule->mutex);
>
> -                if (flags & NXFMF_ACTIONS) {
> +                if (flags & OFPFMF_INSTRUCTIONS) {
>                      const struct rule_actions *actions
>                          = rule_get_actions(rule);
>                      fu.ofpacts = actions->ofpacts;
> @@ -2228,7 +2233,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
> *rule,
>              } else if (!ofconn->sent_abbrev_update) {
>                  struct ofputil_flow_update fu;
>
> -                fu.event = NXFME_ABBREV;
> +                fu.event = OFPFME_ABBREV;
>                  fu.xid = abbrev_xid;
>                  ofputil_append_flow_update(&fu, &ofconn->updates,
>
> ofproto_get_tun_tab(rule->ofproto));
> @@ -2263,9 +2268,8 @@ ofmonitor_flush(struct connmgr *mgr)
>              COVERAGE_INC(ofmonitor_pause);
>              ofconn->monitor_paused = monitor_seqno++;
>              protocol = ofconn_get_protocol(ofconn);
> -            struct ofpbuf *pause = ofpraw_alloc_xid(
> -                OFPRAW_NXT_FLOW_MONITOR_PAUSED,
> -                ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
> +            struct ofpbuf *pause = ofputil_encode_flow_monitor_pause(
> +                OFPFME_PAUSED,protocol);
>              ofconn_send(ofconn, pause, counter);
>          }
>      }
> @@ -2289,9 +2293,8 @@ ofmonitor_resume(struct ofconn *ofconn)
>                                        ofconn_get_protocol(ofconn));
>
>      protocol = ofconn_get_protocol(ofconn);
> -    struct ofpbuf *resumed = ofpraw_alloc_xid(
> -            OFPRAW_NXT_FLOW_MONITOR_RESUMED,
> -            ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
> +    struct ofpbuf *resumed = ofputil_encode_flow_monitor_pause(
> +        OFPFME_RESUMED, protocol);
>      ovs_list_push_back(&msgs, &resumed->list_node);
>      ofconn_send_replies(ofconn, &msgs);
>
> diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
> index 56fdc3504..3471d38f9 100644
> --- a/ofproto/connmgr.h
> +++ b/ofproto/connmgr.h
> @@ -168,10 +168,11 @@ struct ofmonitor {
>      struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */
>      uint32_t id;
>
> -    enum nx_flow_monitor_flags flags;
> +    enum ofp14_flow_monitor_flags flags;
>
>      /* Matching. */
>      ofp_port_t out_port;
> +    uint32_t out_group;
>      uint8_t table_id;
>      struct minimatch match;
>  };
> @@ -187,7 +188,8 @@ void ofmonitor_destroy(struct ofmonitor *)
>      OVS_REQUIRES(ofproto_mutex);
>
>  void ofmonitor_report(struct connmgr *, struct rule *,
> -                      enum nx_flow_update_event, enum
> ofp_flow_removed_reason,
> +                      enum ofp_flow_update_event event,
> +                      enum ofp_flow_removed_reason,
>                        const struct ofconn *abbrev_ofconn, ovs_be32
> abbrev_xid,
>                        const struct rule_actions *old_actions)
>      OVS_REQUIRES(ofproto_mutex);
> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> index 57c7d17cb..373526738 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -419,7 +419,7 @@ struct rule {
>       * 'add_seqno' is the sequence number when this rule was created.
>       * 'modify_seqno' is the sequence number when this rule was last
> modified.
>       * See 'monitor_seqno' in connmgr.c for more information. */
> -    enum nx_flow_monitor_flags monitor_flags
> OVS_GUARDED_BY(ofproto_mutex);
> +    enum ofp14_flow_monitor_flags monitor_flags
> OVS_GUARDED_BY(ofproto_mutex);
>      uint64_t add_seqno OVS_GUARDED_BY(ofproto_mutex);
>      uint64_t modify_seqno OVS_GUARDED_BY(ofproto_mutex);
>
> @@ -480,6 +480,8 @@ const struct rule_actions *rule_actions_create(const
> struct ofpact *, size_t);
>  void rule_actions_destroy(const struct rule_actions *);
>  bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port)
>      OVS_REQUIRES(ofproto_mutex);
> +bool ofproto_rule_has_out_group(const struct rule *rule, uint32_t
> group_id)
> +    OVS_REQUIRES(ofproto_mutex);
>
>  #define DECL_OFPROTO_COLLECTION(TYPE, NAME)                             \
>      DECL_OBJECT_COLLECTION(TYPE, NAME)                                  \
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index 11aadbf20..980d4d53b 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -3157,7 +3157,7 @@ ofproto_rule_has_out_port(const struct rule *rule,
> ofp_port_t port)
>  }
>
>  /* Returns true if 'rule' has group and equals group_id. */
> -static bool
> +bool
>  ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
>      OVS_REQUIRES(ofproto_mutex)
>  {
> @@ -5215,7 +5215,7 @@ add_flow_finish(struct ofproto *ofproto, struct
> ofproto_flow_mod *ofm,
>      if (old_rule) {
>          ovsrcu_postpone(remove_rule_rcu, old_rule);
>      } else {
> -        ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0,
> +        ofmonitor_report(ofproto->connmgr, new_rule, OFPFME_ADDED, 0,
>                           req ? req->ofconn : NULL,
>                           req ? req->request->xid : 0, NULL);
>
> @@ -5630,8 +5630,8 @@ replace_rule_finish(struct ofproto *ofproto, struct
> ofproto_flow_mod *ofm,
>          learned_cookies_dec(ofproto, old_actions, dead_cookies);
>
>          if (replaced_rule) {
> -            enum nx_flow_update_event event = ofm->command == OFPFC_ADD
> -                ? NXFME_ADDED : NXFME_MODIFIED;
> +            enum ofp_flow_update_event event = ofm->command == OFPFC_ADD
> +                ? OFPFME_ADDED : OFPFME_MODIFIED;
>
>              bool changed_cookie = (new_rule->flow_cookie
>                                     != old_rule->flow_cookie);
> @@ -5641,7 +5641,7 @@ replace_rule_finish(struct ofproto *ofproto, struct
> ofproto_flow_mod *ofm,
>                                                    old_actions->ofpacts,
>
>  old_actions->ofpacts_len);
>
> -            if (event != NXFME_MODIFIED || changed_actions
> +            if (event != OFPFME_MODIFIED || changed_actions
>                  || changed_cookie) {
>                  ofmonitor_report(ofproto->connmgr, new_rule, event, 0,
>                                   req ? req->ofconn : NULL,
> @@ -5650,7 +5650,7 @@ replace_rule_finish(struct ofproto *ofproto, struct
> ofproto_flow_mod *ofm,
>              }
>          } else {
>              /* XXX: This is slight duplication with
> delete_flows_finish__() */
> -            ofmonitor_report(ofproto->connmgr, old_rule, NXFME_DELETED,
> +            ofmonitor_report(ofproto->connmgr, old_rule, OFPFME_REMOVED,
>                               OFPRR_EVICTION,
>                               req ? req->ofconn : NULL,
>                               req ? req->request->xid : 0, NULL);
> @@ -5931,7 +5931,7 @@ delete_flows_finish__(struct ofproto *ofproto,
>               * before the rule is actually destroyed. */
>              rule->removed_reason = reason;
>
> -            ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED,
> reason,
> +            ofmonitor_report(ofproto->connmgr, rule, OFPFME_REMOVED,
> reason,
>                               req ? req->ofconn : NULL,
>                               req ? req->request->xid : 0, NULL);
>
> @@ -6337,7 +6337,7 @@ handle_barrier_request(struct ofconn *ofconn, const
> struct ofp_header *oh)
>
>  static void
>  ofproto_compose_flow_refresh_update(const struct rule *rule,
> -                                    enum nx_flow_monitor_flags flags,
> +                                    enum ofp14_flow_monitor_flags flags,
>                                      struct ovs_list *msgs,
>                                      const struct tun_table *tun_table,
>                                      enum ofputil_protocol protocol)
> @@ -6346,8 +6346,9 @@ ofproto_compose_flow_refresh_update(const struct
> rule *rule,
>      const struct rule_actions *actions;
>      struct ofputil_flow_update fu;
>
> -    fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
> -                ? NXFME_ADDED : NXFME_MODIFIED);
> +    fu.event = flags & OFPFMF_INITIAL ? OFPFME_INITIAL :
> +               flags & OFPFMF_ADD ?
> +                         OFPFME_ADDED : OFPFME_MODIFIED;
>      fu.reason = 0;
>      ovs_mutex_lock(&rule->mutex);
>      fu.idle_timeout = rule->idle_timeout;
> @@ -6358,7 +6359,7 @@ ofproto_compose_flow_refresh_update(const struct
> rule *rule,
>      minimatch_expand(&rule->cr.match, &fu.match);
>      fu.priority = rule->cr.priority;
>
> -    actions = flags & NXFMF_ACTIONS ? rule_get_actions(rule) : NULL;
> +    actions = flags & OFPFMF_INSTRUCTIONS ? rule_get_actions(rule) : NULL;
>      fu.ofpacts = actions ? actions->ofpacts : NULL;
>      fu.ofpacts_len = actions ? actions->ofpacts_len : 0;
>
> @@ -6377,7 +6378,7 @@ ofmonitor_compose_refresh_updates(struct
> rule_collection *rules,
>      struct rule *rule;
>
>      RULE_COLLECTION_FOR_EACH (rule, rules) {
> -        enum nx_flow_monitor_flags flags = rule->monitor_flags;
> +        enum ofp14_flow_monitor_flags flags = rule->monitor_flags;
>          rule->monitor_flags = 0;
>
>          ofproto_compose_flow_refresh_update(rule, flags, msgs,
> @@ -6391,7 +6392,7 @@ ofproto_collect_ofmonitor_refresh_rule(const struct
> ofmonitor *m,
>                                         struct rule_collection *rules)
>      OVS_REQUIRES(ofproto_mutex)
>  {
> -    enum nx_flow_monitor_flags update;
> +    enum ofp14_flow_monitor_flags update;
>
>      if (rule_is_hidden(rule)) {
>          return;
> @@ -6401,11 +6402,15 @@ ofproto_collect_ofmonitor_refresh_rule(const
> struct ofmonitor *m,
>          return;
>      }
>
> +    if (!ofproto_rule_has_out_group(rule, m->out_group)) {
> +        return;
> +    }
> +
>      if (seqno) {
>          if (rule->add_seqno > seqno) {
> -            update = NXFMF_ADD | NXFMF_MODIFY;
> +            update = OFPFMF_ADD | OFPFMF_MODIFY;
>          } else if (rule->modify_seqno > seqno) {
> -            update = NXFMF_MODIFY;
> +            update = OFPFMF_MODIFY;
>          } else {
>              return;
>          }
> @@ -6414,13 +6419,13 @@ ofproto_collect_ofmonitor_refresh_rule(const
> struct ofmonitor *m,
>              return;
>          }
>      } else {
> -        update = NXFMF_INITIAL;
> +        update = OFPFMF_INITIAL;
>      }
>
>      if (!rule->monitor_flags) {
>          rule_collection_add(rules, rule);
>      }
> -    rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
> +    rule->monitor_flags |= update | (m->flags & OFPFMF_INSTRUCTIONS);
>  }
>
>  static void
> @@ -6449,7 +6454,7 @@ ofproto_collect_ofmonitor_initial_rules(struct
> ofmonitor *m,
>                                          struct rule_collection *rules)
>      OVS_REQUIRES(ofproto_mutex)
>  {
> -    if (m->flags & NXFMF_INITIAL) {
> +    if (m->flags & OFPFMF_INITIAL) {
>          ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
>      }
>  }
> @@ -6512,16 +6517,50 @@ handle_flow_monitor_request(struct ofconn *ofconn,
> const struct ovs_list *msgs)
>              }
>
>              struct ofmonitor *m;
> -            error = ofmonitor_create(&request, ofconn, &m);
> -            if (error) {
> -                goto error;
> +            switch (request.command) {
> +            case OFPFMC_ADD: {
> +                error = ofmonitor_create(&request, ofconn, &m);
> +                if (error) {
> +                    goto error;
> +                }
> +
> +                if (n_monitors >= allocated_monitors) {
> +                    monitors = x2nrealloc(monitors, &allocated_monitors,
> +                                          sizeof *monitors);
> +                }
> +                monitors[n_monitors++] = m;
> +                break;
>              }
> +            case OFPFMC_MODIFY:
> +                /* Modify operation is to delete old monitor and create a
> +                 * new one. */
> +                m = ofmonitor_lookup(ofconn, request.id);
> +                if (!m) {
> +                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
> +                    goto error;
> +                }
> +                ofmonitor_destroy(m);
> +
> +                error = ofmonitor_create(&request, ofconn, &m);
> +                if (error) {
> +                    goto error;
> +                }
>
> -            if (n_monitors >= allocated_monitors) {
> -                monitors = x2nrealloc(monitors, &allocated_monitors,
> -                                      sizeof *monitors);
> +                if (n_monitors >= allocated_monitors) {
> +                    monitors = x2nrealloc(monitors, &allocated_monitors,
> +                                          sizeof *monitors);
> +                }
> +                monitors[n_monitors++] = m;
> +                break;
> +            case OFPFMC_DELETE:
> +                m = ofmonitor_lookup(ofconn, request.id);
> +                if (!m) {
> +                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
> +                    goto error;
> +                }
> +                ofmonitor_destroy(m);
> +                break;
>              }
> -            monitors[n_monitors++] = m;
>              continue;
>
>          error:
> diff --git a/tests/ofp-print.at b/tests/ofp-print.at
> index 2c7e163bd..ab5c37649 100644
> --- a/tests/ofp-print.at
> +++ b/tests/ofp-print.at
> @@ -3258,32 +3258,89 @@ NXT_SET_CONTROLLER_ID (xid=0x3): id=123
>  ])
>  AT_CLEANUP
>
> -AT_SETUP([NXT_FLOW_MONITOR_CANCEL])
> +AT_SETUP([FLOW_MONITOR_CANCEL])
>  AT_KEYWORDS([ofp-print])
> +
> +dnl OpenFlow 1.0-1.2
>  AT_CHECK([ovs-ofctl ofp-print "\
>  01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \
>  01 02 30 40 \
>  "], [0], [dnl
>  NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640
>  ])
> +
> +dnl OpenFlow 1.3
> +AT_CHECK([ovs-ofctl ofp-print "\
> +04 04 00 14 00 00 00 06 4f 4e 46 00 00 00 07 4e \
> +01 02 30 40 \
> +"], [0], [dnl
> +ONFT_FLOW_MONITOR_CANCEL (OF1.3) (xid=0x6): id=16920640
> +])
> +
> +dnl OpenFlow 1.4+
> +AT_CHECK([ovs-ofctl ofp-print "\
> +05 12 00 28 00 00 00 04 00 10 00 00 00 00 00 00 \
> +01 02 30 40 00 00 00 00 00 00 00 00 00 00 00 02 \
> +00 01 00 04 00 00 00 00 \
> +"], [0], [dnl
> +OFPST_FLOW_MONITOR request (OF1.4) (xid=0x4):
> + id=16920640 command=delete
> +])
> +
>  AT_CLEANUP
>
> -AT_SETUP([NXT_FLOW_MONITOR_PAUSED])
> +AT_SETUP([FLOW_MONITOR_PAUSED])
>  AT_KEYWORDS([ofp-print])
> +
> +dnl OpenFlow 1.0-1.2
>  AT_CHECK([ovs-ofctl ofp-print "\
>  01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \
>  "], [0], [dnl
>  NXT_FLOW_MONITOR_PAUSED (xid=0x3):
>  ])
> +
> +dnl OpenFlow 1.3
> +AT_CHECK([ovs-ofctl ofp-print "\
> +04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 4F \
> +"], [0], [dnl
> +ONFT_FLOW_MONITOR_PAUSED (OF1.3) (xid=0x3):
> +])
> +
> +dnl OpenFlow 1.4+
> +AT_CHECK([ovs-ofctl ofp-print "\
> +05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00 \
> +00 08 00 05 00 00 00 00 \
> +"], [0], [dnl
> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
> + event=PAUSED
> +])
>  AT_CLEANUP
>
> -AT_SETUP([NXT_FLOW_MONITOR_RESUMED])
> +AT_SETUP([FLOW_MONITOR_RESUMED])
>  AT_KEYWORDS([ofp-print])
> +
> +dnl OpenFlow 1.0-1.2
>  AT_CHECK([ovs-ofctl ofp-print "\
>  01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \
>  "], [0], [dnl
>  NXT_FLOW_MONITOR_RESUMED (xid=0x3):
>  ])
> +
> +dnl OpenFlow 1.3
> +AT_CHECK([ovs-ofctl ofp-print "\
> +04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 50 \
> +"], [0], [dnl
> +ONFT_FLOW_MONITOR_RESUMED (OF1.3) (xid=0x3):
> +])
> +
> +dnl OpenFlow 1.4+
> +AT_CHECK([ovs-ofctl ofp-print "\
> +05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00
> +00 08 00 06 00 00 00 00
> +"], [0], [dnl
> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
> + event=RESUMED
> +])
>  AT_CLEANUP
>
>  AT_SETUP([NXT_SET_FLOW_FORMAT])
> @@ -3629,8 +3686,10 @@ NXST_AGGREGATE reply (xid=0x4): packet_count=7
> byte_count=420 flow_count=7
>  ])
>  AT_CLEANUP
>
> -AT_SETUP([NXST_FLOW_MONITOR request])
> +AT_SETUP([FLOW_MONITOR request])
>  AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
> +
> +dnl OpenFlow 1.0-1.2
>  AT_CHECK([ovs-ofctl ofp-print "\
>  01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
>  00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \
> @@ -3640,10 +3699,37 @@ NXST_FLOW_MONITOR request (xid=0x4):
>   id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL
> table=1
>   id=8192 flags=delete table=2 in_port=1
>  ])
> +
> +dnl OpenFlow 1.3
> +AT_CHECK([ovs-ofctl ofp-print "\
> +04 12 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 \
> +4f 4e 46 00 00 00 07 4e \
> +00 00 10 00 00 3f 00 04 ff ff ff fe 01 00 00 00 00 01 00 04 00 00 00 00 \
> +00 00 20 00 00 04 ff ff 00 00 00 02 01 00 00 00 00 01 00 04 00 00 00 00 \
> +"], [0], [dnl
> +ONFST_FLOW_MONITOR request (OF1.3) (xid=0x6):
> + id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL
> table=1
> + id=8192 flags=delete out_port=2 table=1
> +])
> +
> +dnl OpenFlow 1.4+
> +AT_CHECK([ovs-ofctl ofp-print "\
> +05 12 00 58 00 00 00 06 00 10 00 00 00 00 00 00 \
> +00 00 10 00 ff ff ff fe ff ff ff ff 00 5f 00 00 00 01 00 04 00 00 00 00 \
> +00 00 20 00 00 00 00 01 00 00 00 40 00 5f 01 01 00 01 00 04 00 00 00 00 \
> +00 00 40 00 00 00 00 02 00 00 00 40 00 5f 02 02 00 01 00 04 00 00 00 00 \
> +"], [0], [dnl
> +OFPST_FLOW_MONITOR request (OF1.4) (xid=0x6):
> + id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL
> table=0
> + id=8192 flags=initial,add,delete,modify,actions,own out_port=1
> out_group=64 table=1
> + id=16384 command=delete
> +])
>  AT_CLEANUP
>
> -AT_SETUP([NXST_FLOW_MONITOR reply])
> +AT_SETUP([FLOW_MONITOR reply])
>  AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
> +
> +dnl OpenFlow 1.0-1.2
>  AT_CHECK([ovs-ofctl ofp-print "\
>  01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
>  00 20 00 01 00 05 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \
> @@ -3654,6 +3740,32 @@ NXST_FLOW_MONITOR reply (xid=0x4):
>   event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16
> cookie=0x123456789abcdef0 in_port=1
>   event=ABBREV xid=0x186a0
>  ])
> +
> +dnl OpenFlow 1.3
> +AT_CHECK([ovs-ofctl ofp-print "\
> +04 13 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 4f 4e 46 00 00 00 07 4e \
> +00 28 00 01 00 05 80 00 00 00 00 00 00 0c 00 00 \
> +12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
> +00 00 00 01 00 00 00 00 \
> +00 08 00 03 00 01 86 a0 \
> +"], [0], [dnl
> +ONFST_FLOW_MONITOR reply (OF1.3) (xid=0x6):
> + event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0 in_port=1
> + event=ABBREV xid=0x186a0
> +])
> +
> +dnl OpenFlow 1.4+
> +AT_CHECK([ovs-ofctl ofp-print "\
> +05 13 00 40 00 00 00 00 00 10 00 00 00 00 00 00 \
> +00 28 00 02 00 05 00 00 00 00 80 00 00 00 00 00 \
> +12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
> +00 00 00 01 00 00 00 00 \
> +00 08 00 04 00 01 86 a0 \
> +"], [0], [dnl
> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
> + event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0 in_port=1
> + event=ABBREV xid=0x186a0
> +])
>  AT_CLEANUP
>
>
> diff --git a/tests/ofproto.at b/tests/ofproto.at
> index 899c0be55..3502454b9 100644
> --- a/tests/ofproto.at
> +++ b/tests/ofproto.at
> @@ -4592,8 +4592,11 @@ ovs-ofctl add-flow br0
> in_port=0,dl_vlan=123,actions=output:1
>  ovs-ofctl -O $2 monitor br0 watch: --detach --no-chdir --pidfile
> >monitor.log 2>&1
>  AT_CAPTURE_FILE([monitor.log])
>  ovs-appctl -t ovs-ofctl ofctl/barrier
> +
> +# For OF(1.4+), replace INITIAL to ADDED
> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> -  [NXST_FLOW_MONITOR reply$3:
> +  [$4 reply$3:
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
>  OFPT_BARRIER_REPLY$3:
>  ])
> @@ -4607,7 +4610,7 @@ ovs-ofctl add-flow br0
> in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7
>  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8
>  ovs-ofctl add-flow br0
> in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9
>  ovs-ofctl add-flow br0
> in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10
> -ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,actions=output:11
> +ovs-ofctl add-flow br0
> in_port=0,dl_vlan=65535,dl_vlan_pcp=3,actions=output:11
>  ovs-ofctl add-flow br0
> in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12
>  ovs-ofctl add-flow br0
> in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13
>  ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14
> @@ -4625,71 +4628,74 @@ ovs-ofctl mod-flows br0
> cookie=5,dl_vlan=123,actions=output:3
>  ovs-ofctl del-flows br0 dl_vlan=123
>  ovs-ofctl del-flows br0
>  ovs-appctl -t ovs-ofctl ofctl/barrier
> +
> +# For OF(1.4+), replace INITIAL to ADDED
> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log |
> multiline_sort], [0],
> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +[$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0
> actions=output:6
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1
> actions=output:7
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
> actions=output:9
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
> actions=output:10
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> - event=ADDED table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
> + event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3
> actions=output:11
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0
> actions=output:12
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1
> actions=output:13
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
> actions=output:15
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
> actions=output:16
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
> actions=output:18
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
> actions=output:19
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=0 actions=output:23
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:3
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0
> actions=output:3
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1
> actions=output:3
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0
> actions=output:3
>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1
> actions=output:3
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123
> actions=output:3
>   event=DELETED reason=delete table=0 cookie=0x5
> in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
>   event=DELETED reason=delete table=0 cookie=0x5
> in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=DELETED reason=delete table=0 cookie=0 in_port=0 actions=output:23
>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0
> actions=output:20
>   event=DELETED reason=delete table=0 cookie=0
> in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
>   event=DELETED reason=delete table=0 cookie=0
> in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
> + event=DELETED reason=delete table=0 cookie=0
> in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11
>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124
> actions=output:2
>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095
> actions=output:14
>   event=DELETED reason=delete table=0 cookie=0
> in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
>   event=DELETED reason=delete table=0 cookie=0
> in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0
> actions=output:21
>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1
> actions=output:22
> - event=DELETED reason=delete table=0 cookie=0 in_port=0,vlan_tci=0x0000
> actions=output:11
>  OFPT_BARRIER_REPLY$3:
>  ])
>
> @@ -4703,13 +4709,13 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
>  AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
>  ])
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log |
> multiline_sort], [0],
> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +[$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=1 actions=output:2
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=2 actions=output:1
>  OFPT_BARRIER_REPLY$3:
>  send: OFPT_FLOW_MOD$3: DEL actions=drop
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=DELETED reason=delete table=0 cookie=0 in_port=1 actions=output:2
>   event=DELETED reason=delete table=0 cookie=0 in_port=2 actions=output:1
>  OFPT_BARRIER_REPLY$3:
> @@ -4732,8 +4738,11 @@ ovs-ofctl add-flow br0
> in_port=0,dl_vlan=123,actions=output:1
>  ovs-ofctl -O $2 monitor br0 watch:\!own --detach --no-chdir --pidfile
> >monitor.log 2>&1
>  AT_CAPTURE_FILE([monitor.log])
>  ovs-appctl -t ovs-ofctl ofctl/barrier
> +
> +# For OF(1.4+), replace INITIAL to ADDED
> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> -  [NXST_FLOW_MONITOR reply$3:
> +  [$4 reply$3:
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
>  OFPT_BARRIER_REPLY$3:
>  ])
> @@ -4747,14 +4756,17 @@ ovs-appctl -t ovs-ofctl ofctl/send $send_buf
>  ovs-appctl -t ovs-ofctl ofctl/barrier
>  AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
>  ])
> +
> +# For OF(1.4+), replace INITIAL to ADDED
> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +[$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=1 actions=output:2
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 in_port=2 actions=output:1
>  OFPT_BARRIER_REPLY$3:
>  send: OFPT_FLOW_MOD$3: DEL actions=drop
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=ABBREV xid=0x12345678
>  OFPT_BARRIER_REPLY$3:
>  ])
> @@ -4775,8 +4787,11 @@ ovs-ofctl add-flow br0
> in_port=0,dl_vlan=123,actions=output:2
>  ovs-ofctl -O $2 monitor br0 watch:out_port=2 --detach --no-chdir
> --pidfile >monitor.log 2>&1
>  AT_CAPTURE_FILE([monitor.log])
>  ovs-appctl -t ovs-ofctl ofctl/barrier
> +
> +# For OF(1.4+), replace INITIAL to ADDED
> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> -  [NXST_FLOW_MONITOR reply$3:
> +  [$4 reply$3:
>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
>  OFPT_BARRIER_REPLY$3:
>  ])
> @@ -4796,17 +4811,19 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
>  ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:2
>  ovs-appctl -t ovs-ofctl ofctl/barrier
>
> +# For OF(1.4+), replace INITIAL to ADDED
> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +[$4 reply$3 (xid=0x0):
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122
> actions=output:1,output:2
>  OFPT_BARRIER_REPLY$3:
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123
> actions=output:1,output:2
>  OFPT_BARRIER_REPLY$3:
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1
>  OFPT_BARRIER_REPLY$3:
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3 (xid=0x0):
>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
>  OFPT_BARRIER_REPLY$3:
>  ])
> @@ -4869,7 +4886,7 @@ ovs-appctl -t ovs-ofctl ofctl/unblock
>  # A barrier doesn't work for this purpose.
>  #    https://www.mail-archive.com/dev@openvswitch.org/msg27013.html
>  #    https://www.mail-archive.com/dev@openvswitch.org/msg27675.html
> -OVS_WAIT_UNTIL([grep NXT_FLOW_MONITOR_RESUMED monitor.log])
> +OVS_WAIT_UNTIL([grep RESUMED monitor.log])
>
>  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>
> @@ -4897,24 +4914,30 @@ AT_CHECK([test $adds = $deletes])
>  # and MODIFIED lines lines depends on hash order, that is, it varies
>  # as we change the hash function or change architecture.  Therefore,
>  # we use a couple of tests below to accept both orders.
> +
> +# Rename all version specific strings to a generic one
> +sed -i'.raw' -e 's|NXT_FLOW_MONITOR|FLOW_MONITOR|' -e
> 's|ONFT_FLOW_MONITOR|FLOW_MONITOR|' monitor.log
> +sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n
> event=PAUSED|FLOW_MONITOR_PAUSED$3:|' monitor.log
> +sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n
> event=RESUMED|FLOW_MONITOR_RESUMED$3:|' monitor.log
> +
>  AT_CHECK([ofctl_strip < monitor.log | sed -n -e '
>  /reg1=0x22$/p
>  /cookie=0x[[23]]/p
> -/NXT_FLOW_MONITOR_PAUSED$3:/p
> -/NXT_FLOW_MONITOR_RESUMED$3:/p
> +/FLOW_MONITOR_PAUSED$3:/p
> +/FLOW_MONITOR_RESUMED$3:/p
>  ' > monitor.log.subset])
>  AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl
>   event=ADDED table=0 cookie=0x1 reg1=0x22
> -NXT_FLOW_MONITOR_PAUSED$3:
> +FLOW_MONITOR_PAUSED$3:
>   event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
>   event=ADDED table=0 cookie=0x3 in_port=1
> -NXT_FLOW_MONITOR_RESUMED$3:
> +FLOW_MONITOR_RESUMED$3:
>  ])
>  AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl
> -NXT_FLOW_MONITOR_PAUSED$3:
> +FLOW_MONITOR_PAUSED$3:
>   event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
>   event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
> -NXT_FLOW_MONITOR_RESUMED$3:
> +FLOW_MONITOR_RESUMED$3:
>  ])
>
>  OVS_VSWITCHD_STOP
> @@ -4931,7 +4954,7 @@ ovs-ofctl -O $2 monitor br0 watch:udp,udp_dst=8
> --detach --no-chdir --pidfile >m
>  AT_CAPTURE_FILE([monitor.log])
>
>  # Wait till reply comes backs with OF Version
> -OVS_WAIT_UNTIL([grep "NXST_FLOW_MONITOR reply$3" monitor.log])
> +OVS_WAIT_UNTIL([grep "$4 reply$3" monitor.log])
>  ovs-appctl -t ovs-ofctl exit
>
>  # Make sure protocol type in messages from vswitchd, matches that of
> requested protocol
> @@ -4940,17 +4963,64 @@ ovs-ofctl add-flow br0
> sctp,sctp_dst=9,action=normal
>
>  OVS_WAIT_UNTIL([grep "event=ADDED " monitor.log])
>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0], [dnl
> -NXST_FLOW_MONITOR reply$3:
> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
> +$4 reply$3:
> +$4 reply$3 (xid=0x0):
>   event=ADDED table=0 cookie=0 sctp,tp_dst=9 actions=NORMAL
>  ])
>  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>  ])
> -CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [])
> -CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)])
> -CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)])
> +CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [],         NXST_FLOW_MONITOR)
> +CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)], NXST_FLOW_MONITOR)
> +CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)], NXST_FLOW_MONITOR)
> +CHECK_FLOW_MONITORING([1.3], [OpenFlow13], [ (OF1.3)], ONFST_FLOW_MONITOR)
> +CHECK_FLOW_MONITORING([1.4], [OpenFlow14], [ (OF1.4)], OFPST_FLOW_MONITOR)
> +CHECK_FLOW_MONITORING([1.5], [OpenFlow15], [ (OF1.5)], OFPST_FLOW_MONITOR)
> +
> +AT_SETUP([ofproto - OpenFlow14 flow monitoring with out_group])
> +AT_KEYWORDS([monitor])
> +OVS_VSWITCHD_START
> +
> +AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0
> group_id=1,type=all,bucket=output:1])
> +AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0
> group_id=2,type=all,bucket=output:2])
> +
> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=121,actions=output:1
> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=122,actions=group:1
> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=123,actions=group:2
> +
> +# Start a monitor watching the flow table and check the initial reply.
> +ovs-ofctl -OOpenFlow14 monitor br0 watch:out_group=2 --detach --no-chdir
> --pidfile >monitor.log 2>&1
> +AT_CAPTURE_FILE([monitor.log])
> +ovs-appctl -t ovs-ofctl ofctl/barrier
> +AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> +[OFPST_FLOW_MONITOR reply (OF1.4):
> + event=INITIAL table=0 cookie=0 in_port=0,dl_vlan=123 actions=group:2
> +OFPT_BARRIER_REPLY (OF1.4):
> +])
> +
> +ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
> +
> +# Add, modify flows and check the updates.
> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=121,actions=group:2
> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=122,actions=group:2
> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=123,actions=group:1
> +ovs-appctl -t ovs-ofctl ofctl/barrier
> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=124,actions=group:2
> +
> +AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
> [0],
> +[OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
> + event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=121 actions=group:2
> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
> + event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=group:2
> +OFPT_BARRIER_REPLY (OF1.4):
> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
> + event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=group:2
> +])
> +
> +OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
>
>  AT_SETUP([ofproto - event filtering (OpenFlow 1.3)])
>  AT_KEYWORDS([monitor])
> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
> index 2017c6eba..d9336a43b 100644
> --- a/utilities/ovs-ofctl.8.in
> +++ b/utilities/ovs-ofctl.8.in
> @@ -626,6 +626,9 @@ monitored.
>  If set, only flows that output to \fIport\fR are monitored.  The
>  \fIport\fR may be an OpenFlow port number or keyword
>  (e.g. \fBLOCAL\fR).
> +.IP "\fBout_group=\fIgroup\fR"
> +If set, only flows that output to \fIgroup\fR number are monitored.
> +This field requires OpenFlow 1.4 (-OOpenFlow14) or later.
>  .IP "\fIfield\fB=\fIvalue\fR"
>  Monitors only flows that have \fIfield\fR specified as the given
>  \fIvalue\fR.  Any syntax valid for matching on \fBdump\-flows\fR may
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 614b73006..5e6823240 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -2313,6 +2313,12 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
>
>              msg = ofpbuf_new(0);
>              ofputil_append_flow_monitor_request(&fmr, msg, protocol);
> +
> +            if (verbosity) {
> +                ofpmsg_update_length(msg);
> +                ofp_print(stdout, msg->data, msg->size, NULL,
> +                          NULL, verbosity + 2);
> +            }
>              dump_transaction(vconn, msg);
>              fflush(stdout);
>          } else if (!strcmp(arg, "resume")) {
> --
> 2.29.2
>
>
Ashish Varma Aug. 24, 2021, 8:11 p.m. UTC | #2
I will take a look.

Thanks,
Ashish

On Tue, Aug 24, 2021 at 9:29 AM Vasu Dasari <vdasari@gmail.com> wrote:

> Hi Ben/Ashish,
>
> When you get a chance, Can you please take a look at my code?
>
> -Vasu
>
> *Vasu Dasari*
>
>
> On Thu, Jul 29, 2021 at 10:36 PM Vasu Dasari <vdasari@gmail.com> wrote:
>
>> Extended OpenFlow monitoring support
>> * OpenFlow 1.3 with ONF extensions
>> * OpenFlow 1.4+ as defined in OpenFlow specification 1.4+.
>>
>> ONF extensions are similar to Nicira extensions except for
>> onf_flow_monitor_request{}
>> where out_port is defined as 32-bit number OF(1.1) number, oxm match
>> formats are
>> used in update and request messages.
>>
>> Flow monitoring support in 1.4+ is slightly different from Nicira and ONF
>> extensions.
>>  * More flow monitoring flags are defined.
>>  * Monitor add/modify/delete command is intruduced in flow_monitor
>>    request message.
>>  * Addition of out_group as part of flow_monitor request message
>>
>> Description of changes:
>> 1. Generate ofp-msgs.inc to be able to support 1.3, 1.4+ flow Monitoring
>> messages.
>>     include/openvswitch/ofp-msgs.h
>>
>> 2. Modify openflow header files with protocol specific headers.
>>     include/openflow/openflow-1.3.h
>>     include/openflow/openflow-1.4.h
>>
>> 3. Modify OvS abstraction of openflow headers. ofp-monitor.h leverages
>> enums
>>    from on nicira extensions for creating protocol abstraction headers.
>> OF(1.4+)
>>    enums are superset of nicira extensions.
>>     include/openvswitch/ofp-monitor.h
>>
>> 4. Changes to these files reflect encoding and decoding of new protocol
>> messages.
>>     lib/ofp-monitor.c
>>
>> 5. Changes to mmodules using ofp-monitor APIs. Most of the changes here
>> are to
>>    migrate enums from nicira to OF 1.4+ versions.
>>     ofproto/connmgr.c
>>     ofproto/connmgr.h
>>     ofproto/ofproto-provider.h
>>     ofproto/ofproto.c
>>
>> 6. Extended protocol decoding tests to verify all protocol versions
>>         FLOW_MONITOR_CANCEL
>>         FLOW_MONITOR_PAUSED
>>         FLOW_MONITOR_RESUMED
>>         FLOW_MONITOR request
>>         FLOW_MONITOR reply
>>     tests/ofp-print.at
>>
>> 7. Modify flow monitoring tests to be able executed by all protocol
>> versions.
>>     tests/ofproto.at
>>
>> 7. Modified documentation highlighting the change
>>     utilities/ovs-ofctl.8.in
>>     NEWS
>>
>> Signed-off-by: Vasu Dasari <vdasari@gmail.com>
>> Reported-at:
>> https://mail.openvswitch.org/pipermail/ovs-dev/2021-June/383915.html
>> ---
>> v1:
>>  - Fixed 0-day Robot errors
>>
>> ---
>>  NEWS                              |   6 +-
>>  include/openflow/openflow-1.3.h   |  89 ++++
>>  include/openflow/openflow-1.4.h   |  93 +++-
>>  include/openvswitch/ofp-monitor.h |   9 +-
>>  include/openvswitch/ofp-msgs.h    |  39 +-
>>  lib/ofp-monitor.c                 | 844 ++++++++++++++++++++++++------
>>  lib/ofp-print.c                   |  24 +-
>>  ofproto/connmgr.c                 |  47 +-
>>  ofproto/connmgr.h                 |   6 +-
>>  ofproto/ofproto-provider.h        |   4 +-
>>  ofproto/ofproto.c                 |  89 +++-
>>  tests/ofp-print.at                | 122 ++++-
>>  tests/ofproto.at                  | 176 +++++--
>>  utilities/ovs-ofctl.8.in          |   3 +
>>  utilities/ovs-ofctl.c             |   6 +
>>  15 files changed, 1265 insertions(+), 292 deletions(-)
>>
>> diff --git a/NEWS b/NEWS
>> index 02884b774..47ad9de2a 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -25,8 +25,10 @@ v2.16.0 - xx xxx xxxx
>>     - In ovs-vsctl and vtep-ctl, the "find" command now accept new
>>       operators {in} and {not-in}.
>>     - OpenFlow:
>> -     * Extend Flow Monitoring support for OpenFlow 1.0-1.2 with Nicira
>> -       Extensions
>> +     * Extended Flow Monitoring support for all supported OpenFlow
>> versions
>> +         OpenFlow versions 1.0-1.2 with Nicira Extensions
>> +         OpenFlow versions 1.3 with Open Network Foundation extension
>> +         OpenFlow versions 1.4+, as defined in the OpenFlow specification
>>     - Userspace datapath:
>>       * Auto load balancing of PMDs now partially supports cross-NUMA
>> polling
>>         cases, e.g if all PMD threads are running on the same NUMA node.
>> diff --git a/include/openflow/openflow-1.3.h
>> b/include/openflow/openflow-1.3.h
>> index c48a8ea7f..1a818dbb4 100644
>> --- a/include/openflow/openflow-1.3.h
>> +++ b/include/openflow/openflow-1.3.h
>> @@ -374,4 +374,93 @@ struct ofp13_async_config {
>>  };
>>  OFP_ASSERT(sizeof(struct ofp13_async_config) == 24);
>>
>> +struct onf_flow_monitor_request {
>> +    ovs_be32   id;            /* Controller-assigned ID for this
>> monitor. */
>> +    ovs_be16   flags;         /* ONFFMF_*. */
>> +    ovs_be16   match_len;     /* Length of oxm_fields. */
>> +    ovs_be32   out_port;      /* Required output port, if not OFPP_NONE.
>> */
>> +    uint8_t    table_id;      /* One table’s ID or 0xff for all tables.
>> */
>> +    uint8_t    zeros[3];      /* Align to 64 bits (must be zero). */
>> +    /* Followed by an ofp11_match structure. */
>> +};
>> +OFP_ASSERT(sizeof(struct onf_flow_monitor_request) == 16);
>> +
>> +/* Header for experimenter requests and replies. */
>> +struct onf_experimenter_header {
>> +    struct ofp_header header;
>> +    ovs_be32   vendor;        /* ONF_EXPERIMENTER_ID. */
>> +    ovs_be32   subtype;       /* One of ONFT_*. */
>> +};
>> +OFP_ASSERT(sizeof(struct onf_experimenter_header) == 16);
>> +
>> +enum onf_flow_monitor_msg_type {
>> +    ONFT_FLOW_MONITOR_CANCEL = 1870,
>> +    ONFT_FLOW_MONITOR_PAUSED = 1871,
>> +    ONFT_FLOW_MONITOR_RESUMED = 1872
>> +};
>> +
>> +/* ’flags’ bits in struct onf_flow_monitor_request. */
>> +enum onf_flow_monitor_flags {
>> +    /* When to send updates. */
>> +    ONFFMF_INITIAL = 1 << 0,  /* Initially matching flows. */
>> +    ONFFMF_ADD = 1 << 1,      /* New matching flows as they are added. */
>> +    ONFFMF_DELETE = 1 << 2,   /* Old matching flows as they are removed.
>> */
>> +    ONFFMF_MODIFY = 1 << 3,   /* Matching flows as they are changed. */
>> +
>> +    /* What to include in updates. */
>> +    ONFFMF_ACTIONS = 1 << 4,  /* If set, actions are included. */
>> +    ONFFMF_OWN = 1 << 5,      /* If set, include own changes in full. */
>> +};
>> +
>> +/* ONFST_FLOW_MONITOR reply header. */
>> +struct onf_flow_update_header {
>> +    ovs_be16   length;        /* Length of this entry. */
>> +    ovs_be16   event;         /* One of ONFFME_*. */
>> +    /* ...other data depending on ’event’... */
>> +};
>> +OFP_ASSERT(sizeof(struct onf_flow_update_header) == 4);
>> +
>> +/* ’event’ values in struct onf_flow_update_header. */
>> +enum onf_flow_update_event {
>> +    /* struct onf_flow_update_full. */
>> +    ONFFME_ADDED = 0,         /* Flow was added. */
>> +    ONFFME_DELETED = 1,       /* Flow was deleted. */
>> +    ONFFME_MODIFIED = 2,      /* Flow (generally its actions) was
>> changed. */
>> +
>> +    /* struct onf_flow_update_abbrev. */
>> +    ONFFME_ABBREV = 3,        /* Abbreviated reply. */
>> +};
>> +
>> +/* ONFST_FLOW_MONITOR reply for ONFFME_ADDED, ONFFME_DELETED, and
>> +* ONFFME_MODIFIED. */
>> +struct onf_flow_update_full {
>> +    ovs_be16   length;        /* Length is 24. */
>> +    ovs_be16   event;         /* One of ONFFME_*. */
>> +    ovs_be16   reason;        /* OFPRR_* for ONFFME_DELETED, else zero.
>> */
>> +    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   match_len;     /* Length of oxm_fields. */
>> +    uint8_t    table_id;      /* ID of flow’s table. */
>> +    uint8_t    pad;           /* Reserved, currently zeroed. */
>> +    ovs_be64   cookie;        /* Opaque controller-issued identifier. */
>> +    /* Followed by:
>> +     *   - Exactly match_len (possibly 0) bytes containing the
>> oxm_fields, then
>> +     *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7)
>> bytes of
>> +     *     all-zero bytes, then
>> +     *   - Instructions to fill out the remainder ’length’ bytes (always
>> a
>> +     *     multiple of 8). If ONFFMF_ACTIONS was not specified, or
>> ’event’ is
>> +     *     ONFFME_DELETED, no actions are included.
>> +     */
>> +};
>> +OFP_ASSERT(sizeof(struct onf_flow_update_full) == 24);
>> +
>> +/* ONFST_FLOW_MONITOR reply for ONFFME_ABBREV. */
>> +struct onf_flow_update_abbrev {
>> +    ovs_be16   length;     /* Length is 8. */
>> +    ovs_be16   event;      /* ONFFME_ABBREV. */
>> +    ovs_be32   xid;        /* Controller-specified xid from flow_mod. */
>> +};
>> +OFP_ASSERT(sizeof(struct onf_flow_update_abbrev) == 8);
>> +
>>  #endif /* openflow/openflow-1.3.h */
>> diff --git a/include/openflow/openflow-1.4.h
>> b/include/openflow/openflow-1.4.h
>> index be191180b..8e6a163fd 100644
>> --- a/include/openflow/openflow-1.4.h
>> +++ b/include/openflow/openflow-1.4.h
>> @@ -358,27 +358,100 @@ OFP_ASSERT(sizeof(struct
>> ofp14_flow_monitor_request) == 16);
>>
>>  /* Flow monitor commands */
>>  enum ofp14_flow_monitor_command {
>> -    OFPFMC14_ADD = 0, /* New flow monitor. */
>> -    OFPFMC14_MODIFY = 1, /* Modify existing flow monitor. */
>> -    OFPFMC14_DELETE = 2, /* Delete/cancel existing flow monitor. */
>> +    OFPFMC_ADD = 0, /* New flow monitor. */
>> +    OFPFMC_MODIFY = 1, /* Modify existing flow monitor. */
>> +    OFPFMC_DELETE = 2, /* Delete/cancel existing flow monitor. */
>>  };
>>
>>  /* 'flags' bits in struct of_flow_monitor_request. */
>>  enum ofp14_flow_monitor_flags {
>>      /* When to send updates. */
>>      /* Common to NX and OpenFlow 1.4 */
>> -    OFPFMF14_INITIAL = 1 << 0,     /* Initially matching flows. */
>> -    OFPFMF14_ADD = 1 << 1,         /* New matching flows as they are
>> added. */
>> -    OFPFMF14_REMOVED = 1 << 2,     /* Old matching flows as they are
>> removed. */
>> -    OFPFMF14_MODIFY = 1 << 3,      /* Matching flows as they are
>> changed. */
>> +    OFPFMF_INITIAL = 1 << 0,     /* Initially matching flows. */
>> +    OFPFMF_ADD = 1 << 1,         /* New matching flows as they are
>> added. */
>> +    OFPFMF_REMOVED = 1 << 2,     /* Old matching flows as they are
>> removed. */
>> +    OFPFMF_MODIFY = 1 << 3,      /* Matching flows as they are changed.
>> */
>>
>>      /* What to include in updates. */
>>      /* Common to NX and OpenFlow 1.4 */
>> -    OFPFMF14_INSTRUCTIONS = 1 << 4, /* If set, instructions are
>> included. */
>> -    OFPFMF14_NO_ABBREV = 1 << 5,    /* If set, include own changes in
>> full. */
>> +    OFPFMF_INSTRUCTIONS = 1 << 4, /* If set, instructions are included.
>> */
>> +    OFPFMF_NO_ABBREV = 1 << 5,    /* If set, include own changes in
>> full. */
>>      /* OpenFlow 1.4 */
>> -    OFPFMF14_ONLY_OWN = 1 << 6,     /* If set, don't include other
>> controllers.
>> +    OFPFMF_ONLY_OWN = 1 << 6,     /* If set, don't include other
>> controllers.
>>                                       */
>>  };
>>
>> +/* OFPMP_FLOW_MONITOR reply header.
>> + *
>> + * The body of an OFPMP_FLOW_MONITOR reply is an array of variable-length
>> + * structures, each of which begins with this header.  The ’length’
>> member may
>> + * be used to traverse the array, and the ’event’ member may be used to
>> + * determine the particular structure.
>> + * Every instance is a multiple of 8 bytes long. */
>> +struct ofp_flow_update_header {
>> +    ovs_be16 length;                /* Length of this entry. */
>> +    ovs_be16 event;                 /* One of OFPFME_*. */
>> +    /* ...other data depending on ’event’... */
>> +};
>> +OFP_ASSERT(sizeof(struct ofp_flow_update_header) == 4);
>> +
>> +/* ’event’ values in struct ofp_flow_update_header. */
>> +enum ofp_flow_update_event {
>> +    /* struct ofp_flow_update_full. */
>> +    OFPFME_INITIAL = 0,          /* Flow present when flow monitor
>> created. */
>> +    OFPFME_ADDED = 1,            /* Flow was added. */
>> +    OFPFME_REMOVED = 2,          /* Flow was removed. */
>> +    OFPFME_MODIFIED = 3,         /* Flow instructions were changed. */
>> +
>> +    /* struct ofp_flow_update_abbrev. */
>> +    OFPFME_ABBREV = 4,           /* Abbreviated reply. */
>> +
>> +    /* struct ofp_flow_update_header. */
>> +    OFPFME_PAUSED = 5,           /* Monitoring paused (out of buffer
>> space). */
>> +    OFPFME_RESUMED = 6,          /* Monitoring resumed. */
>> +};
>> +
>> +/* OFPMP_FLOW_MONITOR reply for OFPFME_INITIAL, OFPFME_ADDED,
>> OFPFME_REMOVED,
>> + * and OFPFME_MODIFIED. */
>> +struct ofp_flow_update_full {
>> +    ovs_be16 length;           /* Length is 32 + match + instructions. */
>> +    ovs_be16 event;            /* One of OFPFME_*. */
>> +    uint8_t table_id;          /* ID of flow’s table. */
>> +    uint8_t reason;            /* OFPRR_* for OFPFME_REMOVED, else zero.
>> */
>> +    ovs_be16 idle_timeout;     /* Number of seconds idle before
>> expiration. */
>> +    ovs_be16 hard_timeout;     /* Number of seconds before expiration. */
>> +    ovs_be16 priority;         /* Priority of the entry. */
>> +    uint8_t zeros[4];          /* Reserved, currently zeroed. */
>> +    ovs_be64 cookie;           /* Opaque controller-issued identifier. */
>> +    /* Instruction set.
>> +     *      If OFPFMF_INSTRUCTIONS was not specified, or ’event’ is
>> +     *     OFPFME_REMOVED, no instructions are included.
>> +     */
>> +};
>> +OFP_ASSERT(sizeof(struct ofp_flow_update_full) == 24);
>> +
>> +/* OFPMP_FLOW_MONITOR reply for OFPFME_ABBREV.
>> + *
>> + * When the controller does not specify OFPFMF_NO_ABBREV in a monitor
>> request,
>> + * any flow tables changes due to the controller’s own requests (on the
>> same
>> + * OpenFlow channel) will be abbreviated, when possible, to this form,
>> which
>> + * simply specifies the ’xid’ of the OpenFlow request (e.g. an
>> OFPT_FLOW_MOD)
>> + * that caused the change.
>> + * Some changes cannot be abbreviated and will be sent in full.
>> + */
>> +struct ofp_flow_update_abbrev {
>> +    ovs_be16 length;     /* Length is 8. */
>> +    ovs_be16 event;      /* OFPFME_ABBREV. */
>> +    ovs_be32 xid;        /* Controller-specified xid from flow_mod. */
>> +};
>> +OFP_ASSERT(sizeof(struct ofp_flow_update_abbrev) == 8);
>> +
>> +/* OFPMP_FLOW_MONITOR reply for OFPFME_PAUSED and OFPFME_RESUMED.*/
>> +struct ofp_flow_update_paused {
>> +    ovs_be16 length;      /* Length is 8. */
>> +    ovs_be16 event;       /* One of OFPFME_*. */
>> +    uint8_t zeros[4];     /* Reserved, currently zeroed. */
>> +};
>> +OFP_ASSERT(sizeof(struct ofp_flow_update_paused) == 8);
>> +
>>  #endif /* openflow/openflow-1.4.h */
>> diff --git a/include/openvswitch/ofp-monitor.h
>> b/include/openvswitch/ofp-monitor.h
>> index 835efd0f3..7c7cfcff4 100644
>> --- a/include/openvswitch/ofp-monitor.h
>> +++ b/include/openvswitch/ofp-monitor.h
>> @@ -61,8 +61,10 @@ void ofputil_flow_removed_format(struct ds *,
>>  /* Abstract nx_flow_monitor_request. */
>>  struct ofputil_flow_monitor_request {
>>      uint32_t id;
>> -    enum nx_flow_monitor_flags flags;
>> +    enum ofp14_flow_monitor_command command;
>> +    enum ofp14_flow_monitor_flags flags;
>>      ofp_port_t out_port;
>> +    uint32_t out_group;
>>      uint8_t table_id;
>>      struct match match;
>>  };
>> @@ -85,7 +87,7 @@ char *parse_flow_monitor_request(struct
>> ofputil_flow_monitor_request *,
>>
>>  /* Abstract nx_flow_update. */
>>  struct ofputil_flow_update {
>> -    enum nx_flow_update_event event;
>> +    enum ofp_flow_update_event event;
>>
>>      /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */
>>      enum ofp_flow_removed_reason reason;
>> @@ -119,6 +121,9 @@ uint32_t ofputil_decode_flow_monitor_cancel(const
>> struct ofp_header *);
>>  struct ofpbuf *ofputil_encode_flow_monitor_cancel(
>>      uint32_t id, enum ofputil_protocol protocol);
>>
>> +struct ofpbuf * ofputil_encode_flow_monitor_pause(
>> +    enum ofp_flow_update_event command, enum ofputil_protocol protocol);
>> +
>>  struct ofputil_requestforward {
>>      ovs_be32 xid;
>>      /* Also used for OF 1.0-1.3 when using Nicira Extension: */
>> diff --git a/include/openvswitch/ofp-msgs.h
>> b/include/openvswitch/ofp-msgs.h
>> index c5fde0270..921a937e5 100644
>> --- a/include/openvswitch/ofp-msgs.h
>> +++ b/include/openvswitch/ofp-msgs.h
>> @@ -453,14 +453,33 @@ enum ofpraw {
>>
>>      /* OFPST 1.4+ (16): uint8_t[8][]. */
>>      OFPRAW_OFPST14_FLOW_MONITOR_REQUEST,
>> +    /* ONFST 1.3 (1870): uint8_t[8][]. */
>> +    OFPRAW_ONFST13_FLOW_MONITOR_REQUEST,
>>      /* NXST 1.0-1.2 (2): uint8_t[8][]. */
>>      OFPRAW_NXST_FLOW_MONITOR_REQUEST,
>>
>>      /* OFPST 1.4+ (16): uint8_t[8][]. */
>>      OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
>> +    /* ONFST 1.3 (1870): uint8_t[8][]. */
>> +    OFPRAW_ONFST13_FLOW_MONITOR_REPLY,
>>      /* NXST 1.0-1.2 (2): uint8_t[8][]. */
>>      OFPRAW_NXST_FLOW_MONITOR_REPLY,
>>
>> +    /* ONFT 1.3 (1870): struct nx_flow_monitor_cancel. */
>> +    OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
>> +    /* NXT 1.0-1.2 (21): struct nx_flow_monitor_cancel. */
>> +    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
>> +
>> +    /* ONFT 1.3 (1871): void. */
>> +    OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
>> +    /* NXT 1.0-1.2 (22): void. */
>> +    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>> +
>> +    /* ONFT 1.3 (1872): void. */
>> +    OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
>> +    /* NXT 1.0-1.2 (23): void. */
>> +    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>> +
>>  /* Nicira extension messages.
>>   *
>>   * Nicira extensions that correspond to standard OpenFlow messages are
>> listed
>> @@ -481,15 +500,6 @@ enum ofpraw {
>>      /* NXT 1.0+ (20): struct nx_controller_id. */
>>      OFPRAW_NXT_SET_CONTROLLER_ID,
>>
>> -    /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */
>> -    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
>> -
>> -    /* NXT 1.0+ (22): void. */
>> -    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>> -
>> -    /* NXT 1.0+ (23): void. */
>> -    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>> -
>>      /* NXT 1.0+ (24): struct nx_tlv_table_mod, struct nx_tlv_map[]. */
>>      OFPRAW_NXT_TLV_TABLE_MOD,
>>
>> @@ -741,8 +751,10 @@ enum ofptype {
>>                                        * OFPRAW_OFPST14_PORT_DESC_REPLY.
>> */
>>
>>      OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /*
>> OFPRAW_OFPST14_FLOW_MONITOR_REQUEST.
>> +                                         *
>> OFPRAW_ONFST13_FLOW_MONITOR_REQUEST.
>>                                           *
>> OFPRAW_NXST_FLOW_MONITOR_REQUEST. */
>>      OFPTYPE_FLOW_MONITOR_STATS_REPLY,   /*
>> OFPRAW_OFPST14_FLOW_MONITOR_REPLY.
>> +                                         *
>> OFPRAW_ONFST13_FLOW_MONITOR_REPLY.
>>                                           *
>> OFPRAW_NXST_FLOW_MONITOR_REPLY. */
>>
>>      /* Nicira extensions. */
>> @@ -762,9 +774,12 @@ enum ofptype {
>>      OFPTYPE_CT_FLUSH_ZONE,            /* OFPRAW_NXT_CT_FLUSH_ZONE. */
>>
>>      /* Flow monitor extension. */
>> -    OFPTYPE_FLOW_MONITOR_CANCEL,        /*
>> OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
>> -    OFPTYPE_FLOW_MONITOR_PAUSED,        /*
>> OFPRAW_NXT_FLOW_MONITOR_PAUSED. */
>> -    OFPTYPE_FLOW_MONITOR_RESUMED,       /*
>> OFPRAW_NXT_FLOW_MONITOR_RESUMED. */
>> +    OFPTYPE_FLOW_MONITOR_CANCEL,  /* OFPRAW_NXT_FLOW_MONITOR_CANCEL.
>> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_CANCEL.
>> */
>> +    OFPTYPE_FLOW_MONITOR_PAUSED,  /* OFPRAW_NXT_FLOW_MONITOR_PAUSED.
>> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_PAUSED.
>> */
>> +    OFPTYPE_FLOW_MONITOR_RESUMED, /* OFPRAW_NXT_FLOW_MONITOR_RESUMED.
>> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_RESUMED
>> */
>>  };
>>
>>  /* Decoding messages into OFPTYPE_* values. */
>> diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
>> index 51f01b100..1756c9e8e 100644
>> --- a/lib/ofp-monitor.c
>> +++ b/lib/ofp-monitor.c
>> @@ -328,6 +328,98 @@ ofputil_flow_removed_format(struct ds *s,
>>      ds_put_format(s, " pkts%"PRIu64" bytes%"PRIu64"\n",
>>                    fr->packet_count, fr->byte_count);
>>  }
>> +
>> +static uint16_t
>> +nx_to_ofp_flow_monitor_flags(uint16_t flags)
>> +{
>> +    uint16_t oxm_flags = 0;
>> +
>> +    if (flags & NXFMF_INITIAL) {
>> +        oxm_flags |= OFPFMF_INITIAL;
>> +    }
>> +    if (flags & NXFMF_ADD) {
>> +        oxm_flags |= OFPFMF_ADD;
>> +    }
>> +    if (flags & NXFMF_DELETE) {
>> +        oxm_flags |= OFPFMF_REMOVED;
>> +    }
>> +    if (flags & NXFMF_MODIFY) {
>> +        oxm_flags |= OFPFMF_MODIFY;
>> +    }
>> +    if (flags & NXFMF_ACTIONS) {
>> +        oxm_flags |= OFPFMF_INSTRUCTIONS;
>> +    }
>> +    if (flags & NXFMF_OWN) {
>> +        oxm_flags |= OFPFMF_ONLY_OWN;
>> +    }
>> +
>> +    return oxm_flags;
>> +}
>> +
>> +static uint16_t
>> +ofp_to_nx_flow_monitor_flags(uint16_t flags)
>> +{
>> +    uint16_t nx_flags = 0;
>> +
>> +    if (flags & OFPFMF_INITIAL) {
>> +        nx_flags |= NXFMF_INITIAL;
>> +    }
>> +    if (flags & OFPFMF_ADD) {
>> +        nx_flags |= NXFMF_ADD;
>> +    }
>> +    if (flags & OFPFMF_REMOVED) {
>> +        nx_flags |= NXFMF_DELETE;
>> +    }
>> +    if (flags & OFPFMF_MODIFY) {
>> +        nx_flags |= NXFMF_MODIFY;
>> +    }
>> +    if (flags & OFPFMF_INSTRUCTIONS) {
>> +        nx_flags |= NXFMF_ACTIONS;
>> +    }
>> +    if (flags & OFPFMF_ONLY_OWN) {
>> +        nx_flags |= NXFMF_OWN;
>> +    }
>> +
>> +    return nx_flags;
>> +}
>> +
>> +static enum ofp_flow_update_event
>> +nx_to_ofp_flow_update_event(enum nx_flow_update_event event)
>> +{
>> +    switch (event) {
>> +    case NXFME_ADDED:
>> +        return OFPFME_ADDED;
>> +    case NXFME_DELETED:
>> +        return OFPFME_REMOVED;
>> +    case NXFME_MODIFIED:
>> +        return OFPFME_MODIFIED;
>> +    case NXFME_ABBREV:
>> +        return OFPFME_ABBREV;
>> +     default:
>> +        OVS_NOT_REACHED();
>> +    }
>> +}
>> +
>> +static enum nx_flow_update_event
>> +ofp_to_nx_flow_update_event(enum ofp_flow_update_event event)
>> +{
>> +    switch (event) {
>> +    case OFPFME_INITIAL:
>> +    case OFPFME_ADDED:
>> +        return NXFME_ADDED;
>> +    case OFPFME_REMOVED:
>> +        return NXFME_DELETED;
>> +    case OFPFME_MODIFIED:
>> +        return NXFME_MODIFIED;
>> +    case OFPFME_ABBREV:
>> +        return NXFME_ABBREV;
>> +    default:
>> +    case OFPFME_PAUSED:
>> +    case OFPFME_RESUMED:
>> +        OVS_NOT_REACHED();
>> +    }
>> +}
>> +
>>
>>  /* ofputil_flow_monitor_request */
>>
>> @@ -345,43 +437,129 @@ int
>>  ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request
>> *rq,
>>                                      struct ofpbuf *msg)
>>  {
>> -    struct nx_flow_monitor_request *nfmr;
>>      uint16_t flags;
>> +    enum ofperr error;
>> +    enum ofpraw raw;
>>
>> -    if (!msg->header) {
>> -        ofpraw_pull_assert(msg);
>> +    error = (msg->header ? ofpraw_decode(&raw, msg->header)
>> +             : ofpraw_pull(&raw, msg));
>> +    if (error) {
>> +        return error;
>>      }
>>
>>      if (!msg->size) {
>>          return EOF;
>>      }
>>
>> -    nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
>> -    if (!nfmr) {
>> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
>> -                     "leftover bytes at end", msg->size);
>> -        return OFPERR_OFPBRC_BAD_LEN;
>> -    }
>> +    switch ((int) raw) {
>> +    case OFPRAW_NXST_FLOW_MONITOR_REQUEST: {
>> +        struct nx_flow_monitor_request *nfmr;
>>
>> -    flags = ntohs(nfmr->flags);
>> -    if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
>> -        || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
>> -                     | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
>> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
>> flags);
>> -        return OFPERR_OFPMOFC_BAD_FLAGS;
>> +        nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
>> +        if (!nfmr) {
>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
>> +                    "leftover bytes at end", msg->size);
>> +            return OFPERR_OFPBRC_BAD_LEN;
>> +        }
>> +
>> +        flags = ntohs(nfmr->flags);
>> +        if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
>> +            || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
>> +                         | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
>> +                         flags);
>> +            return OFPERR_OFPMOFC_BAD_FLAGS;
>> +        }
>> +
>> +        if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
>> +            return OFPERR_NXBRC_MUST_BE_ZERO;
>> +        }
>> +
>> +        rq->id = ntohl(nfmr->id);
>> +        rq->command = OFPFMC_ADD;
>> +        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
>> +        rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
>> +        rq->table_id = nfmr->table_id;
>> +        rq->out_group = OFPG_ANY;
>> +
>> +        return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match,
>> NULL,
>> +                NULL, false, NULL, NULL);
>>      }
>> +    case OFPRAW_ONFST13_FLOW_MONITOR_REQUEST: {
>> +        struct onf_flow_monitor_request *ofmr;
>> +
>> +        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
>> +        if (!ofmr) {
>> +            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR request has %"PRIu32" "
>> +                         "leftover bytes at end", msg->size);
>> +            return OFPERR_OFPBRC_BAD_LEN;
>> +        }
>> +
>> +        flags = ntohs(ofmr->flags);
>> +        if (!(flags & (ONFFMF_ADD | ONFFMF_DELETE | ONFFMF_MODIFY))
>> +            || flags & ~(ONFFMF_INITIAL | ONFFMF_ADD | ONFFMF_DELETE
>> +                         | ONFFMF_MODIFY | ONFFMF_ACTIONS | ONFFMF_OWN))
>> {
>> +            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR has bad flags
>> %#"PRIx16,
>> +                         flags);
>> +            return OFPERR_OFPMOFC_BAD_FLAGS;
>> +        }
>> +
>> +        if (!is_all_zeros(ofmr->zeros, sizeof ofmr->zeros)) {
>> +            return OFPERR_NXBRC_MUST_BE_ZERO;
>> +        }
>> +
>> +        rq->id = ntohl(ofmr->id);
>> +        rq->command = OFPFMC_ADD;
>> +        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
>> +        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
>> +        if (error) {
>> +            return error;
>> +        }
>> +        rq->table_id = ofmr->table_id;
>> +        rq->out_group = OFPG_ANY;
>>
>> -    if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
>> -        return OFPERR_NXBRC_MUST_BE_ZERO;
>> +        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
>> NULL);
>>      }
>> +    case OFPRAW_OFPST14_FLOW_MONITOR_REQUEST: {
>> +        struct ofp14_flow_monitor_request *ofmr;
>> +
>> +        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
>> +        if (!ofmr) {
>> +            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR request has %"PRIu32" "
>> +                    "leftover bytes at end", msg->size);
>> +            return OFPERR_OFPBRC_BAD_LEN;
>> +        }
>>
>> -    rq->id = ntohl(nfmr->id);
>> -    rq->flags = flags;
>> -    rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
>> -    rq->table_id = nfmr->table_id;
>> +        flags = ntohs(ofmr->flags);
>> +        rq->id = ntohl(ofmr->monitor_id);
>> +        rq->command = ofmr->command;
>> +
>> +        if (ofmr->command == OFPFMC_DELETE) {
>> +            return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
>> NULL);
>> +        }
>>
>> -    return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
>> -                         NULL, false, NULL, NULL);
>> +        if (!(flags & (OFPFMF_ADD | OFPFMF_REMOVED | OFPFMF_MODIFY))
>> +                || flags & ~(OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED
>> +                    | OFPFMF_MODIFY | OFPFMF_INSTRUCTIONS |
>> OFPFMF_ONLY_OWN)) {
>> +            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR has bad flags
>> %#"PRIx16,
>> +                         flags);
>> +            return OFPERR_OFPMOFC_BAD_FLAGS;
>> +        }
>> +
>> +        rq->command = ofmr->command;
>> +        rq->flags = flags;
>> +        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
>> +        if (error) {
>> +            return error;
>> +        }
>> +        rq->out_group = ntohl(ofmr->out_group);
>> +        rq->table_id = ofmr->table_id;
>> +
>> +        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
>> NULL);
>> +    }
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>>  }
>>
>>  void
>> @@ -389,66 +567,143 @@ ofputil_append_flow_monitor_request(
>>      const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg,
>>      enum ofputil_protocol protocol)
>>  {
>> -    struct nx_flow_monitor_request *nfmr;
>>      size_t start_ofs;
>>      int match_len;
>>      enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>>
>>      if (!msg->size) {
>> -        ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version, msg);
>> -    }
>> +        switch (version) {
>> +        case OFP10_VERSION:
>> +        case OFP11_VERSION:
>> +        case OFP12_VERSION: {
>> +            struct nx_flow_monitor_request *nfmr;
>> +
>> +            if (!msg->size) {
>> +                ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version,
>> msg);
>> +            }
>>
>> -    start_ofs = msg->size;
>> -    ofpbuf_put_zeros(msg, sizeof *nfmr);
>> -    match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
>> -
>> -    nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
>> -    nfmr->id = htonl(rq->id);
>> -    nfmr->flags = htons(rq->flags);
>> -    nfmr->out_port = htons(ofp_to_u16(rq->out_port));
>> -    nfmr->match_len = htons(match_len);
>> -    nfmr->table_id = rq->table_id;
>> +            start_ofs = msg->size;
>> +            ofpbuf_put_zeros(msg, sizeof *nfmr);
>> +            match_len = nx_put_match(msg, &rq->match, htonll(0),
>> htonll(0));
>> +
>> +            nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
>> +            nfmr->id = htonl(rq->id);
>> +            nfmr->flags = htons(ofp_to_nx_flow_monitor_flags(rq->flags));
>> +            nfmr->out_port = htons(ofp_to_u16(rq->out_port));
>> +            nfmr->match_len = htons(match_len);
>> +            nfmr->table_id = rq->table_id;
>> +            break;
>> +        }
>> +        case OFP13_VERSION: {
>> +            struct onf_flow_monitor_request *ofmr;
>> +
>> +            if (!msg->size) {
>> +                ofpraw_put(OFPRAW_ONFST13_FLOW_MONITOR_REQUEST, version,
>> msg);
>> +            }
>> +
>> +            start_ofs = msg->size;
>> +            ofpbuf_put_zeros(msg, sizeof *ofmr);
>> +            match_len = oxm_put_match(msg, &rq->match, version);
>> +
>> +            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
>> +            ofmr->id = htonl(rq->id);
>> +            ofmr->flags = htons(ofp_to_nx_flow_monitor_flags(rq->flags));
>> +            ofmr->match_len = htons(match_len);
>> +            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
>> +            ofmr->table_id = rq->table_id;
>> +            break;
>> +        }
>> +        case OFP14_VERSION:
>> +        case OFP15_VERSION: {
>> +            struct ofp14_flow_monitor_request *ofmr;
>> +
>> +            if (!msg->size) {
>> +                ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version,
>> msg);
>> +            }
>> +
>> +            start_ofs = msg->size;
>> +            ofpbuf_put_zeros(msg, sizeof *ofmr);
>> +            oxm_put_match(msg, &rq->match, version);
>> +
>> +            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
>> +            ofmr->monitor_id = htonl(rq->id);
>> +            ofmr->command = OFPFMC_ADD;
>> +            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
>> +            ofmr->out_group = htonl(rq->out_group);
>> +            ofmr->flags = htons(rq->flags);
>> +            ofmr->table_id = rq->table_id;
>> +            break;
>> +        }
>> +        default:
>> +            OVS_NOT_REACHED();
>> +        }
>> +    }
>>  }
>>
>>  static const char *
>> -nx_flow_monitor_flags_to_name(uint32_t bit)
>> +ofp_flow_monitor_flags_to_name(uint32_t bit)
>>  {
>> -    enum nx_flow_monitor_flags fmf = bit;
>> +    enum ofp14_flow_monitor_flags fmf = bit;
>>
>>      switch (fmf) {
>> -    case NXFMF_INITIAL: return "initial";
>> -    case NXFMF_ADD: return "add";
>> -    case NXFMF_DELETE: return "delete";
>> -    case NXFMF_MODIFY: return "modify";
>> -    case NXFMF_ACTIONS: return "actions";
>> -    case NXFMF_OWN: return "own";
>> +    case OFPFMF_INITIAL: return "initial";
>> +    case OFPFMF_ADD: return "add";
>> +    case OFPFMF_REMOVED: return "delete";
>> +    case OFPFMF_MODIFY: return "modify";
>> +    case OFPFMF_INSTRUCTIONS: return "actions";
>> +    case OFPFMF_NO_ABBREV: return "no-abbrev";
>> +    case OFPFMF_ONLY_OWN: return "own";
>>      }
>>
>>      return NULL;
>>  }
>>
>> +static const char *
>> +ofp_flow_monitor_command_to_string(enum ofp14_flow_monitor_command
>> command)
>> +{
>> +    switch (command) {
>> +    case OFPFMC_ADD: return "add";
>> +    case OFPFMC_MODIFY: return "modify";
>> +    case OFPFMC_DELETE: return "delete";
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>> +}
>> +
>>  void
>>  ofputil_flow_monitor_request_format(
>>      struct ds *s, const struct ofputil_flow_monitor_request *request,
>>      const struct ofputil_port_map *port_map,
>>      const struct ofputil_table_map *table_map)
>>  {
>> +    if (request->command == OFPFMC_DELETE) {
>> +        ds_put_format(s, "\n id=%"PRIu32" command=%s", request->id,
>> +
>> ofp_flow_monitor_command_to_string(request->command));
>> +        return;
>> +    }
>>      ds_put_format(s, "\n id=%"PRIu32" flags=", request->id);
>> -    ofp_print_bit_names(s, request->flags,
>> nx_flow_monitor_flags_to_name, ',');
>> +    ofp_print_bit_names(s, request->flags,
>> +                        ofp_flow_monitor_flags_to_name, ',');
>>
>>      if (request->out_port != OFPP_NONE) {
>>          ds_put_cstr(s, " out_port=");
>>          ofputil_format_port(request->out_port, port_map, s);
>>      }
>>
>> +    if (request->out_group && (request->out_group != OFPG_ANY)) {
>> +        ds_put_format(s, " out_group=%d", request->out_group);
>> +    }
>> +
>>      if (request->table_id != 0xff) {
>>          ds_put_format(s, " table=");
>>          ofputil_format_table(request->table_id, table_map, s);
>>      }
>>
>> -    ds_put_char(s, ' ');
>> -    match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
>> -    ds_chomp(s, ' ');
>> +    if (request->command != OFPFMC_DELETE) {
>> +        ds_put_char(s, ' ');
>> +        match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
>> +        ds_chomp(s, ' ');
>> +    }
>>  }
>>
>>  static char * OVS_WARN_UNUSED_RESULT
>> @@ -464,9 +719,10 @@ parse_flow_monitor_request__(struct
>> ofputil_flow_monitor_request *fmr,
>>
>>      fmr->id = atomic_count_inc(&id);
>>
>> -    fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
>> -                  | NXFMF_OWN | NXFMF_ACTIONS);
>> +    fmr->flags = (OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED |
>> OFPFMF_MODIFY
>> +                  | OFPFMF_ONLY_OWN | OFPFMF_INSTRUCTIONS);
>>      fmr->out_port = OFPP_NONE;
>> +    fmr->out_group = OFPG_ANY;
>>      fmr->table_id = 0xff;
>>      match_init_catchall(&fmr->match);
>>
>> @@ -476,17 +732,19 @@ parse_flow_monitor_request__(struct
>> ofputil_flow_monitor_request *fmr,
>>          char *error = NULL;
>>
>>          if (!strcmp(name, "!initial")) {
>> -            fmr->flags &= ~NXFMF_INITIAL;
>> +            fmr->flags &= ~OFPFMF_INITIAL;
>>          } else if (!strcmp(name, "!add")) {
>> -            fmr->flags &= ~NXFMF_ADD;
>> +            fmr->flags &= ~OFPFMF_ADD;
>>          } else if (!strcmp(name, "!delete")) {
>> -            fmr->flags &= ~NXFMF_DELETE;
>> +            fmr->flags &= ~OFPFMF_REMOVED;
>>          } else if (!strcmp(name, "!modify")) {
>> -            fmr->flags &= ~NXFMF_MODIFY;
>> +            fmr->flags &= ~OFPFMF_MODIFY;
>>          } else if (!strcmp(name, "!actions")) {
>> -            fmr->flags &= ~NXFMF_ACTIONS;
>> +            fmr->flags &= ~OFPFMF_INSTRUCTIONS;
>> +        } else if (!strcmp(name, "!abbrev")) {
>> +            fmr->flags &= ~OFPFMF_NO_ABBREV;
>>          } else if (!strcmp(name, "!own")) {
>> -            fmr->flags &= ~NXFMF_OWN;
>> +            fmr->flags &= ~OFPFMF_ONLY_OWN;
>>          } else if (ofp_parse_protocol(name, &p)) {
>>              match_set_dl_type(&fmr->match, htons(p->dl_type));
>>              if (p->nw_proto) {
>> @@ -511,6 +769,8 @@ parse_flow_monitor_request__(struct
>> ofputil_flow_monitor_request *fmr,
>>                  }
>>              } else if (!strcmp(name, "out_port")) {
>>                  fmr->out_port = u16_to_ofp(atoi(value));
>> +            } else if (!strcmp(name, "out_group")) {
>> +                fmr->out_group = atoi(value);
>>              } else {
>>                  return xasprintf("%s: unknown keyword %s", str_, name);
>>              }
>> @@ -562,101 +822,226 @@ int
>>  ofputil_decode_flow_update(struct ofputil_flow_update *update,
>>                             struct ofpbuf *msg, struct ofpbuf *ofpacts)
>>  {
>> -    struct nx_flow_update_header *nfuh;
>>      unsigned int length;
>>      struct ofp_header *oh;
>> +    enum ofperr error;
>> +    enum ofpraw raw;
>>
>>      if (!msg->header) {
>>          ofpraw_pull_assert(msg);
>>      }
>>
>> +    error = ofpraw_decode(&raw, msg->header);
>> +    if (error) {
>> +        return error;
>> +    }
>> +
>>      ofpbuf_clear(ofpacts);
>>      if (!msg->size) {
>>          return EOF;
>>      }
>>
>> -    if (msg->size < sizeof(struct nx_flow_update_header)) {
>> -        goto bad_len;
>> -    }
>> -
>>      oh = msg->header;
>>
>> -    nfuh = msg->data;
>> -    update->event = ntohs(nfuh->event);
>> -    length = ntohs(nfuh->length);
>> -    if (length > msg->size || length % 8) {
>> -        goto bad_len;
>> -    }
>> +    switch ((int) raw) {
>> +    case OFPRAW_ONFST13_FLOW_MONITOR_REPLY:
>> +    case OFPRAW_NXST_FLOW_MONITOR_REPLY: {
>> +        struct nx_flow_update_header *nfuh;
>>
>> -    if (update->event == NXFME_ABBREV) {
>> -        struct nx_flow_update_abbrev *nfua;
>> +        if (msg->size < sizeof(struct nx_flow_update_header)) {
>> +            goto bad_len;
>> +        }
>>
>> -        if (length != sizeof *nfua) {
>> +        nfuh = msg->data;
>> +        update->event = nx_to_ofp_flow_update_event(ntohs(nfuh->event));
>> +        length = ntohs(nfuh->length);
>> +        if (length > msg->size || length % 8) {
>>              goto bad_len;
>>          }
>>
>> -        nfua = ofpbuf_pull(msg, sizeof *nfua);
>> -        update->xid = nfua->xid;
>> -        return 0;
>> -    } else if (update->event == NXFME_ADDED
>> -               || update->event == NXFME_DELETED
>> -               || update->event == NXFME_MODIFIED) {
>> -        struct nx_flow_update_full *nfuf;
>> -        unsigned int actions_len;
>> -        unsigned int match_len;
>> -        enum ofperr error;
>> +        if (update->event == OFPFME_ABBREV) {
>> +            struct nx_flow_update_abbrev *nfua;
>> +
>> +            if (length != sizeof *nfua) {
>> +                goto bad_len;
>> +            }
>> +
>> +            nfua = ofpbuf_pull(msg, sizeof *nfua);
>> +            update->xid = nfua->xid;
>> +            return 0;
>> +        } else if (update->event == OFPFME_ADDED
>> +                   || update->event == OFPFME_REMOVED
>> +                   || update->event == OFPFME_MODIFIED) {
>> +            struct nx_flow_update_full *nfuf;
>> +            unsigned int actions_len;
>> +            unsigned int match_len;
>> +
>> +            if (length < sizeof *nfuf) {
>> +                goto bad_len;
>> +            }
>>
>> -        if (length < sizeof *nfuf) {
>> +            nfuf = ofpbuf_pull(msg, sizeof *nfuf);
>> +            match_len = ntohs(nfuf->match_len);
>> +            if (sizeof *nfuf + match_len > length) {
>> +                goto bad_len;
>> +            }
>> +
>> +            update->reason = ntohs(nfuf->reason);
>> +            update->idle_timeout = ntohs(nfuf->idle_timeout);
>> +            update->hard_timeout = ntohs(nfuf->hard_timeout);
>> +            update->table_id = nfuf->table_id;
>> +            update->cookie = nfuf->cookie;
>> +            update->priority = ntohs(nfuf->priority);
>> +
>> +            if (raw == OFPRAW_ONFST13_FLOW_MONITOR_REPLY) {
>> +                uint16_t padded_match_len = 0;
>> +                unsigned int instructions_len;
>> +
>> +                error = ofputil_pull_ofp11_match(
>> +                    msg, NULL, NULL, &update->match, &padded_match_len);
>> +                if (error) {
>> +                    return error;
>> +                }
>> +
>> +                instructions_len = length - sizeof *nfuf -
>> padded_match_len;
>> +                error = ofpacts_pull_openflow_instructions(
>> +                    msg, instructions_len, oh->version, NULL, NULL,
>> ofpacts);
>> +                if (error) {
>> +                    return error;
>> +                }
>> +            } else {
>> +                error = nx_pull_match(msg, match_len, &update->match,
>> NULL,
>> +                                      NULL, false, NULL, NULL);
>> +                if (error) {
>> +                    return error;
>> +                }
>> +
>> +                actions_len = length - sizeof *nfuf -
>> ROUND_UP(match_len, 8);
>> +                error = ofpacts_pull_openflow_actions(
>> +                    msg, actions_len, oh->version, NULL, NULL, ofpacts);
>> +                if (error) {
>> +                    return error;
>> +                }
>> +            }
>> +
>> +            update->ofpacts = ofpacts->data;
>> +            update->ofpacts_len = ofpacts->size;
>> +            return 0;
>> +        } else {
>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
>> %"PRIu16,
>> +                         ntohs(nfuh->event));
>> +            return OFPERR_NXBRC_FM_BAD_EVENT;
>> +        }
>> +    }
>> +    case OFPRAW_OFPST14_FLOW_MONITOR_REPLY: {
>> +        struct ofp_flow_update_header *ofuh;
>> +        uint16_t padded_match_len = 0;
>> +
>> +        if (msg->size < sizeof(struct ofp_flow_update_header)) {
>>              goto bad_len;
>>          }
>>
>> -        nfuf = ofpbuf_pull(msg, sizeof *nfuf);
>> -        match_len = ntohs(nfuf->match_len);
>> -        if (sizeof *nfuf + match_len > length) {
>> +        ofuh = msg->data;
>> +        update->event = ntohs(ofuh->event);
>> +        length = ntohs(ofuh->length);
>> +        if (length > msg->size || length % 8) {
>>              goto bad_len;
>>          }
>>
>> -        update->reason = ntohs(nfuf->reason);
>> -        update->idle_timeout = ntohs(nfuf->idle_timeout);
>> -        update->hard_timeout = ntohs(nfuf->hard_timeout);
>> -        update->table_id = nfuf->table_id;
>> -        update->cookie = nfuf->cookie;
>> -        update->priority = ntohs(nfuf->priority);
>> +        if (update->event == OFPFME_ABBREV) {
>> +            struct ofp_flow_update_abbrev *ofua;
>>
>> -        error = nx_pull_match(msg, match_len, &update->match, NULL, NULL,
>> -                              false, NULL, NULL);
>> -        if (error) {
>> -            return error;
>> -        }
>> +            if (length != sizeof *ofua) {
>> +                goto bad_len;
>> +            }
>>
>> -        actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
>> -        error = ofpacts_pull_openflow_actions(msg, actions_len,
>> oh->version,
>> -                                              NULL, NULL, ofpacts);
>> -        if (error) {
>> -            return error;
>> -        }
>> +            ofua = ofpbuf_pull(msg, sizeof *ofua);
>> +            update->xid = ofua->xid;
>> +            return 0;
>> +        } else if (update->event == OFPFME_PAUSED
>> +                   || update->event == OFPFME_RESUMED) {
>> +            struct ofp_flow_update_paused *ofup;
>>
>> -        update->ofpacts = ofpacts->data;
>> -        update->ofpacts_len = ofpacts->size;
>> -        return 0;
>> -    } else {
>> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
>> %"PRIu16,
>> -                     ntohs(nfuh->event));
>> -        return OFPERR_NXBRC_FM_BAD_EVENT;
>> -    }
>> +            if (length != sizeof *ofup) {
>> +                goto bad_len;
>> +            }
>> +
>> +            ofup = ofpbuf_pull(msg, sizeof *ofup);
>> +            return 0;
>> +        } else if (update->event == OFPFME_INITIAL
>> +                   || update->event == OFPFME_ADDED
>> +                   || update->event == OFPFME_REMOVED
>> +                   || update->event == OFPFME_MODIFIED) {
>> +            struct ofp_flow_update_full *ofuf;
>> +            unsigned int instructions_len;
>> +
>> +            if (length < sizeof *ofuf) {
>> +                goto bad_len;
>> +            }
>>
>> +            ofuf = ofpbuf_pull(msg, sizeof *ofuf);
>> +            if (sizeof *ofuf > length) {
>> +                goto bad_len;
>> +            }
>> +
>> +            update->reason = ofuf->reason;
>> +            update->idle_timeout = ntohs(ofuf->idle_timeout);
>> +            update->hard_timeout = ntohs(ofuf->hard_timeout);
>> +            update->table_id = ofuf->table_id;
>> +            update->cookie = ofuf->cookie;
>> +            update->priority = ntohs(ofuf->priority);
>> +
>> +            error = ofputil_pull_ofp11_match(
>> +                msg, NULL, NULL, &update->match, &padded_match_len);
>> +            if (error) {
>> +                return error;
>> +            }
>> +
>> +            instructions_len = length - sizeof *ofuf - padded_match_len;
>> +            error = ofpacts_pull_openflow_instructions(
>> +                msg, instructions_len, oh->version, NULL, NULL, ofpacts);
>> +            if (error) {
>> +                return error;
>> +            }
>> +
>> +            update->ofpacts = ofpacts->data;
>> +            update->ofpacts_len = ofpacts->size;
>> +            return 0;
>> +        } else {
>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
>> %"PRIu16,
>> +                         ntohs(ofuh->event));
>> +            return OFPERR_NXBRC_FM_BAD_EVENT;
>> +        }
>> +    }
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>>  bad_len:
>> -    VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has %"PRIu32" "
>> -                 "leftover bytes at end", msg->size);
>> +    VLOG_WARN_RL(&rl, "%s reply has %"PRIu32" leftover bytes at end",
>> +                ofpraw_get_name(raw), msg->size);
>>      return OFPERR_OFPBRC_BAD_LEN;
>>  }
>>
>>  uint32_t
>>  ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
>>  {
>> -    const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
>> +    enum ofperr error;
>> +    enum ofpraw raw;
>>
>> -    return ntohl(cancel->id);
>> +    error = ofpraw_decode(&raw, oh);
>> +    if (error) {
>> +        return error;
>> +    }
>> +
>> +    switch ((int) raw) {
>> +    case OFPRAW_ONFT13_FLOW_MONITOR_CANCEL:
>> +    case OFPRAW_NXT_FLOW_MONITOR_CANCEL: {
>> +        const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
>> +        return ntohl(cancel->id);
>> +    }
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>>  }
>>
>>  struct ofpbuf *
>> @@ -666,9 +1051,99 @@ ofputil_encode_flow_monitor_cancel(uint32_t id,
>> enum ofputil_protocol protocol)
>>      enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>>      struct ofpbuf *msg;
>>
>> -    msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version, 0);
>> -    nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
>> -    nfmc->id = htonl(id);
>> +    switch (version) {
>> +    case OFP10_VERSION:
>> +    case OFP11_VERSION:
>> +    case OFP12_VERSION:
>> +    case OFP13_VERSION: {
>> +        if (version == OFP13_VERSION) {
>> +            msg = ofpraw_alloc(OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
>> version, 0);
>> +        } else {
>> +            msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version,
>> 0);
>> +        }
>> +        nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
>> +        nfmc->id = htonl(id);
>> +        break;
>> +    }
>> +    case OFP14_VERSION:
>> +    case OFP15_VERSION: {
>> +        struct ofp14_flow_monitor_request *ofmr;
>> +
>> +        msg = ofpbuf_new(0);
>> +
>> +        ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version, msg);
>> +
>> +        size_t start_ofs = msg->size;
>> +        ofpbuf_put_zeros(msg, sizeof *ofmr);
>> +
>> +        ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
>> +        ofmr->monitor_id = htonl(id);
>> +        ofmr->command = OFPFMC_DELETE;
>> +        break;
>> +    }
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>> +    return msg;
>> +}
>> +
>> +struct ofpbuf *
>> +ofputil_encode_flow_monitor_pause(enum ofp_flow_update_event command,
>> +                                  enum ofputil_protocol protocol)
>> +{
>> +    struct ofpbuf *msg;
>> +    enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>> +
>> +    if (!(command == OFPFME_PAUSED || command == OFPFME_RESUMED)) {
>> +        OVS_NOT_REACHED();
>> +    }
>> +
>> +    switch (version) {
>> +    case OFP10_VERSION:
>> +    case OFP11_VERSION:
>> +    case OFP12_VERSION:
>> +        if (command == OFPFME_PAUSED) {
>> +            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>> +                                   version, htonl(0), 0);
>> +        } else {
>> +            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>> +                                   version, htonl(0), 0);
>> +        }
>> +        break;
>> +    case OFP13_VERSION:
>> +        if (command == OFPFME_PAUSED) {
>> +            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
>> +                                   version, htonl(0), 0);
>> +        } else {
>> +            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
>> +                                   version, htonl(0), 0);
>> +        }
>> +        break;
>> +    case OFP14_VERSION:
>> +    case OFP15_VERSION: {
>> +        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
>> version,
>> +                                                   htonl(0), 1024);
>> +        struct ofp_flow_update_header *ofuh;
>> +        size_t start_ofs = msg->size;
>> +
>> +        struct ofp_flow_update_paused *ofup;
>> +
>> +        ofpbuf_put_zeros(msg, sizeof *ofup);
>> +        ofup = ofpbuf_at_assert(msg, start_ofs, sizeof *ofup);
>> +        ofup->event = htons(command);
>> +        ofup->length = htons(8);
>> +
>> +        ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
>> +        ofuh->length = htons(msg->size - start_ofs);
>> +        ofuh->event = htons(command);
>> +
>> +        ofpmsg_update_length(msg);
>> +        break;
>> +    }
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>> +
>>      return msg;
>>  }
>>
>> @@ -679,8 +1154,25 @@ ofputil_start_flow_update(struct ovs_list *replies,
>>      struct ofpbuf *msg;
>>      enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
>>
>> -    msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
>> -                           htonl(0), 1024);
>> +    switch (version) {
>> +    case OFP10_VERSION:
>> +    case OFP11_VERSION:
>> +    case OFP12_VERSION:
>> +        msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
>> +                               htonl(0), 1024);
>> +        break;
>> +    case OFP13_VERSION:
>> +        msg = ofpraw_alloc_xid(OFPRAW_ONFST13_FLOW_MONITOR_REPLY,
>> version,
>> +                               htonl(0), 1024);
>> +        break;
>> +    case OFP14_VERSION:
>> +    case OFP15_VERSION:
>> +        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
>> version,
>> +                               htonl(0), 1024);
>> +        break;
>> +    default:
>> +        OVS_NOT_REACHED();
>> +    }
>>
>>      ovs_list_init(replies);
>>      ovs_list_push_back(replies, &msg->list_node);
>> @@ -695,7 +1187,6 @@ ofputil_append_flow_update(const struct
>> ofputil_flow_update *update,
>>          CONST_CAST(struct ofputil_flow_update *, update);
>>      const struct tun_table *orig_tun_table;
>>      enum ofp_version version = ofpmp_version(replies);
>> -    struct nx_flow_update_header *nfuh;
>>      struct ofpbuf *msg;
>>      size_t start_ofs;
>>
>> @@ -705,32 +1196,80 @@ ofputil_append_flow_update(const struct
>> ofputil_flow_update *update,
>>      msg = ofpbuf_from_list(ovs_list_back(replies));
>>      start_ofs = msg->size;
>>
>> -    if (update->event == NXFME_ABBREV) {
>> -        struct nx_flow_update_abbrev *nfua;
>> +    switch (version) {
>> +        case OFP10_VERSION:
>> +        case OFP11_VERSION:
>> +        case OFP12_VERSION:
>> +        case OFP13_VERSION: {
>> +             struct nx_flow_update_header *nfuh;
>>
>> -        nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
>> -        nfua->xid = update->xid;
>> -    } else {
>> -        struct nx_flow_update_full *nfuf;
>> -        int match_len;
>> +            if (update->event == OFPFME_ABBREV) {
>> +                struct nx_flow_update_abbrev *nfua;
>>
>> -        ofpbuf_put_zeros(msg, sizeof *nfuf);
>> -        match_len = nx_put_match(msg, &update->match, htonll(0),
>> htonll(0));
>> -        ofpacts_put_openflow_actions(update->ofpacts,
>> update->ofpacts_len, msg,
>> -                                     version);
>> -        nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
>> -        nfuf->reason = htons(update->reason);
>> -        nfuf->priority = htons(update->priority);
>> -        nfuf->idle_timeout = htons(update->idle_timeout);
>> -        nfuf->hard_timeout = htons(update->hard_timeout);
>> -        nfuf->match_len = htons(match_len);
>> -        nfuf->table_id = update->table_id;
>> -        nfuf->cookie = update->cookie;
>> -    }
>> +                nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
>> +                nfua->xid = update->xid;
>> +            } else {
>> +                struct nx_flow_update_full *nfuf;
>> +                int match_len;
>> +
>> +                ofpbuf_put_zeros(msg, sizeof *nfuf);
>> +                if (version == OFP13_VERSION) {
>> +                    match_len = oxm_put_match(msg, &update->match,
>> version);
>> +                    ofpacts_put_openflow_instructions(
>> +                        update->ofpacts, update->ofpacts_len, msg,
>> version);
>> +                } else {
>> +                    match_len = nx_put_match(msg, &update->match,
>> +                                             htonll(0), htonll(0));
>> +                    ofpacts_put_openflow_actions(
>> +                        update->ofpacts, update->ofpacts_len, msg,
>> version);
>> +                }
>> +                nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
>> +                nfuf->reason = htons(update->reason);
>> +                nfuf->priority = htons(update->priority);
>> +                nfuf->idle_timeout = htons(update->idle_timeout);
>> +                nfuf->hard_timeout = htons(update->hard_timeout);
>> +                nfuf->match_len = htons(match_len);
>> +                nfuf->table_id = update->table_id;
>> +                nfuf->cookie = update->cookie;
>> +            }
>> +
>> +            nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
>> +            nfuh->length = htons(msg->size - start_ofs);
>> +            nfuh->event =
>> htons(ofp_to_nx_flow_update_event(update->event));
>> +            break;
>> +        }
>> +        case OFP14_VERSION:
>> +        case OFP15_VERSION: {
>> +            struct ofp_flow_update_header *ofuh;
>>
>> -    nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
>> -    nfuh->length = htons(msg->size - start_ofs);
>> -    nfuh->event = htons(update->event);
>> +            if (update->event == OFPFME_ABBREV) {
>> +                struct ofp_flow_update_abbrev *ofua;
>> +
>> +                ofua = ofpbuf_put_zeros(msg, sizeof *ofua);
>> +                ofua->xid = update->xid;
>> +            } else {
>> +                struct ofp_flow_update_full *ofuf;
>> +
>> +                ofpbuf_put_zeros(msg, sizeof *ofuf);
>> +                oxm_put_match(msg, &update->match, version);
>> +                ofpacts_put_openflow_instructions(update->ofpacts,
>> +                                                  update->ofpacts_len,
>> +                                                  msg, version);
>> +                ofuf = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuf);
>> +                ofuf->reason = update->reason;
>> +                ofuf->priority = htons(update->priority);
>> +                ofuf->idle_timeout = htons(update->idle_timeout);
>> +                ofuf->hard_timeout = htons(update->hard_timeout);
>> +                ofuf->table_id = update->table_id;
>> +                ofuf->cookie = update->cookie;
>> +            }
>> +
>> +            ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
>> +            ofuh->length = htons(msg->size - start_ofs);
>> +            ofuh->event = htons(update->event);
>> +            break;
>> +        }
>> +    }
>>
>>      ofpmp_postappend(replies, start_ofs);
>>      update_->match.flow.tunnel.metadata.tab = orig_tun_table;
>> @@ -746,24 +1285,37 @@ ofputil_flow_update_format(struct ds *s,
>>
>>      ds_put_cstr(s, "\n event=");
>>      switch (update->event) {
>> -    case NXFME_ADDED:
>> +    case OFPFME_INITIAL:
>> +        ds_put_cstr(s, "INITIAL");
>> +        break;
>> +
>> +    case OFPFME_ADDED:
>>          ds_put_cstr(s, "ADDED");
>>          break;
>>
>> -    case NXFME_DELETED:
>> +    case OFPFME_REMOVED:
>>          ds_put_format(s, "DELETED reason=%s",
>>                        ofp_flow_removed_reason_to_string(update->reason,
>>                                                          reasonbuf,
>>                                                          sizeof
>> reasonbuf));
>>          break;
>>
>> -    case NXFME_MODIFIED:
>> +    case OFPFME_MODIFIED:
>>          ds_put_cstr(s, "MODIFIED");
>>          break;
>>
>> -    case NXFME_ABBREV:
>> +    case OFPFME_ABBREV:
>>          ds_put_format(s, "ABBREV xid=0x%"PRIx32, ntohl(update->xid));
>>          return;
>> +
>> +    case OFPFME_PAUSED:
>> +        ds_put_cstr(s, "PAUSED");
>> +        return;
>> +
>> +    case OFPFME_RESUMED:
>> +        ds_put_cstr(s, "RESUMED");
>> +        return;
>> +
>>      }
>>
>>      ds_put_format(s, " table=");
>> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
>> index b0facbf9f..bd37fa17a 100644
>> --- a/lib/ofp-print.c
>> +++ b/lib/ofp-print.c
>> @@ -744,10 +744,10 @@ ofp_print_nxt_flow_monitor_cancel(struct ds *string,
>>  }
>>
>>  static enum ofperr
>> -ofp_print_nxst_flow_monitor_request(struct ds *string,
>> -                                    const struct ofp_header *oh,
>> -                                    const struct ofputil_port_map
>> *port_map,
>> -                                    const struct ofputil_table_map
>> *table_map)
>> +ofp_print_flow_monitor_request(struct ds *string,
>> +                               const struct ofp_header *oh,
>> +                               const struct ofputil_port_map *port_map,
>> +                               const struct ofputil_table_map *table_map)
>>  {
>>      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
>>      for (;;) {
>> @@ -765,10 +765,10 @@ ofp_print_nxst_flow_monitor_request(struct ds
>> *string,
>>  }
>>
>>  static enum ofperr
>> -ofp_print_nxst_flow_monitor_reply(struct ds *string,
>> -                                  const struct ofp_header *oh,
>> -                                  const struct ofputil_port_map
>> *port_map,
>> -                                  const struct ofputil_table_map
>> *table_map)
>> +ofp_print_flow_monitor_reply(struct ds *string,
>> +                             const struct ofp_header *oh,
>> +                             const struct ofputil_port_map *port_map,
>> +                             const struct ofputil_table_map *table_map)
>>  {
>>      uint64_t ofpacts_stub[1024 / 8];
>>      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
>> @@ -1147,12 +1147,12 @@ ofp_to_string__(const struct ofp_header *oh,
>>          break;
>>
>>      case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
>> -        return ofp_print_nxst_flow_monitor_request(string, msg, port_map,
>> -                                                   table_map);
>> +        return ofp_print_flow_monitor_request(string, msg, port_map,
>> +                                              table_map);
>>
>>      case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
>> -        return ofp_print_nxst_flow_monitor_reply(string, msg, port_map,
>> -                                                 table_map);
>> +        return ofp_print_flow_monitor_reply(string, msg, port_map,
>> +                                            table_map);
>>
>>      case OFPTYPE_BUNDLE_CONTROL:
>>          return ofp_print_bundle_ctrl(string, msg);
>> diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
>> index c14834f84..325f45966 100644
>> --- a/ofproto/connmgr.c
>> +++ b/ofproto/connmgr.c
>> @@ -2099,6 +2099,7 @@ ofmonitor_create(const struct
>> ofputil_flow_monitor_request *request,
>>      m->id = request->id;
>>      m->flags = request->flags;
>>      m->out_port = request->out_port;
>> +    m->out_group = request->out_group;
>>      m->table_id = request->table_id;
>>      minimatch_init(&m->match, &request->match);
>>
>> @@ -2134,7 +2135,7 @@ ofmonitor_destroy(struct ofmonitor *m)
>>
>>  void
>>  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
>> -                 enum nx_flow_update_event event,
>> +                 enum ofp_flow_update_event event,
>>                   enum ofp_flow_removed_reason reason,
>>                   const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid,
>>                   const struct rule_actions *old_actions)
>> @@ -2144,39 +2145,42 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>> *rule,
>>          return;
>>      }
>>
>> -    enum nx_flow_monitor_flags update;
>> +    enum ofp14_flow_monitor_flags update;
>>      switch (event) {
>> -    case NXFME_ADDED:
>> -        update = NXFMF_ADD;
>> +    case OFPFME_ADDED:
>> +        update = OFPFMF_ADD;
>>          rule->add_seqno = rule->modify_seqno = monitor_seqno++;
>>          break;
>>
>> -    case NXFME_DELETED:
>> -        update = NXFMF_DELETE;
>> +    case OFPFME_REMOVED:
>> +        update = OFPFMF_REMOVED;
>>          break;
>>
>> -    case NXFME_MODIFIED:
>> -        update = NXFMF_MODIFY;
>> +    case OFPFME_MODIFIED:
>> +        update = OFPFMF_MODIFY;
>>          rule->modify_seqno = monitor_seqno++;
>>          break;
>>
>>      default:
>> -    case NXFME_ABBREV:
>> +    case OFPFME_INITIAL:
>> +    case OFPFME_PAUSED:
>> +    case OFPFME_RESUMED:
>> +    case OFPFME_ABBREV:
>>          OVS_NOT_REACHED();
>>      }
>>
>>      struct ofconn *ofconn;
>>      LIST_FOR_EACH (ofconn, connmgr_node, &mgr->conns) {
>>          if (ofconn->monitor_paused) {
>> -            /* Only send NXFME_DELETED notifications for flows that were
>> added
>> +            /* Only send OFPFME_REMOVED notifications for flows that
>> were added
>>               * before we paused. */
>> -            if (event != NXFME_DELETED
>> +            if (event != OFPFME_REMOVED
>>                  || rule->add_seqno > ofconn->monitor_paused) {
>>                  continue;
>>              }
>>          }
>>
>> -        enum nx_flow_monitor_flags flags = 0;
>> +        enum ofp14_flow_monitor_flags flags = 0;
>>          struct ofmonitor *m;
>>          HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
>>              if (m->flags & update
>> @@ -2186,6 +2190,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>> *rule,
>>                          && ofpacts_output_to_port(old_actions->ofpacts,
>>
>>  old_actions->ofpacts_len,
>>                                                    m->out_port)))
>> +                && ofproto_rule_has_out_group(rule, m->out_group)
>>                  && cls_rule_is_loose_match(&rule->cr, &m->match)) {
>>                  flags |= m->flags;
>>              }
>> @@ -2198,12 +2203,12 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>> *rule,
>>                  ofconn->sent_abbrev_update = false;
>>              }
>>
>> -            if (flags & NXFMF_OWN || ofconn != abbrev_ofconn
>> +            if (flags & OFPFMF_ONLY_OWN || ofconn != abbrev_ofconn
>>                  || ofconn->monitor_paused) {
>>                  struct ofputil_flow_update fu;
>>
>>                  fu.event = event;
>> -                fu.reason = event == NXFME_DELETED ? reason : 0;
>> +                fu.reason = event == OFPFME_REMOVED ? reason : 0;
>>                  fu.table_id = rule->table_id;
>>                  fu.cookie = rule->flow_cookie;
>>                  minimatch_expand(&rule->cr.match, &fu.match);
>> @@ -2214,7 +2219,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>> *rule,
>>                  fu.hard_timeout = rule->hard_timeout;
>>                  ovs_mutex_unlock(&rule->mutex);
>>
>> -                if (flags & NXFMF_ACTIONS) {
>> +                if (flags & OFPFMF_INSTRUCTIONS) {
>>                      const struct rule_actions *actions
>>                          = rule_get_actions(rule);
>>                      fu.ofpacts = actions->ofpacts;
>> @@ -2228,7 +2233,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>> *rule,
>>              } else if (!ofconn->sent_abbrev_update) {
>>                  struct ofputil_flow_update fu;
>>
>> -                fu.event = NXFME_ABBREV;
>> +                fu.event = OFPFME_ABBREV;
>>                  fu.xid = abbrev_xid;
>>                  ofputil_append_flow_update(&fu, &ofconn->updates,
>>
>> ofproto_get_tun_tab(rule->ofproto));
>> @@ -2263,9 +2268,8 @@ ofmonitor_flush(struct connmgr *mgr)
>>              COVERAGE_INC(ofmonitor_pause);
>>              ofconn->monitor_paused = monitor_seqno++;
>>              protocol = ofconn_get_protocol(ofconn);
>> -            struct ofpbuf *pause = ofpraw_alloc_xid(
>> -                OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>> -                ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
>> +            struct ofpbuf *pause = ofputil_encode_flow_monitor_pause(
>> +                OFPFME_PAUSED,protocol);
>>              ofconn_send(ofconn, pause, counter);
>>          }
>>      }
>> @@ -2289,9 +2293,8 @@ ofmonitor_resume(struct ofconn *ofconn)
>>                                        ofconn_get_protocol(ofconn));
>>
>>      protocol = ofconn_get_protocol(ofconn);
>> -    struct ofpbuf *resumed = ofpraw_alloc_xid(
>> -            OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>> -            ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
>> +    struct ofpbuf *resumed = ofputil_encode_flow_monitor_pause(
>> +        OFPFME_RESUMED, protocol);
>>      ovs_list_push_back(&msgs, &resumed->list_node);
>>      ofconn_send_replies(ofconn, &msgs);
>>
>> diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
>> index 56fdc3504..3471d38f9 100644
>> --- a/ofproto/connmgr.h
>> +++ b/ofproto/connmgr.h
>> @@ -168,10 +168,11 @@ struct ofmonitor {
>>      struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */
>>      uint32_t id;
>>
>> -    enum nx_flow_monitor_flags flags;
>> +    enum ofp14_flow_monitor_flags flags;
>>
>>      /* Matching. */
>>      ofp_port_t out_port;
>> +    uint32_t out_group;
>>      uint8_t table_id;
>>      struct minimatch match;
>>  };
>> @@ -187,7 +188,8 @@ void ofmonitor_destroy(struct ofmonitor *)
>>      OVS_REQUIRES(ofproto_mutex);
>>
>>  void ofmonitor_report(struct connmgr *, struct rule *,
>> -                      enum nx_flow_update_event, enum
>> ofp_flow_removed_reason,
>> +                      enum ofp_flow_update_event event,
>> +                      enum ofp_flow_removed_reason,
>>                        const struct ofconn *abbrev_ofconn, ovs_be32
>> abbrev_xid,
>>                        const struct rule_actions *old_actions)
>>      OVS_REQUIRES(ofproto_mutex);
>> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
>> index 57c7d17cb..373526738 100644
>> --- a/ofproto/ofproto-provider.h
>> +++ b/ofproto/ofproto-provider.h
>> @@ -419,7 +419,7 @@ struct rule {
>>       * 'add_seqno' is the sequence number when this rule was created.
>>       * 'modify_seqno' is the sequence number when this rule was last
>> modified.
>>       * See 'monitor_seqno' in connmgr.c for more information. */
>> -    enum nx_flow_monitor_flags monitor_flags
>> OVS_GUARDED_BY(ofproto_mutex);
>> +    enum ofp14_flow_monitor_flags monitor_flags
>> OVS_GUARDED_BY(ofproto_mutex);
>>      uint64_t add_seqno OVS_GUARDED_BY(ofproto_mutex);
>>      uint64_t modify_seqno OVS_GUARDED_BY(ofproto_mutex);
>>
>> @@ -480,6 +480,8 @@ const struct rule_actions *rule_actions_create(const
>> struct ofpact *, size_t);
>>  void rule_actions_destroy(const struct rule_actions *);
>>  bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port)
>>      OVS_REQUIRES(ofproto_mutex);
>> +bool ofproto_rule_has_out_group(const struct rule *rule, uint32_t
>> group_id)
>> +    OVS_REQUIRES(ofproto_mutex);
>>
>>  #define DECL_OFPROTO_COLLECTION(TYPE, NAME)                             \
>>      DECL_OBJECT_COLLECTION(TYPE, NAME)                                  \
>> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
>> index 11aadbf20..980d4d53b 100644
>> --- a/ofproto/ofproto.c
>> +++ b/ofproto/ofproto.c
>> @@ -3157,7 +3157,7 @@ ofproto_rule_has_out_port(const struct rule *rule,
>> ofp_port_t port)
>>  }
>>
>>  /* Returns true if 'rule' has group and equals group_id. */
>> -static bool
>> +bool
>>  ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
>>      OVS_REQUIRES(ofproto_mutex)
>>  {
>> @@ -5215,7 +5215,7 @@ add_flow_finish(struct ofproto *ofproto, struct
>> ofproto_flow_mod *ofm,
>>      if (old_rule) {
>>          ovsrcu_postpone(remove_rule_rcu, old_rule);
>>      } else {
>> -        ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0,
>> +        ofmonitor_report(ofproto->connmgr, new_rule, OFPFME_ADDED, 0,
>>                           req ? req->ofconn : NULL,
>>                           req ? req->request->xid : 0, NULL);
>>
>> @@ -5630,8 +5630,8 @@ replace_rule_finish(struct ofproto *ofproto, struct
>> ofproto_flow_mod *ofm,
>>          learned_cookies_dec(ofproto, old_actions, dead_cookies);
>>
>>          if (replaced_rule) {
>> -            enum nx_flow_update_event event = ofm->command == OFPFC_ADD
>> -                ? NXFME_ADDED : NXFME_MODIFIED;
>> +            enum ofp_flow_update_event event = ofm->command == OFPFC_ADD
>> +                ? OFPFME_ADDED : OFPFME_MODIFIED;
>>
>>              bool changed_cookie = (new_rule->flow_cookie
>>                                     != old_rule->flow_cookie);
>> @@ -5641,7 +5641,7 @@ replace_rule_finish(struct ofproto *ofproto, struct
>> ofproto_flow_mod *ofm,
>>                                                    old_actions->ofpacts,
>>
>>  old_actions->ofpacts_len);
>>
>> -            if (event != NXFME_MODIFIED || changed_actions
>> +            if (event != OFPFME_MODIFIED || changed_actions
>>                  || changed_cookie) {
>>                  ofmonitor_report(ofproto->connmgr, new_rule, event, 0,
>>                                   req ? req->ofconn : NULL,
>> @@ -5650,7 +5650,7 @@ replace_rule_finish(struct ofproto *ofproto, struct
>> ofproto_flow_mod *ofm,
>>              }
>>          } else {
>>              /* XXX: This is slight duplication with
>> delete_flows_finish__() */
>> -            ofmonitor_report(ofproto->connmgr, old_rule, NXFME_DELETED,
>> +            ofmonitor_report(ofproto->connmgr, old_rule, OFPFME_REMOVED,
>>                               OFPRR_EVICTION,
>>                               req ? req->ofconn : NULL,
>>                               req ? req->request->xid : 0, NULL);
>> @@ -5931,7 +5931,7 @@ delete_flows_finish__(struct ofproto *ofproto,
>>               * before the rule is actually destroyed. */
>>              rule->removed_reason = reason;
>>
>> -            ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED,
>> reason,
>> +            ofmonitor_report(ofproto->connmgr, rule, OFPFME_REMOVED,
>> reason,
>>                               req ? req->ofconn : NULL,
>>                               req ? req->request->xid : 0, NULL);
>>
>> @@ -6337,7 +6337,7 @@ handle_barrier_request(struct ofconn *ofconn, const
>> struct ofp_header *oh)
>>
>>  static void
>>  ofproto_compose_flow_refresh_update(const struct rule *rule,
>> -                                    enum nx_flow_monitor_flags flags,
>> +                                    enum ofp14_flow_monitor_flags flags,
>>                                      struct ovs_list *msgs,
>>                                      const struct tun_table *tun_table,
>>                                      enum ofputil_protocol protocol)
>> @@ -6346,8 +6346,9 @@ ofproto_compose_flow_refresh_update(const struct
>> rule *rule,
>>      const struct rule_actions *actions;
>>      struct ofputil_flow_update fu;
>>
>> -    fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
>> -                ? NXFME_ADDED : NXFME_MODIFIED);
>> +    fu.event = flags & OFPFMF_INITIAL ? OFPFME_INITIAL :
>> +               flags & OFPFMF_ADD ?
>> +                         OFPFME_ADDED : OFPFME_MODIFIED;
>>      fu.reason = 0;
>>      ovs_mutex_lock(&rule->mutex);
>>      fu.idle_timeout = rule->idle_timeout;
>> @@ -6358,7 +6359,7 @@ ofproto_compose_flow_refresh_update(const struct
>> rule *rule,
>>      minimatch_expand(&rule->cr.match, &fu.match);
>>      fu.priority = rule->cr.priority;
>>
>> -    actions = flags & NXFMF_ACTIONS ? rule_get_actions(rule) : NULL;
>> +    actions = flags & OFPFMF_INSTRUCTIONS ? rule_get_actions(rule) :
>> NULL;
>>      fu.ofpacts = actions ? actions->ofpacts : NULL;
>>      fu.ofpacts_len = actions ? actions->ofpacts_len : 0;
>>
>> @@ -6377,7 +6378,7 @@ ofmonitor_compose_refresh_updates(struct
>> rule_collection *rules,
>>      struct rule *rule;
>>
>>      RULE_COLLECTION_FOR_EACH (rule, rules) {
>> -        enum nx_flow_monitor_flags flags = rule->monitor_flags;
>> +        enum ofp14_flow_monitor_flags flags = rule->monitor_flags;
>>          rule->monitor_flags = 0;
>>
>>          ofproto_compose_flow_refresh_update(rule, flags, msgs,
>> @@ -6391,7 +6392,7 @@ ofproto_collect_ofmonitor_refresh_rule(const struct
>> ofmonitor *m,
>>                                         struct rule_collection *rules)
>>      OVS_REQUIRES(ofproto_mutex)
>>  {
>> -    enum nx_flow_monitor_flags update;
>> +    enum ofp14_flow_monitor_flags update;
>>
>>      if (rule_is_hidden(rule)) {
>>          return;
>> @@ -6401,11 +6402,15 @@ ofproto_collect_ofmonitor_refresh_rule(const
>> struct ofmonitor *m,
>>          return;
>>      }
>>
>> +    if (!ofproto_rule_has_out_group(rule, m->out_group)) {
>> +        return;
>> +    }
>> +
>>      if (seqno) {
>>          if (rule->add_seqno > seqno) {
>> -            update = NXFMF_ADD | NXFMF_MODIFY;
>> +            update = OFPFMF_ADD | OFPFMF_MODIFY;
>>          } else if (rule->modify_seqno > seqno) {
>> -            update = NXFMF_MODIFY;
>> +            update = OFPFMF_MODIFY;
>>          } else {
>>              return;
>>          }
>> @@ -6414,13 +6419,13 @@ ofproto_collect_ofmonitor_refresh_rule(const
>> struct ofmonitor *m,
>>              return;
>>          }
>>      } else {
>> -        update = NXFMF_INITIAL;
>> +        update = OFPFMF_INITIAL;
>>      }
>>
>>      if (!rule->monitor_flags) {
>>          rule_collection_add(rules, rule);
>>      }
>> -    rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
>> +    rule->monitor_flags |= update | (m->flags & OFPFMF_INSTRUCTIONS);
>>  }
>>
>>  static void
>> @@ -6449,7 +6454,7 @@ ofproto_collect_ofmonitor_initial_rules(struct
>> ofmonitor *m,
>>                                          struct rule_collection *rules)
>>      OVS_REQUIRES(ofproto_mutex)
>>  {
>> -    if (m->flags & NXFMF_INITIAL) {
>> +    if (m->flags & OFPFMF_INITIAL) {
>>          ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
>>      }
>>  }
>> @@ -6512,16 +6517,50 @@ handle_flow_monitor_request(struct ofconn
>> *ofconn, const struct ovs_list *msgs)
>>              }
>>
>>              struct ofmonitor *m;
>> -            error = ofmonitor_create(&request, ofconn, &m);
>> -            if (error) {
>> -                goto error;
>> +            switch (request.command) {
>> +            case OFPFMC_ADD: {
>> +                error = ofmonitor_create(&request, ofconn, &m);
>> +                if (error) {
>> +                    goto error;
>> +                }
>> +
>> +                if (n_monitors >= allocated_monitors) {
>> +                    monitors = x2nrealloc(monitors, &allocated_monitors,
>> +                                          sizeof *monitors);
>> +                }
>> +                monitors[n_monitors++] = m;
>> +                break;
>>              }
>> +            case OFPFMC_MODIFY:
>> +                /* Modify operation is to delete old monitor and create a
>> +                 * new one. */
>> +                m = ofmonitor_lookup(ofconn, request.id);
>> +                if (!m) {
>> +                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
>> +                    goto error;
>> +                }
>> +                ofmonitor_destroy(m);
>> +
>> +                error = ofmonitor_create(&request, ofconn, &m);
>> +                if (error) {
>> +                    goto error;
>> +                }
>>
>> -            if (n_monitors >= allocated_monitors) {
>> -                monitors = x2nrealloc(monitors, &allocated_monitors,
>> -                                      sizeof *monitors);
>> +                if (n_monitors >= allocated_monitors) {
>> +                    monitors = x2nrealloc(monitors, &allocated_monitors,
>> +                                          sizeof *monitors);
>> +                }
>> +                monitors[n_monitors++] = m;
>> +                break;
>> +            case OFPFMC_DELETE:
>> +                m = ofmonitor_lookup(ofconn, request.id);
>> +                if (!m) {
>> +                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
>> +                    goto error;
>> +                }
>> +                ofmonitor_destroy(m);
>> +                break;
>>              }
>> -            monitors[n_monitors++] = m;
>>              continue;
>>
>>          error:
>> diff --git a/tests/ofp-print.at b/tests/ofp-print.at
>> index 2c7e163bd..ab5c37649 100644
>> --- a/tests/ofp-print.at
>> +++ b/tests/ofp-print.at
>> @@ -3258,32 +3258,89 @@ NXT_SET_CONTROLLER_ID (xid=0x3): id=123
>>  ])
>>  AT_CLEANUP
>>
>> -AT_SETUP([NXT_FLOW_MONITOR_CANCEL])
>> +AT_SETUP([FLOW_MONITOR_CANCEL])
>>  AT_KEYWORDS([ofp-print])
>> +
>> +dnl OpenFlow 1.0-1.2
>>  AT_CHECK([ovs-ofctl ofp-print "\
>>  01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \
>>  01 02 30 40 \
>>  "], [0], [dnl
>>  NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640
>>  ])
>> +
>> +dnl OpenFlow 1.3
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +04 04 00 14 00 00 00 06 4f 4e 46 00 00 00 07 4e \
>> +01 02 30 40 \
>> +"], [0], [dnl
>> +ONFT_FLOW_MONITOR_CANCEL (OF1.3) (xid=0x6): id=16920640
>> +])
>> +
>> +dnl OpenFlow 1.4+
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +05 12 00 28 00 00 00 04 00 10 00 00 00 00 00 00 \
>> +01 02 30 40 00 00 00 00 00 00 00 00 00 00 00 02 \
>> +00 01 00 04 00 00 00 00 \
>> +"], [0], [dnl
>> +OFPST_FLOW_MONITOR request (OF1.4) (xid=0x4):
>> + id=16920640 command=delete
>> +])
>> +
>>  AT_CLEANUP
>>
>> -AT_SETUP([NXT_FLOW_MONITOR_PAUSED])
>> +AT_SETUP([FLOW_MONITOR_PAUSED])
>>  AT_KEYWORDS([ofp-print])
>> +
>> +dnl OpenFlow 1.0-1.2
>>  AT_CHECK([ovs-ofctl ofp-print "\
>>  01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \
>>  "], [0], [dnl
>>  NXT_FLOW_MONITOR_PAUSED (xid=0x3):
>>  ])
>> +
>> +dnl OpenFlow 1.3
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 4F \
>> +"], [0], [dnl
>> +ONFT_FLOW_MONITOR_PAUSED (OF1.3) (xid=0x3):
>> +])
>> +
>> +dnl OpenFlow 1.4+
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00 \
>> +00 08 00 05 00 00 00 00 \
>> +"], [0], [dnl
>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>> + event=PAUSED
>> +])
>>  AT_CLEANUP
>>
>> -AT_SETUP([NXT_FLOW_MONITOR_RESUMED])
>> +AT_SETUP([FLOW_MONITOR_RESUMED])
>>  AT_KEYWORDS([ofp-print])
>> +
>> +dnl OpenFlow 1.0-1.2
>>  AT_CHECK([ovs-ofctl ofp-print "\
>>  01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \
>>  "], [0], [dnl
>>  NXT_FLOW_MONITOR_RESUMED (xid=0x3):
>>  ])
>> +
>> +dnl OpenFlow 1.3
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 50 \
>> +"], [0], [dnl
>> +ONFT_FLOW_MONITOR_RESUMED (OF1.3) (xid=0x3):
>> +])
>> +
>> +dnl OpenFlow 1.4+
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00
>> +00 08 00 06 00 00 00 00
>> +"], [0], [dnl
>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>> + event=RESUMED
>> +])
>>  AT_CLEANUP
>>
>>  AT_SETUP([NXT_SET_FLOW_FORMAT])
>> @@ -3629,8 +3686,10 @@ NXST_AGGREGATE reply (xid=0x4): packet_count=7
>> byte_count=420 flow_count=7
>>  ])
>>  AT_CLEANUP
>>
>> -AT_SETUP([NXST_FLOW_MONITOR request])
>> +AT_SETUP([FLOW_MONITOR request])
>>  AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
>> +
>> +dnl OpenFlow 1.0-1.2
>>  AT_CHECK([ovs-ofctl ofp-print "\
>>  01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
>>  00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \
>> @@ -3640,10 +3699,37 @@ NXST_FLOW_MONITOR request (xid=0x4):
>>   id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL
>> table=1
>>   id=8192 flags=delete table=2 in_port=1
>>  ])
>> +
>> +dnl OpenFlow 1.3
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +04 12 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 \
>> +4f 4e 46 00 00 00 07 4e \
>> +00 00 10 00 00 3f 00 04 ff ff ff fe 01 00 00 00 00 01 00 04 00 00 00 00 \
>> +00 00 20 00 00 04 ff ff 00 00 00 02 01 00 00 00 00 01 00 04 00 00 00 00 \
>> +"], [0], [dnl
>> +ONFST_FLOW_MONITOR request (OF1.3) (xid=0x6):
>> + id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL
>> table=1
>> + id=8192 flags=delete out_port=2 table=1
>> +])
>> +
>> +dnl OpenFlow 1.4+
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +05 12 00 58 00 00 00 06 00 10 00 00 00 00 00 00 \
>> +00 00 10 00 ff ff ff fe ff ff ff ff 00 5f 00 00 00 01 00 04 00 00 00 00 \
>> +00 00 20 00 00 00 00 01 00 00 00 40 00 5f 01 01 00 01 00 04 00 00 00 00 \
>> +00 00 40 00 00 00 00 02 00 00 00 40 00 5f 02 02 00 01 00 04 00 00 00 00 \
>> +"], [0], [dnl
>> +OFPST_FLOW_MONITOR request (OF1.4) (xid=0x6):
>> + id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL
>> table=0
>> + id=8192 flags=initial,add,delete,modify,actions,own out_port=1
>> out_group=64 table=1
>> + id=16384 command=delete
>> +])
>>  AT_CLEANUP
>>
>> -AT_SETUP([NXST_FLOW_MONITOR reply])
>> +AT_SETUP([FLOW_MONITOR reply])
>>  AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
>> +
>> +dnl OpenFlow 1.0-1.2
>>  AT_CHECK([ovs-ofctl ofp-print "\
>>  01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
>>  00 20 00 01 00 05 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \
>> @@ -3654,6 +3740,32 @@ NXST_FLOW_MONITOR reply (xid=0x4):
>>   event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16
>> cookie=0x123456789abcdef0 in_port=1
>>   event=ABBREV xid=0x186a0
>>  ])
>> +
>> +dnl OpenFlow 1.3
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +04 13 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 4f 4e 46 00 00 00 07 4e \
>> +00 28 00 01 00 05 80 00 00 00 00 00 00 0c 00 00 \
>> +12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
>> +00 00 00 01 00 00 00 00 \
>> +00 08 00 03 00 01 86 a0 \
>> +"], [0], [dnl
>> +ONFST_FLOW_MONITOR reply (OF1.3) (xid=0x6):
>> + event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0
>> in_port=1
>> + event=ABBREV xid=0x186a0
>> +])
>> +
>> +dnl OpenFlow 1.4+
>> +AT_CHECK([ovs-ofctl ofp-print "\
>> +05 13 00 40 00 00 00 00 00 10 00 00 00 00 00 00 \
>> +00 28 00 02 00 05 00 00 00 00 80 00 00 00 00 00 \
>> +12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
>> +00 00 00 01 00 00 00 00 \
>> +00 08 00 04 00 01 86 a0 \
>> +"], [0], [dnl
>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>> + event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0
>> in_port=1
>> + event=ABBREV xid=0x186a0
>> +])
>>  AT_CLEANUP
>>
>>
>> diff --git a/tests/ofproto.at b/tests/ofproto.at
>> index 899c0be55..3502454b9 100644
>> --- a/tests/ofproto.at
>> +++ b/tests/ofproto.at
>> @@ -4592,8 +4592,11 @@ ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=123,actions=output:1
>>  ovs-ofctl -O $2 monitor br0 watch: --detach --no-chdir --pidfile
>> >monitor.log 2>&1
>>  AT_CAPTURE_FILE([monitor.log])
>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>> +
>> +# For OF(1.4+), replace INITIAL to ADDED
>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> -  [NXST_FLOW_MONITOR reply$3:
>> +  [$4 reply$3:
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
>>  OFPT_BARRIER_REPLY$3:
>>  ])
>> @@ -4607,7 +4610,7 @@ ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7
>>  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8
>>  ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9
>>  ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10
>> -ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,actions=output:11
>> +ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=65535,dl_vlan_pcp=3,actions=output:11
>>  ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12
>>  ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13
>>  ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14
>> @@ -4625,71 +4628,74 @@ ovs-ofctl mod-flows br0
>> cookie=5,dl_vlan=123,actions=output:3
>>  ovs-ofctl del-flows br0 dl_vlan=123
>>  ovs-ofctl del-flows br0
>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>> +
>> +# For OF(1.4+), replace INITIAL to ADDED
>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log |
>> multiline_sort], [0],
>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +[$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0
>> actions=output:6
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1
>> actions=output:7
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
>> actions=output:9
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
>> actions=output:10
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> - event=ADDED table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>> + event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3
>> actions=output:11
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0
>> actions=output:12
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1
>> actions=output:13
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
>> actions=output:15
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
>> actions=output:16
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
>> actions=output:18
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
>> actions=output:19
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=0 actions=output:23
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:3
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0
>> actions=output:3
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1
>> actions=output:3
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
>>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0
>> actions=output:3
>>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1
>> actions=output:3
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123
>> actions=output:3
>>   event=DELETED reason=delete table=0 cookie=0x5
>> in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
>>   event=DELETED reason=delete table=0 cookie=0x5
>> in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=DELETED reason=delete table=0 cookie=0 in_port=0 actions=output:23
>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0
>> actions=output:20
>>   event=DELETED reason=delete table=0 cookie=0
>> in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
>>   event=DELETED reason=delete table=0 cookie=0
>> in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
>> + event=DELETED reason=delete table=0 cookie=0
>> in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11
>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124
>> actions=output:2
>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095
>> actions=output:14
>>   event=DELETED reason=delete table=0 cookie=0
>> in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
>>   event=DELETED reason=delete table=0 cookie=0
>> in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0
>> actions=output:21
>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1
>> actions=output:22
>> - event=DELETED reason=delete table=0 cookie=0 in_port=0,vlan_tci=0x0000
>> actions=output:11
>>  OFPT_BARRIER_REPLY$3:
>>  ])
>>
>> @@ -4703,13 +4709,13 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
>>  AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
>>  ])
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log |
>> multiline_sort], [0],
>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +[$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=1 actions=output:2
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=2 actions=output:1
>>  OFPT_BARRIER_REPLY$3:
>>  send: OFPT_FLOW_MOD$3: DEL actions=drop
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=DELETED reason=delete table=0 cookie=0 in_port=1 actions=output:2
>>   event=DELETED reason=delete table=0 cookie=0 in_port=2 actions=output:1
>>  OFPT_BARRIER_REPLY$3:
>> @@ -4732,8 +4738,11 @@ ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=123,actions=output:1
>>  ovs-ofctl -O $2 monitor br0 watch:\!own --detach --no-chdir --pidfile
>> >monitor.log 2>&1
>>  AT_CAPTURE_FILE([monitor.log])
>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>> +
>> +# For OF(1.4+), replace INITIAL to ADDED
>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> -  [NXST_FLOW_MONITOR reply$3:
>> +  [$4 reply$3:
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
>>  OFPT_BARRIER_REPLY$3:
>>  ])
>> @@ -4747,14 +4756,17 @@ ovs-appctl -t ovs-ofctl ofctl/send $send_buf
>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>  AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
>>  ])
>> +
>> +# For OF(1.4+), replace INITIAL to ADDED
>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +[$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=1 actions=output:2
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 in_port=2 actions=output:1
>>  OFPT_BARRIER_REPLY$3:
>>  send: OFPT_FLOW_MOD$3: DEL actions=drop
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=ABBREV xid=0x12345678
>>  OFPT_BARRIER_REPLY$3:
>>  ])
>> @@ -4775,8 +4787,11 @@ ovs-ofctl add-flow br0
>> in_port=0,dl_vlan=123,actions=output:2
>>  ovs-ofctl -O $2 monitor br0 watch:out_port=2 --detach --no-chdir
>> --pidfile >monitor.log 2>&1
>>  AT_CAPTURE_FILE([monitor.log])
>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>> +
>> +# For OF(1.4+), replace INITIAL to ADDED
>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> -  [NXST_FLOW_MONITOR reply$3:
>> +  [$4 reply$3:
>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
>>  OFPT_BARRIER_REPLY$3:
>>  ])
>> @@ -4796,17 +4811,19 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
>>  ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:2
>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>
>> +# For OF(1.4+), replace INITIAL to ADDED
>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +[$4 reply$3 (xid=0x0):
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122
>> actions=output:1,output:2
>>  OFPT_BARRIER_REPLY$3:
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123
>> actions=output:1,output:2
>>  OFPT_BARRIER_REPLY$3:
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1
>>  OFPT_BARRIER_REPLY$3:
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3 (xid=0x0):
>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
>>  OFPT_BARRIER_REPLY$3:
>>  ])
>> @@ -4869,7 +4886,7 @@ ovs-appctl -t ovs-ofctl ofctl/unblock
>>  # A barrier doesn't work for this purpose.
>>  #    https://www.mail-archive.com/dev@openvswitch.org/msg27013.html
>>  #    https://www.mail-archive.com/dev@openvswitch.org/msg27675.html
>> -OVS_WAIT_UNTIL([grep NXT_FLOW_MONITOR_RESUMED monitor.log])
>> +OVS_WAIT_UNTIL([grep RESUMED monitor.log])
>>
>>  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>>
>> @@ -4897,24 +4914,30 @@ AT_CHECK([test $adds = $deletes])
>>  # and MODIFIED lines lines depends on hash order, that is, it varies
>>  # as we change the hash function or change architecture.  Therefore,
>>  # we use a couple of tests below to accept both orders.
>> +
>> +# Rename all version specific strings to a generic one
>> +sed -i'.raw' -e 's|NXT_FLOW_MONITOR|FLOW_MONITOR|' -e
>> 's|ONFT_FLOW_MONITOR|FLOW_MONITOR|' monitor.log
>> +sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n
>> event=PAUSED|FLOW_MONITOR_PAUSED$3:|' monitor.log
>> +sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n
>> event=RESUMED|FLOW_MONITOR_RESUMED$3:|' monitor.log
>> +
>>  AT_CHECK([ofctl_strip < monitor.log | sed -n -e '
>>  /reg1=0x22$/p
>>  /cookie=0x[[23]]/p
>> -/NXT_FLOW_MONITOR_PAUSED$3:/p
>> -/NXT_FLOW_MONITOR_RESUMED$3:/p
>> +/FLOW_MONITOR_PAUSED$3:/p
>> +/FLOW_MONITOR_RESUMED$3:/p
>>  ' > monitor.log.subset])
>>  AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl
>>   event=ADDED table=0 cookie=0x1 reg1=0x22
>> -NXT_FLOW_MONITOR_PAUSED$3:
>> +FLOW_MONITOR_PAUSED$3:
>>   event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
>>   event=ADDED table=0 cookie=0x3 in_port=1
>> -NXT_FLOW_MONITOR_RESUMED$3:
>> +FLOW_MONITOR_RESUMED$3:
>>  ])
>>  AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl
>> -NXT_FLOW_MONITOR_PAUSED$3:
>> +FLOW_MONITOR_PAUSED$3:
>>   event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
>>   event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
>> -NXT_FLOW_MONITOR_RESUMED$3:
>> +FLOW_MONITOR_RESUMED$3:
>>  ])
>>
>>  OVS_VSWITCHD_STOP
>> @@ -4931,7 +4954,7 @@ ovs-ofctl -O $2 monitor br0 watch:udp,udp_dst=8
>> --detach --no-chdir --pidfile >m
>>  AT_CAPTURE_FILE([monitor.log])
>>
>>  # Wait till reply comes backs with OF Version
>> -OVS_WAIT_UNTIL([grep "NXST_FLOW_MONITOR reply$3" monitor.log])
>> +OVS_WAIT_UNTIL([grep "$4 reply$3" monitor.log])
>>  ovs-appctl -t ovs-ofctl exit
>>
>>  # Make sure protocol type in messages from vswitchd, matches that of
>> requested protocol
>> @@ -4940,17 +4963,64 @@ ovs-ofctl add-flow br0
>> sctp,sctp_dst=9,action=normal
>>
>>  OVS_WAIT_UNTIL([grep "event=ADDED " monitor.log])
>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0], [dnl
>> -NXST_FLOW_MONITOR reply$3:
>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>> +$4 reply$3:
>> +$4 reply$3 (xid=0x0):
>>   event=ADDED table=0 cookie=0 sctp,tp_dst=9 actions=NORMAL
>>  ])
>>  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>>  OVS_VSWITCHD_STOP
>>  AT_CLEANUP
>>  ])
>> -CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [])
>> -CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)])
>> -CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)])
>> +CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [],         NXST_FLOW_MONITOR)
>> +CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)], NXST_FLOW_MONITOR)
>> +CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)], NXST_FLOW_MONITOR)
>> +CHECK_FLOW_MONITORING([1.3], [OpenFlow13], [ (OF1.3)],
>> ONFST_FLOW_MONITOR)
>> +CHECK_FLOW_MONITORING([1.4], [OpenFlow14], [ (OF1.4)],
>> OFPST_FLOW_MONITOR)
>> +CHECK_FLOW_MONITORING([1.5], [OpenFlow15], [ (OF1.5)],
>> OFPST_FLOW_MONITOR)
>> +
>> +AT_SETUP([ofproto - OpenFlow14 flow monitoring with out_group])
>> +AT_KEYWORDS([monitor])
>> +OVS_VSWITCHD_START
>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0
>> group_id=1,type=all,bucket=output:1])
>> +AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0
>> group_id=2,type=all,bucket=output:2])
>> +
>> +ovs-ofctl -OOpenFlow14 add-flow br0
>> in_port=0,dl_vlan=121,actions=output:1
>> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=122,actions=group:1
>> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=123,actions=group:2
>> +
>> +# Start a monitor watching the flow table and check the initial reply.
>> +ovs-ofctl -OOpenFlow14 monitor br0 watch:out_group=2 --detach --no-chdir
>> --pidfile >monitor.log 2>&1
>> +AT_CAPTURE_FILE([monitor.log])
>> +ovs-appctl -t ovs-ofctl ofctl/barrier
>> +AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> +[OFPST_FLOW_MONITOR reply (OF1.4):
>> + event=INITIAL table=0 cookie=0 in_port=0,dl_vlan=123 actions=group:2
>> +OFPT_BARRIER_REPLY (OF1.4):
>> +])
>> +
>> +ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
>> +
>> +# Add, modify flows and check the updates.
>> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=121,actions=group:2
>> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=122,actions=group:2
>> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=123,actions=group:1
>> +ovs-appctl -t ovs-ofctl ofctl/barrier
>> +ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=124,actions=group:2
>> +
>> +AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>> [0],
>> +[OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>> + event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=121 actions=group:2
>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>> + event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=group:2
>> +OFPT_BARRIER_REPLY (OF1.4):
>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>> + event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=group:2
>> +])
>> +
>> +OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
>>
>>  AT_SETUP([ofproto - event filtering (OpenFlow 1.3)])
>>  AT_KEYWORDS([monitor])
>> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
>> index 2017c6eba..d9336a43b 100644
>> --- a/utilities/ovs-ofctl.8.in
>> +++ b/utilities/ovs-ofctl.8.in
>> @@ -626,6 +626,9 @@ monitored.
>>  If set, only flows that output to \fIport\fR are monitored.  The
>>  \fIport\fR may be an OpenFlow port number or keyword
>>  (e.g. \fBLOCAL\fR).
>> +.IP "\fBout_group=\fIgroup\fR"
>> +If set, only flows that output to \fIgroup\fR number are monitored.
>> +This field requires OpenFlow 1.4 (-OOpenFlow14) or later.
>>  .IP "\fIfield\fB=\fIvalue\fR"
>>  Monitors only flows that have \fIfield\fR specified as the given
>>  \fIvalue\fR.  Any syntax valid for matching on \fBdump\-flows\fR may
>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
>> index 614b73006..5e6823240 100644
>> --- a/utilities/ovs-ofctl.c
>> +++ b/utilities/ovs-ofctl.c
>> @@ -2313,6 +2313,12 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
>>
>>              msg = ofpbuf_new(0);
>>              ofputil_append_flow_monitor_request(&fmr, msg, protocol);
>> +
>> +            if (verbosity) {
>> +                ofpmsg_update_length(msg);
>> +                ofp_print(stdout, msg->data, msg->size, NULL,
>> +                          NULL, verbosity + 2);
>> +            }
>>              dump_transaction(vconn, msg);
>>              fflush(stdout);
>>          } else if (!strcmp(arg, "resume")) {
>> --
>> 2.29.2
>>
>>
Vasu Dasari Aug. 24, 2021, 8:23 p.m. UTC | #3
Great, thanks!

*Vasu Dasari*


On Tue, Aug 24, 2021 at 4:11 PM Ashish Varma <ashishvarma.ovs@gmail.com>
wrote:

> I will take a look.
>
> Thanks,
> Ashish
>
> On Tue, Aug 24, 2021 at 9:29 AM Vasu Dasari <vdasari@gmail.com> wrote:
>
>> Hi Ben/Ashish,
>>
>> When you get a chance, Can you please take a look at my code?
>>
>> -Vasu
>>
>> *Vasu Dasari*
>>
>>
>> On Thu, Jul 29, 2021 at 10:36 PM Vasu Dasari <vdasari@gmail.com> wrote:
>>
>>> Extended OpenFlow monitoring support
>>> * OpenFlow 1.3 with ONF extensions
>>> * OpenFlow 1.4+ as defined in OpenFlow specification 1.4+.
>>>
>>> ONF extensions are similar to Nicira extensions except for
>>> onf_flow_monitor_request{}
>>> where out_port is defined as 32-bit number OF(1.1) number, oxm match
>>> formats are
>>> used in update and request messages.
>>>
>>> Flow monitoring support in 1.4+ is slightly different from Nicira and ONF
>>> extensions.
>>>  * More flow monitoring flags are defined.
>>>  * Monitor add/modify/delete command is intruduced in flow_monitor
>>>    request message.
>>>  * Addition of out_group as part of flow_monitor request message
>>>
>>> Description of changes:
>>> 1. Generate ofp-msgs.inc to be able to support 1.3, 1.4+ flow Monitoring
>>> messages.
>>>     include/openvswitch/ofp-msgs.h
>>>
>>> 2. Modify openflow header files with protocol specific headers.
>>>     include/openflow/openflow-1.3.h
>>>     include/openflow/openflow-1.4.h
>>>
>>> 3. Modify OvS abstraction of openflow headers. ofp-monitor.h leverages
>>> enums
>>>    from on nicira extensions for creating protocol abstraction headers.
>>> OF(1.4+)
>>>    enums are superset of nicira extensions.
>>>     include/openvswitch/ofp-monitor.h
>>>
>>> 4. Changes to these files reflect encoding and decoding of new protocol
>>> messages.
>>>     lib/ofp-monitor.c
>>>
>>> 5. Changes to mmodules using ofp-monitor APIs. Most of the changes here
>>> are to
>>>    migrate enums from nicira to OF 1.4+ versions.
>>>     ofproto/connmgr.c
>>>     ofproto/connmgr.h
>>>     ofproto/ofproto-provider.h
>>>     ofproto/ofproto.c
>>>
>>> 6. Extended protocol decoding tests to verify all protocol versions
>>>         FLOW_MONITOR_CANCEL
>>>         FLOW_MONITOR_PAUSED
>>>         FLOW_MONITOR_RESUMED
>>>         FLOW_MONITOR request
>>>         FLOW_MONITOR reply
>>>     tests/ofp-print.at
>>>
>>> 7. Modify flow monitoring tests to be able executed by all protocol
>>> versions.
>>>     tests/ofproto.at
>>>
>>> 7. Modified documentation highlighting the change
>>>     utilities/ovs-ofctl.8.in
>>>     NEWS
>>>
>>> Signed-off-by: Vasu Dasari <vdasari@gmail.com>
>>> Reported-at:
>>> https://mail.openvswitch.org/pipermail/ovs-dev/2021-June/383915.html
>>> ---
>>> v1:
>>>  - Fixed 0-day Robot errors
>>>
>>> ---
>>>  NEWS                              |   6 +-
>>>  include/openflow/openflow-1.3.h   |  89 ++++
>>>  include/openflow/openflow-1.4.h   |  93 +++-
>>>  include/openvswitch/ofp-monitor.h |   9 +-
>>>  include/openvswitch/ofp-msgs.h    |  39 +-
>>>  lib/ofp-monitor.c                 | 844 ++++++++++++++++++++++++------
>>>  lib/ofp-print.c                   |  24 +-
>>>  ofproto/connmgr.c                 |  47 +-
>>>  ofproto/connmgr.h                 |   6 +-
>>>  ofproto/ofproto-provider.h        |   4 +-
>>>  ofproto/ofproto.c                 |  89 +++-
>>>  tests/ofp-print.at                | 122 ++++-
>>>  tests/ofproto.at                  | 176 +++++--
>>>  utilities/ovs-ofctl.8.in          |   3 +
>>>  utilities/ovs-ofctl.c             |   6 +
>>>  15 files changed, 1265 insertions(+), 292 deletions(-)
>>>
>>> diff --git a/NEWS b/NEWS
>>> index 02884b774..47ad9de2a 100644
>>> --- a/NEWS
>>> +++ b/NEWS
>>> @@ -25,8 +25,10 @@ v2.16.0 - xx xxx xxxx
>>>     - In ovs-vsctl and vtep-ctl, the "find" command now accept new
>>>       operators {in} and {not-in}.
>>>     - OpenFlow:
>>> -     * Extend Flow Monitoring support for OpenFlow 1.0-1.2 with Nicira
>>> -       Extensions
>>> +     * Extended Flow Monitoring support for all supported OpenFlow
>>> versions
>>> +         OpenFlow versions 1.0-1.2 with Nicira Extensions
>>> +         OpenFlow versions 1.3 with Open Network Foundation extension
>>> +         OpenFlow versions 1.4+, as defined in the OpenFlow
>>> specification
>>>     - Userspace datapath:
>>>       * Auto load balancing of PMDs now partially supports cross-NUMA
>>> polling
>>>         cases, e.g if all PMD threads are running on the same NUMA node.
>>> diff --git a/include/openflow/openflow-1.3.h
>>> b/include/openflow/openflow-1.3.h
>>> index c48a8ea7f..1a818dbb4 100644
>>> --- a/include/openflow/openflow-1.3.h
>>> +++ b/include/openflow/openflow-1.3.h
>>> @@ -374,4 +374,93 @@ struct ofp13_async_config {
>>>  };
>>>  OFP_ASSERT(sizeof(struct ofp13_async_config) == 24);
>>>
>>> +struct onf_flow_monitor_request {
>>> +    ovs_be32   id;            /* Controller-assigned ID for this
>>> monitor. */
>>> +    ovs_be16   flags;         /* ONFFMF_*. */
>>> +    ovs_be16   match_len;     /* Length of oxm_fields. */
>>> +    ovs_be32   out_port;      /* Required output port, if not
>>> OFPP_NONE. */
>>> +    uint8_t    table_id;      /* One table’s ID or 0xff for all tables.
>>> */
>>> +    uint8_t    zeros[3];      /* Align to 64 bits (must be zero). */
>>> +    /* Followed by an ofp11_match structure. */
>>> +};
>>> +OFP_ASSERT(sizeof(struct onf_flow_monitor_request) == 16);
>>> +
>>> +/* Header for experimenter requests and replies. */
>>> +struct onf_experimenter_header {
>>> +    struct ofp_header header;
>>> +    ovs_be32   vendor;        /* ONF_EXPERIMENTER_ID. */
>>> +    ovs_be32   subtype;       /* One of ONFT_*. */
>>> +};
>>> +OFP_ASSERT(sizeof(struct onf_experimenter_header) == 16);
>>> +
>>> +enum onf_flow_monitor_msg_type {
>>> +    ONFT_FLOW_MONITOR_CANCEL = 1870,
>>> +    ONFT_FLOW_MONITOR_PAUSED = 1871,
>>> +    ONFT_FLOW_MONITOR_RESUMED = 1872
>>> +};
>>> +
>>> +/* ’flags’ bits in struct onf_flow_monitor_request. */
>>> +enum onf_flow_monitor_flags {
>>> +    /* When to send updates. */
>>> +    ONFFMF_INITIAL = 1 << 0,  /* Initially matching flows. */
>>> +    ONFFMF_ADD = 1 << 1,      /* New matching flows as they are added.
>>> */
>>> +    ONFFMF_DELETE = 1 << 2,   /* Old matching flows as they are
>>> removed. */
>>> +    ONFFMF_MODIFY = 1 << 3,   /* Matching flows as they are changed. */
>>> +
>>> +    /* What to include in updates. */
>>> +    ONFFMF_ACTIONS = 1 << 4,  /* If set, actions are included. */
>>> +    ONFFMF_OWN = 1 << 5,      /* If set, include own changes in full. */
>>> +};
>>> +
>>> +/* ONFST_FLOW_MONITOR reply header. */
>>> +struct onf_flow_update_header {
>>> +    ovs_be16   length;        /* Length of this entry. */
>>> +    ovs_be16   event;         /* One of ONFFME_*. */
>>> +    /* ...other data depending on ’event’... */
>>> +};
>>> +OFP_ASSERT(sizeof(struct onf_flow_update_header) == 4);
>>> +
>>> +/* ’event’ values in struct onf_flow_update_header. */
>>> +enum onf_flow_update_event {
>>> +    /* struct onf_flow_update_full. */
>>> +    ONFFME_ADDED = 0,         /* Flow was added. */
>>> +    ONFFME_DELETED = 1,       /* Flow was deleted. */
>>> +    ONFFME_MODIFIED = 2,      /* Flow (generally its actions) was
>>> changed. */
>>> +
>>> +    /* struct onf_flow_update_abbrev. */
>>> +    ONFFME_ABBREV = 3,        /* Abbreviated reply. */
>>> +};
>>> +
>>> +/* ONFST_FLOW_MONITOR reply for ONFFME_ADDED, ONFFME_DELETED, and
>>> +* ONFFME_MODIFIED. */
>>> +struct onf_flow_update_full {
>>> +    ovs_be16   length;        /* Length is 24. */
>>> +    ovs_be16   event;         /* One of ONFFME_*. */
>>> +    ovs_be16   reason;        /* OFPRR_* for ONFFME_DELETED, else zero.
>>> */
>>> +    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   match_len;     /* Length of oxm_fields. */
>>> +    uint8_t    table_id;      /* ID of flow’s table. */
>>> +    uint8_t    pad;           /* Reserved, currently zeroed. */
>>> +    ovs_be64   cookie;        /* Opaque controller-issued identifier. */
>>> +    /* Followed by:
>>> +     *   - Exactly match_len (possibly 0) bytes containing the
>>> oxm_fields, then
>>> +     *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7)
>>> bytes of
>>> +     *     all-zero bytes, then
>>> +     *   - Instructions to fill out the remainder ’length’ bytes
>>> (always a
>>> +     *     multiple of 8). If ONFFMF_ACTIONS was not specified, or
>>> ’event’ is
>>> +     *     ONFFME_DELETED, no actions are included.
>>> +     */
>>> +};
>>> +OFP_ASSERT(sizeof(struct onf_flow_update_full) == 24);
>>> +
>>> +/* ONFST_FLOW_MONITOR reply for ONFFME_ABBREV. */
>>> +struct onf_flow_update_abbrev {
>>> +    ovs_be16   length;     /* Length is 8. */
>>> +    ovs_be16   event;      /* ONFFME_ABBREV. */
>>> +    ovs_be32   xid;        /* Controller-specified xid from flow_mod. */
>>> +};
>>> +OFP_ASSERT(sizeof(struct onf_flow_update_abbrev) == 8);
>>> +
>>>  #endif /* openflow/openflow-1.3.h */
>>> diff --git a/include/openflow/openflow-1.4.h
>>> b/include/openflow/openflow-1.4.h
>>> index be191180b..8e6a163fd 100644
>>> --- a/include/openflow/openflow-1.4.h
>>> +++ b/include/openflow/openflow-1.4.h
>>> @@ -358,27 +358,100 @@ OFP_ASSERT(sizeof(struct
>>> ofp14_flow_monitor_request) == 16);
>>>
>>>  /* Flow monitor commands */
>>>  enum ofp14_flow_monitor_command {
>>> -    OFPFMC14_ADD = 0, /* New flow monitor. */
>>> -    OFPFMC14_MODIFY = 1, /* Modify existing flow monitor. */
>>> -    OFPFMC14_DELETE = 2, /* Delete/cancel existing flow monitor. */
>>> +    OFPFMC_ADD = 0, /* New flow monitor. */
>>> +    OFPFMC_MODIFY = 1, /* Modify existing flow monitor. */
>>> +    OFPFMC_DELETE = 2, /* Delete/cancel existing flow monitor. */
>>>  };
>>>
>>>  /* 'flags' bits in struct of_flow_monitor_request. */
>>>  enum ofp14_flow_monitor_flags {
>>>      /* When to send updates. */
>>>      /* Common to NX and OpenFlow 1.4 */
>>> -    OFPFMF14_INITIAL = 1 << 0,     /* Initially matching flows. */
>>> -    OFPFMF14_ADD = 1 << 1,         /* New matching flows as they are
>>> added. */
>>> -    OFPFMF14_REMOVED = 1 << 2,     /* Old matching flows as they are
>>> removed. */
>>> -    OFPFMF14_MODIFY = 1 << 3,      /* Matching flows as they are
>>> changed. */
>>> +    OFPFMF_INITIAL = 1 << 0,     /* Initially matching flows. */
>>> +    OFPFMF_ADD = 1 << 1,         /* New matching flows as they are
>>> added. */
>>> +    OFPFMF_REMOVED = 1 << 2,     /* Old matching flows as they are
>>> removed. */
>>> +    OFPFMF_MODIFY = 1 << 3,      /* Matching flows as they are changed.
>>> */
>>>
>>>      /* What to include in updates. */
>>>      /* Common to NX and OpenFlow 1.4 */
>>> -    OFPFMF14_INSTRUCTIONS = 1 << 4, /* If set, instructions are
>>> included. */
>>> -    OFPFMF14_NO_ABBREV = 1 << 5,    /* If set, include own changes in
>>> full. */
>>> +    OFPFMF_INSTRUCTIONS = 1 << 4, /* If set, instructions are included.
>>> */
>>> +    OFPFMF_NO_ABBREV = 1 << 5,    /* If set, include own changes in
>>> full. */
>>>      /* OpenFlow 1.4 */
>>> -    OFPFMF14_ONLY_OWN = 1 << 6,     /* If set, don't include other
>>> controllers.
>>> +    OFPFMF_ONLY_OWN = 1 << 6,     /* If set, don't include other
>>> controllers.
>>>                                       */
>>>  };
>>>
>>> +/* OFPMP_FLOW_MONITOR reply header.
>>> + *
>>> + * The body of an OFPMP_FLOW_MONITOR reply is an array of
>>> variable-length
>>> + * structures, each of which begins with this header.  The ’length’
>>> member may
>>> + * be used to traverse the array, and the ’event’ member may be used to
>>> + * determine the particular structure.
>>> + * Every instance is a multiple of 8 bytes long. */
>>> +struct ofp_flow_update_header {
>>> +    ovs_be16 length;                /* Length of this entry. */
>>> +    ovs_be16 event;                 /* One of OFPFME_*. */
>>> +    /* ...other data depending on ’event’... */
>>> +};
>>> +OFP_ASSERT(sizeof(struct ofp_flow_update_header) == 4);
>>> +
>>> +/* ’event’ values in struct ofp_flow_update_header. */
>>> +enum ofp_flow_update_event {
>>> +    /* struct ofp_flow_update_full. */
>>> +    OFPFME_INITIAL = 0,          /* Flow present when flow monitor
>>> created. */
>>> +    OFPFME_ADDED = 1,            /* Flow was added. */
>>> +    OFPFME_REMOVED = 2,          /* Flow was removed. */
>>> +    OFPFME_MODIFIED = 3,         /* Flow instructions were changed. */
>>> +
>>> +    /* struct ofp_flow_update_abbrev. */
>>> +    OFPFME_ABBREV = 4,           /* Abbreviated reply. */
>>> +
>>> +    /* struct ofp_flow_update_header. */
>>> +    OFPFME_PAUSED = 5,           /* Monitoring paused (out of buffer
>>> space). */
>>> +    OFPFME_RESUMED = 6,          /* Monitoring resumed. */
>>> +};
>>> +
>>> +/* OFPMP_FLOW_MONITOR reply for OFPFME_INITIAL, OFPFME_ADDED,
>>> OFPFME_REMOVED,
>>> + * and OFPFME_MODIFIED. */
>>> +struct ofp_flow_update_full {
>>> +    ovs_be16 length;           /* Length is 32 + match + instructions.
>>> */
>>> +    ovs_be16 event;            /* One of OFPFME_*. */
>>> +    uint8_t table_id;          /* ID of flow’s table. */
>>> +    uint8_t reason;            /* OFPRR_* for OFPFME_REMOVED, else
>>> zero. */
>>> +    ovs_be16 idle_timeout;     /* Number of seconds idle before
>>> expiration. */
>>> +    ovs_be16 hard_timeout;     /* Number of seconds before expiration.
>>> */
>>> +    ovs_be16 priority;         /* Priority of the entry. */
>>> +    uint8_t zeros[4];          /* Reserved, currently zeroed. */
>>> +    ovs_be64 cookie;           /* Opaque controller-issued identifier.
>>> */
>>> +    /* Instruction set.
>>> +     *      If OFPFMF_INSTRUCTIONS was not specified, or ’event’ is
>>> +     *     OFPFME_REMOVED, no instructions are included.
>>> +     */
>>> +};
>>> +OFP_ASSERT(sizeof(struct ofp_flow_update_full) == 24);
>>> +
>>> +/* OFPMP_FLOW_MONITOR reply for OFPFME_ABBREV.
>>> + *
>>> + * When the controller does not specify OFPFMF_NO_ABBREV in a monitor
>>> request,
>>> + * any flow tables changes due to the controller’s own requests (on the
>>> same
>>> + * OpenFlow channel) will be abbreviated, when possible, to this form,
>>> which
>>> + * simply specifies the ’xid’ of the OpenFlow request (e.g. an
>>> OFPT_FLOW_MOD)
>>> + * that caused the change.
>>> + * Some changes cannot be abbreviated and will be sent in full.
>>> + */
>>> +struct ofp_flow_update_abbrev {
>>> +    ovs_be16 length;     /* Length is 8. */
>>> +    ovs_be16 event;      /* OFPFME_ABBREV. */
>>> +    ovs_be32 xid;        /* Controller-specified xid from flow_mod. */
>>> +};
>>> +OFP_ASSERT(sizeof(struct ofp_flow_update_abbrev) == 8);
>>> +
>>> +/* OFPMP_FLOW_MONITOR reply for OFPFME_PAUSED and OFPFME_RESUMED.*/
>>> +struct ofp_flow_update_paused {
>>> +    ovs_be16 length;      /* Length is 8. */
>>> +    ovs_be16 event;       /* One of OFPFME_*. */
>>> +    uint8_t zeros[4];     /* Reserved, currently zeroed. */
>>> +};
>>> +OFP_ASSERT(sizeof(struct ofp_flow_update_paused) == 8);
>>> +
>>>  #endif /* openflow/openflow-1.4.h */
>>> diff --git a/include/openvswitch/ofp-monitor.h
>>> b/include/openvswitch/ofp-monitor.h
>>> index 835efd0f3..7c7cfcff4 100644
>>> --- a/include/openvswitch/ofp-monitor.h
>>> +++ b/include/openvswitch/ofp-monitor.h
>>> @@ -61,8 +61,10 @@ void ofputil_flow_removed_format(struct ds *,
>>>  /* Abstract nx_flow_monitor_request. */
>>>  struct ofputil_flow_monitor_request {
>>>      uint32_t id;
>>> -    enum nx_flow_monitor_flags flags;
>>> +    enum ofp14_flow_monitor_command command;
>>> +    enum ofp14_flow_monitor_flags flags;
>>>      ofp_port_t out_port;
>>> +    uint32_t out_group;
>>>      uint8_t table_id;
>>>      struct match match;
>>>  };
>>> @@ -85,7 +87,7 @@ char *parse_flow_monitor_request(struct
>>> ofputil_flow_monitor_request *,
>>>
>>>  /* Abstract nx_flow_update. */
>>>  struct ofputil_flow_update {
>>> -    enum nx_flow_update_event event;
>>> +    enum ofp_flow_update_event event;
>>>
>>>      /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */
>>>      enum ofp_flow_removed_reason reason;
>>> @@ -119,6 +121,9 @@ uint32_t ofputil_decode_flow_monitor_cancel(const
>>> struct ofp_header *);
>>>  struct ofpbuf *ofputil_encode_flow_monitor_cancel(
>>>      uint32_t id, enum ofputil_protocol protocol);
>>>
>>> +struct ofpbuf * ofputil_encode_flow_monitor_pause(
>>> +    enum ofp_flow_update_event command, enum ofputil_protocol protocol);
>>> +
>>>  struct ofputil_requestforward {
>>>      ovs_be32 xid;
>>>      /* Also used for OF 1.0-1.3 when using Nicira Extension: */
>>> diff --git a/include/openvswitch/ofp-msgs.h
>>> b/include/openvswitch/ofp-msgs.h
>>> index c5fde0270..921a937e5 100644
>>> --- a/include/openvswitch/ofp-msgs.h
>>> +++ b/include/openvswitch/ofp-msgs.h
>>> @@ -453,14 +453,33 @@ enum ofpraw {
>>>
>>>      /* OFPST 1.4+ (16): uint8_t[8][]. */
>>>      OFPRAW_OFPST14_FLOW_MONITOR_REQUEST,
>>> +    /* ONFST 1.3 (1870): uint8_t[8][]. */
>>> +    OFPRAW_ONFST13_FLOW_MONITOR_REQUEST,
>>>      /* NXST 1.0-1.2 (2): uint8_t[8][]. */
>>>      OFPRAW_NXST_FLOW_MONITOR_REQUEST,
>>>
>>>      /* OFPST 1.4+ (16): uint8_t[8][]. */
>>>      OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
>>> +    /* ONFST 1.3 (1870): uint8_t[8][]. */
>>> +    OFPRAW_ONFST13_FLOW_MONITOR_REPLY,
>>>      /* NXST 1.0-1.2 (2): uint8_t[8][]. */
>>>      OFPRAW_NXST_FLOW_MONITOR_REPLY,
>>>
>>> +    /* ONFT 1.3 (1870): struct nx_flow_monitor_cancel. */
>>> +    OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
>>> +    /* NXT 1.0-1.2 (21): struct nx_flow_monitor_cancel. */
>>> +    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
>>> +
>>> +    /* ONFT 1.3 (1871): void. */
>>> +    OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
>>> +    /* NXT 1.0-1.2 (22): void. */
>>> +    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>>> +
>>> +    /* ONFT 1.3 (1872): void. */
>>> +    OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
>>> +    /* NXT 1.0-1.2 (23): void. */
>>> +    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>>> +
>>>  /* Nicira extension messages.
>>>   *
>>>   * Nicira extensions that correspond to standard OpenFlow messages are
>>> listed
>>> @@ -481,15 +500,6 @@ enum ofpraw {
>>>      /* NXT 1.0+ (20): struct nx_controller_id. */
>>>      OFPRAW_NXT_SET_CONTROLLER_ID,
>>>
>>> -    /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */
>>> -    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
>>> -
>>> -    /* NXT 1.0+ (22): void. */
>>> -    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>>> -
>>> -    /* NXT 1.0+ (23): void. */
>>> -    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>>> -
>>>      /* NXT 1.0+ (24): struct nx_tlv_table_mod, struct nx_tlv_map[]. */
>>>      OFPRAW_NXT_TLV_TABLE_MOD,
>>>
>>> @@ -741,8 +751,10 @@ enum ofptype {
>>>                                        * OFPRAW_OFPST14_PORT_DESC_REPLY.
>>> */
>>>
>>>      OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /*
>>> OFPRAW_OFPST14_FLOW_MONITOR_REQUEST.
>>> +                                         *
>>> OFPRAW_ONFST13_FLOW_MONITOR_REQUEST.
>>>                                           *
>>> OFPRAW_NXST_FLOW_MONITOR_REQUEST. */
>>>      OFPTYPE_FLOW_MONITOR_STATS_REPLY,   /*
>>> OFPRAW_OFPST14_FLOW_MONITOR_REPLY.
>>> +                                         *
>>> OFPRAW_ONFST13_FLOW_MONITOR_REPLY.
>>>                                           *
>>> OFPRAW_NXST_FLOW_MONITOR_REPLY. */
>>>
>>>      /* Nicira extensions. */
>>> @@ -762,9 +774,12 @@ enum ofptype {
>>>      OFPTYPE_CT_FLUSH_ZONE,            /* OFPRAW_NXT_CT_FLUSH_ZONE. */
>>>
>>>      /* Flow monitor extension. */
>>> -    OFPTYPE_FLOW_MONITOR_CANCEL,        /*
>>> OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
>>> -    OFPTYPE_FLOW_MONITOR_PAUSED,        /*
>>> OFPRAW_NXT_FLOW_MONITOR_PAUSED. */
>>> -    OFPTYPE_FLOW_MONITOR_RESUMED,       /*
>>> OFPRAW_NXT_FLOW_MONITOR_RESUMED. */
>>> +    OFPTYPE_FLOW_MONITOR_CANCEL,  /* OFPRAW_NXT_FLOW_MONITOR_CANCEL.
>>> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_CANCEL.
>>> */
>>> +    OFPTYPE_FLOW_MONITOR_PAUSED,  /* OFPRAW_NXT_FLOW_MONITOR_PAUSED.
>>> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_PAUSED.
>>> */
>>> +    OFPTYPE_FLOW_MONITOR_RESUMED, /* OFPRAW_NXT_FLOW_MONITOR_RESUMED.
>>> +                                   * OFPRAW_ONFT13_FLOW_MONITOR_RESUMED
>>> */
>>>  };
>>>
>>>  /* Decoding messages into OFPTYPE_* values. */
>>> diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
>>> index 51f01b100..1756c9e8e 100644
>>> --- a/lib/ofp-monitor.c
>>> +++ b/lib/ofp-monitor.c
>>> @@ -328,6 +328,98 @@ ofputil_flow_removed_format(struct ds *s,
>>>      ds_put_format(s, " pkts%"PRIu64" bytes%"PRIu64"\n",
>>>                    fr->packet_count, fr->byte_count);
>>>  }
>>> +
>>> +static uint16_t
>>> +nx_to_ofp_flow_monitor_flags(uint16_t flags)
>>> +{
>>> +    uint16_t oxm_flags = 0;
>>> +
>>> +    if (flags & NXFMF_INITIAL) {
>>> +        oxm_flags |= OFPFMF_INITIAL;
>>> +    }
>>> +    if (flags & NXFMF_ADD) {
>>> +        oxm_flags |= OFPFMF_ADD;
>>> +    }
>>> +    if (flags & NXFMF_DELETE) {
>>> +        oxm_flags |= OFPFMF_REMOVED;
>>> +    }
>>> +    if (flags & NXFMF_MODIFY) {
>>> +        oxm_flags |= OFPFMF_MODIFY;
>>> +    }
>>> +    if (flags & NXFMF_ACTIONS) {
>>> +        oxm_flags |= OFPFMF_INSTRUCTIONS;
>>> +    }
>>> +    if (flags & NXFMF_OWN) {
>>> +        oxm_flags |= OFPFMF_ONLY_OWN;
>>> +    }
>>> +
>>> +    return oxm_flags;
>>> +}
>>> +
>>> +static uint16_t
>>> +ofp_to_nx_flow_monitor_flags(uint16_t flags)
>>> +{
>>> +    uint16_t nx_flags = 0;
>>> +
>>> +    if (flags & OFPFMF_INITIAL) {
>>> +        nx_flags |= NXFMF_INITIAL;
>>> +    }
>>> +    if (flags & OFPFMF_ADD) {
>>> +        nx_flags |= NXFMF_ADD;
>>> +    }
>>> +    if (flags & OFPFMF_REMOVED) {
>>> +        nx_flags |= NXFMF_DELETE;
>>> +    }
>>> +    if (flags & OFPFMF_MODIFY) {
>>> +        nx_flags |= NXFMF_MODIFY;
>>> +    }
>>> +    if (flags & OFPFMF_INSTRUCTIONS) {
>>> +        nx_flags |= NXFMF_ACTIONS;
>>> +    }
>>> +    if (flags & OFPFMF_ONLY_OWN) {
>>> +        nx_flags |= NXFMF_OWN;
>>> +    }
>>> +
>>> +    return nx_flags;
>>> +}
>>> +
>>> +static enum ofp_flow_update_event
>>> +nx_to_ofp_flow_update_event(enum nx_flow_update_event event)
>>> +{
>>> +    switch (event) {
>>> +    case NXFME_ADDED:
>>> +        return OFPFME_ADDED;
>>> +    case NXFME_DELETED:
>>> +        return OFPFME_REMOVED;
>>> +    case NXFME_MODIFIED:
>>> +        return OFPFME_MODIFIED;
>>> +    case NXFME_ABBREV:
>>> +        return OFPFME_ABBREV;
>>> +     default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +}
>>> +
>>> +static enum nx_flow_update_event
>>> +ofp_to_nx_flow_update_event(enum ofp_flow_update_event event)
>>> +{
>>> +    switch (event) {
>>> +    case OFPFME_INITIAL:
>>> +    case OFPFME_ADDED:
>>> +        return NXFME_ADDED;
>>> +    case OFPFME_REMOVED:
>>> +        return NXFME_DELETED;
>>> +    case OFPFME_MODIFIED:
>>> +        return NXFME_MODIFIED;
>>> +    case OFPFME_ABBREV:
>>> +        return NXFME_ABBREV;
>>> +    default:
>>> +    case OFPFME_PAUSED:
>>> +    case OFPFME_RESUMED:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +}
>>> +
>>>
>>>  /* ofputil_flow_monitor_request */
>>>
>>> @@ -345,43 +437,129 @@ int
>>>  ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request
>>> *rq,
>>>                                      struct ofpbuf *msg)
>>>  {
>>> -    struct nx_flow_monitor_request *nfmr;
>>>      uint16_t flags;
>>> +    enum ofperr error;
>>> +    enum ofpraw raw;
>>>
>>> -    if (!msg->header) {
>>> -        ofpraw_pull_assert(msg);
>>> +    error = (msg->header ? ofpraw_decode(&raw, msg->header)
>>> +             : ofpraw_pull(&raw, msg));
>>> +    if (error) {
>>> +        return error;
>>>      }
>>>
>>>      if (!msg->size) {
>>>          return EOF;
>>>      }
>>>
>>> -    nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
>>> -    if (!nfmr) {
>>> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
>>> -                     "leftover bytes at end", msg->size);
>>> -        return OFPERR_OFPBRC_BAD_LEN;
>>> -    }
>>> +    switch ((int) raw) {
>>> +    case OFPRAW_NXST_FLOW_MONITOR_REQUEST: {
>>> +        struct nx_flow_monitor_request *nfmr;
>>>
>>> -    flags = ntohs(nfmr->flags);
>>> -    if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
>>> -        || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
>>> -                     | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
>>> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
>>> flags);
>>> -        return OFPERR_OFPMOFC_BAD_FLAGS;
>>> +        nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
>>> +        if (!nfmr) {
>>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
>>> +                    "leftover bytes at end", msg->size);
>>> +            return OFPERR_OFPBRC_BAD_LEN;
>>> +        }
>>> +
>>> +        flags = ntohs(nfmr->flags);
>>> +        if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
>>> +            || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
>>> +                         | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
>>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags
>>> %#"PRIx16,
>>> +                         flags);
>>> +            return OFPERR_OFPMOFC_BAD_FLAGS;
>>> +        }
>>> +
>>> +        if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
>>> +            return OFPERR_NXBRC_MUST_BE_ZERO;
>>> +        }
>>> +
>>> +        rq->id = ntohl(nfmr->id);
>>> +        rq->command = OFPFMC_ADD;
>>> +        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
>>> +        rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
>>> +        rq->table_id = nfmr->table_id;
>>> +        rq->out_group = OFPG_ANY;
>>> +
>>> +        return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match,
>>> NULL,
>>> +                NULL, false, NULL, NULL);
>>>      }
>>> +    case OFPRAW_ONFST13_FLOW_MONITOR_REQUEST: {
>>> +        struct onf_flow_monitor_request *ofmr;
>>> +
>>> +        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
>>> +        if (!ofmr) {
>>> +            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR request has %"PRIu32"
>>> "
>>> +                         "leftover bytes at end", msg->size);
>>> +            return OFPERR_OFPBRC_BAD_LEN;
>>> +        }
>>> +
>>> +        flags = ntohs(ofmr->flags);
>>> +        if (!(flags & (ONFFMF_ADD | ONFFMF_DELETE | ONFFMF_MODIFY))
>>> +            || flags & ~(ONFFMF_INITIAL | ONFFMF_ADD | ONFFMF_DELETE
>>> +                         | ONFFMF_MODIFY | ONFFMF_ACTIONS |
>>> ONFFMF_OWN)) {
>>> +            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR has bad flags
>>> %#"PRIx16,
>>> +                         flags);
>>> +            return OFPERR_OFPMOFC_BAD_FLAGS;
>>> +        }
>>> +
>>> +        if (!is_all_zeros(ofmr->zeros, sizeof ofmr->zeros)) {
>>> +            return OFPERR_NXBRC_MUST_BE_ZERO;
>>> +        }
>>> +
>>> +        rq->id = ntohl(ofmr->id);
>>> +        rq->command = OFPFMC_ADD;
>>> +        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
>>> +        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
>>> +        if (error) {
>>> +            return error;
>>> +        }
>>> +        rq->table_id = ofmr->table_id;
>>> +        rq->out_group = OFPG_ANY;
>>>
>>> -    if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
>>> -        return OFPERR_NXBRC_MUST_BE_ZERO;
>>> +        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
>>> NULL);
>>>      }
>>> +    case OFPRAW_OFPST14_FLOW_MONITOR_REQUEST: {
>>> +        struct ofp14_flow_monitor_request *ofmr;
>>> +
>>> +        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
>>> +        if (!ofmr) {
>>> +            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR request has %"PRIu32"
>>> "
>>> +                    "leftover bytes at end", msg->size);
>>> +            return OFPERR_OFPBRC_BAD_LEN;
>>> +        }
>>>
>>> -    rq->id = ntohl(nfmr->id);
>>> -    rq->flags = flags;
>>> -    rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
>>> -    rq->table_id = nfmr->table_id;
>>> +        flags = ntohs(ofmr->flags);
>>> +        rq->id = ntohl(ofmr->monitor_id);
>>> +        rq->command = ofmr->command;
>>> +
>>> +        if (ofmr->command == OFPFMC_DELETE) {
>>> +            return ofputil_pull_ofp11_match(msg, NULL, NULL,
>>> &rq->match, NULL);
>>> +        }
>>>
>>> -    return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
>>> -                         NULL, false, NULL, NULL);
>>> +        if (!(flags & (OFPFMF_ADD | OFPFMF_REMOVED | OFPFMF_MODIFY))
>>> +                || flags & ~(OFPFMF_INITIAL | OFPFMF_ADD |
>>> OFPFMF_REMOVED
>>> +                    | OFPFMF_MODIFY | OFPFMF_INSTRUCTIONS |
>>> OFPFMF_ONLY_OWN)) {
>>> +            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR has bad flags
>>> %#"PRIx16,
>>> +                         flags);
>>> +            return OFPERR_OFPMOFC_BAD_FLAGS;
>>> +        }
>>> +
>>> +        rq->command = ofmr->command;
>>> +        rq->flags = flags;
>>> +        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
>>> +        if (error) {
>>> +            return error;
>>> +        }
>>> +        rq->out_group = ntohl(ofmr->out_group);
>>> +        rq->table_id = ofmr->table_id;
>>> +
>>> +        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match,
>>> NULL);
>>> +    }
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>>  }
>>>
>>>  void
>>> @@ -389,66 +567,143 @@ ofputil_append_flow_monitor_request(
>>>      const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg,
>>>      enum ofputil_protocol protocol)
>>>  {
>>> -    struct nx_flow_monitor_request *nfmr;
>>>      size_t start_ofs;
>>>      int match_len;
>>>      enum ofp_version version =
>>> ofputil_protocol_to_ofp_version(protocol);
>>>
>>>      if (!msg->size) {
>>> -        ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version, msg);
>>> -    }
>>> +        switch (version) {
>>> +        case OFP10_VERSION:
>>> +        case OFP11_VERSION:
>>> +        case OFP12_VERSION: {
>>> +            struct nx_flow_monitor_request *nfmr;
>>> +
>>> +            if (!msg->size) {
>>> +                ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version,
>>> msg);
>>> +            }
>>>
>>> -    start_ofs = msg->size;
>>> -    ofpbuf_put_zeros(msg, sizeof *nfmr);
>>> -    match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
>>> -
>>> -    nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
>>> -    nfmr->id = htonl(rq->id);
>>> -    nfmr->flags = htons(rq->flags);
>>> -    nfmr->out_port = htons(ofp_to_u16(rq->out_port));
>>> -    nfmr->match_len = htons(match_len);
>>> -    nfmr->table_id = rq->table_id;
>>> +            start_ofs = msg->size;
>>> +            ofpbuf_put_zeros(msg, sizeof *nfmr);
>>> +            match_len = nx_put_match(msg, &rq->match, htonll(0),
>>> htonll(0));
>>> +
>>> +            nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
>>> +            nfmr->id = htonl(rq->id);
>>> +            nfmr->flags =
>>> htons(ofp_to_nx_flow_monitor_flags(rq->flags));
>>> +            nfmr->out_port = htons(ofp_to_u16(rq->out_port));
>>> +            nfmr->match_len = htons(match_len);
>>> +            nfmr->table_id = rq->table_id;
>>> +            break;
>>> +        }
>>> +        case OFP13_VERSION: {
>>> +            struct onf_flow_monitor_request *ofmr;
>>> +
>>> +            if (!msg->size) {
>>> +                ofpraw_put(OFPRAW_ONFST13_FLOW_MONITOR_REQUEST,
>>> version, msg);
>>> +            }
>>> +
>>> +            start_ofs = msg->size;
>>> +            ofpbuf_put_zeros(msg, sizeof *ofmr);
>>> +            match_len = oxm_put_match(msg, &rq->match, version);
>>> +
>>> +            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
>>> +            ofmr->id = htonl(rq->id);
>>> +            ofmr->flags =
>>> htons(ofp_to_nx_flow_monitor_flags(rq->flags));
>>> +            ofmr->match_len = htons(match_len);
>>> +            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
>>> +            ofmr->table_id = rq->table_id;
>>> +            break;
>>> +        }
>>> +        case OFP14_VERSION:
>>> +        case OFP15_VERSION: {
>>> +            struct ofp14_flow_monitor_request *ofmr;
>>> +
>>> +            if (!msg->size) {
>>> +                ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST,
>>> version, msg);
>>> +            }
>>> +
>>> +            start_ofs = msg->size;
>>> +            ofpbuf_put_zeros(msg, sizeof *ofmr);
>>> +            oxm_put_match(msg, &rq->match, version);
>>> +
>>> +            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
>>> +            ofmr->monitor_id = htonl(rq->id);
>>> +            ofmr->command = OFPFMC_ADD;
>>> +            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
>>> +            ofmr->out_group = htonl(rq->out_group);
>>> +            ofmr->flags = htons(rq->flags);
>>> +            ofmr->table_id = rq->table_id;
>>> +            break;
>>> +        }
>>> +        default:
>>> +            OVS_NOT_REACHED();
>>> +        }
>>> +    }
>>>  }
>>>
>>>  static const char *
>>> -nx_flow_monitor_flags_to_name(uint32_t bit)
>>> +ofp_flow_monitor_flags_to_name(uint32_t bit)
>>>  {
>>> -    enum nx_flow_monitor_flags fmf = bit;
>>> +    enum ofp14_flow_monitor_flags fmf = bit;
>>>
>>>      switch (fmf) {
>>> -    case NXFMF_INITIAL: return "initial";
>>> -    case NXFMF_ADD: return "add";
>>> -    case NXFMF_DELETE: return "delete";
>>> -    case NXFMF_MODIFY: return "modify";
>>> -    case NXFMF_ACTIONS: return "actions";
>>> -    case NXFMF_OWN: return "own";
>>> +    case OFPFMF_INITIAL: return "initial";
>>> +    case OFPFMF_ADD: return "add";
>>> +    case OFPFMF_REMOVED: return "delete";
>>> +    case OFPFMF_MODIFY: return "modify";
>>> +    case OFPFMF_INSTRUCTIONS: return "actions";
>>> +    case OFPFMF_NO_ABBREV: return "no-abbrev";
>>> +    case OFPFMF_ONLY_OWN: return "own";
>>>      }
>>>
>>>      return NULL;
>>>  }
>>>
>>> +static const char *
>>> +ofp_flow_monitor_command_to_string(enum ofp14_flow_monitor_command
>>> command)
>>> +{
>>> +    switch (command) {
>>> +    case OFPFMC_ADD: return "add";
>>> +    case OFPFMC_MODIFY: return "modify";
>>> +    case OFPFMC_DELETE: return "delete";
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +}
>>> +
>>>  void
>>>  ofputil_flow_monitor_request_format(
>>>      struct ds *s, const struct ofputil_flow_monitor_request *request,
>>>      const struct ofputil_port_map *port_map,
>>>      const struct ofputil_table_map *table_map)
>>>  {
>>> +    if (request->command == OFPFMC_DELETE) {
>>> +        ds_put_format(s, "\n id=%"PRIu32" command=%s", request->id,
>>> +
>>> ofp_flow_monitor_command_to_string(request->command));
>>> +        return;
>>> +    }
>>>      ds_put_format(s, "\n id=%"PRIu32" flags=", request->id);
>>> -    ofp_print_bit_names(s, request->flags,
>>> nx_flow_monitor_flags_to_name, ',');
>>> +    ofp_print_bit_names(s, request->flags,
>>> +                        ofp_flow_monitor_flags_to_name, ',');
>>>
>>>      if (request->out_port != OFPP_NONE) {
>>>          ds_put_cstr(s, " out_port=");
>>>          ofputil_format_port(request->out_port, port_map, s);
>>>      }
>>>
>>> +    if (request->out_group && (request->out_group != OFPG_ANY)) {
>>> +        ds_put_format(s, " out_group=%d", request->out_group);
>>> +    }
>>> +
>>>      if (request->table_id != 0xff) {
>>>          ds_put_format(s, " table=");
>>>          ofputil_format_table(request->table_id, table_map, s);
>>>      }
>>>
>>> -    ds_put_char(s, ' ');
>>> -    match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
>>> -    ds_chomp(s, ' ');
>>> +    if (request->command != OFPFMC_DELETE) {
>>> +        ds_put_char(s, ' ');
>>> +        match_format(&request->match, port_map, s,
>>> OFP_DEFAULT_PRIORITY);
>>> +        ds_chomp(s, ' ');
>>> +    }
>>>  }
>>>
>>>  static char * OVS_WARN_UNUSED_RESULT
>>> @@ -464,9 +719,10 @@ parse_flow_monitor_request__(struct
>>> ofputil_flow_monitor_request *fmr,
>>>
>>>      fmr->id = atomic_count_inc(&id);
>>>
>>> -    fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE |
>>> NXFMF_MODIFY
>>> -                  | NXFMF_OWN | NXFMF_ACTIONS);
>>> +    fmr->flags = (OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED |
>>> OFPFMF_MODIFY
>>> +                  | OFPFMF_ONLY_OWN | OFPFMF_INSTRUCTIONS);
>>>      fmr->out_port = OFPP_NONE;
>>> +    fmr->out_group = OFPG_ANY;
>>>      fmr->table_id = 0xff;
>>>      match_init_catchall(&fmr->match);
>>>
>>> @@ -476,17 +732,19 @@ parse_flow_monitor_request__(struct
>>> ofputil_flow_monitor_request *fmr,
>>>          char *error = NULL;
>>>
>>>          if (!strcmp(name, "!initial")) {
>>> -            fmr->flags &= ~NXFMF_INITIAL;
>>> +            fmr->flags &= ~OFPFMF_INITIAL;
>>>          } else if (!strcmp(name, "!add")) {
>>> -            fmr->flags &= ~NXFMF_ADD;
>>> +            fmr->flags &= ~OFPFMF_ADD;
>>>          } else if (!strcmp(name, "!delete")) {
>>> -            fmr->flags &= ~NXFMF_DELETE;
>>> +            fmr->flags &= ~OFPFMF_REMOVED;
>>>          } else if (!strcmp(name, "!modify")) {
>>> -            fmr->flags &= ~NXFMF_MODIFY;
>>> +            fmr->flags &= ~OFPFMF_MODIFY;
>>>          } else if (!strcmp(name, "!actions")) {
>>> -            fmr->flags &= ~NXFMF_ACTIONS;
>>> +            fmr->flags &= ~OFPFMF_INSTRUCTIONS;
>>> +        } else if (!strcmp(name, "!abbrev")) {
>>> +            fmr->flags &= ~OFPFMF_NO_ABBREV;
>>>          } else if (!strcmp(name, "!own")) {
>>> -            fmr->flags &= ~NXFMF_OWN;
>>> +            fmr->flags &= ~OFPFMF_ONLY_OWN;
>>>          } else if (ofp_parse_protocol(name, &p)) {
>>>              match_set_dl_type(&fmr->match, htons(p->dl_type));
>>>              if (p->nw_proto) {
>>> @@ -511,6 +769,8 @@ parse_flow_monitor_request__(struct
>>> ofputil_flow_monitor_request *fmr,
>>>                  }
>>>              } else if (!strcmp(name, "out_port")) {
>>>                  fmr->out_port = u16_to_ofp(atoi(value));
>>> +            } else if (!strcmp(name, "out_group")) {
>>> +                fmr->out_group = atoi(value);
>>>              } else {
>>>                  return xasprintf("%s: unknown keyword %s", str_, name);
>>>              }
>>> @@ -562,101 +822,226 @@ int
>>>  ofputil_decode_flow_update(struct ofputil_flow_update *update,
>>>                             struct ofpbuf *msg, struct ofpbuf *ofpacts)
>>>  {
>>> -    struct nx_flow_update_header *nfuh;
>>>      unsigned int length;
>>>      struct ofp_header *oh;
>>> +    enum ofperr error;
>>> +    enum ofpraw raw;
>>>
>>>      if (!msg->header) {
>>>          ofpraw_pull_assert(msg);
>>>      }
>>>
>>> +    error = ofpraw_decode(&raw, msg->header);
>>> +    if (error) {
>>> +        return error;
>>> +    }
>>> +
>>>      ofpbuf_clear(ofpacts);
>>>      if (!msg->size) {
>>>          return EOF;
>>>      }
>>>
>>> -    if (msg->size < sizeof(struct nx_flow_update_header)) {
>>> -        goto bad_len;
>>> -    }
>>> -
>>>      oh = msg->header;
>>>
>>> -    nfuh = msg->data;
>>> -    update->event = ntohs(nfuh->event);
>>> -    length = ntohs(nfuh->length);
>>> -    if (length > msg->size || length % 8) {
>>> -        goto bad_len;
>>> -    }
>>> +    switch ((int) raw) {
>>> +    case OFPRAW_ONFST13_FLOW_MONITOR_REPLY:
>>> +    case OFPRAW_NXST_FLOW_MONITOR_REPLY: {
>>> +        struct nx_flow_update_header *nfuh;
>>>
>>> -    if (update->event == NXFME_ABBREV) {
>>> -        struct nx_flow_update_abbrev *nfua;
>>> +        if (msg->size < sizeof(struct nx_flow_update_header)) {
>>> +            goto bad_len;
>>> +        }
>>>
>>> -        if (length != sizeof *nfua) {
>>> +        nfuh = msg->data;
>>> +        update->event = nx_to_ofp_flow_update_event(ntohs(nfuh->event));
>>> +        length = ntohs(nfuh->length);
>>> +        if (length > msg->size || length % 8) {
>>>              goto bad_len;
>>>          }
>>>
>>> -        nfua = ofpbuf_pull(msg, sizeof *nfua);
>>> -        update->xid = nfua->xid;
>>> -        return 0;
>>> -    } else if (update->event == NXFME_ADDED
>>> -               || update->event == NXFME_DELETED
>>> -               || update->event == NXFME_MODIFIED) {
>>> -        struct nx_flow_update_full *nfuf;
>>> -        unsigned int actions_len;
>>> -        unsigned int match_len;
>>> -        enum ofperr error;
>>> +        if (update->event == OFPFME_ABBREV) {
>>> +            struct nx_flow_update_abbrev *nfua;
>>> +
>>> +            if (length != sizeof *nfua) {
>>> +                goto bad_len;
>>> +            }
>>> +
>>> +            nfua = ofpbuf_pull(msg, sizeof *nfua);
>>> +            update->xid = nfua->xid;
>>> +            return 0;
>>> +        } else if (update->event == OFPFME_ADDED
>>> +                   || update->event == OFPFME_REMOVED
>>> +                   || update->event == OFPFME_MODIFIED) {
>>> +            struct nx_flow_update_full *nfuf;
>>> +            unsigned int actions_len;
>>> +            unsigned int match_len;
>>> +
>>> +            if (length < sizeof *nfuf) {
>>> +                goto bad_len;
>>> +            }
>>>
>>> -        if (length < sizeof *nfuf) {
>>> +            nfuf = ofpbuf_pull(msg, sizeof *nfuf);
>>> +            match_len = ntohs(nfuf->match_len);
>>> +            if (sizeof *nfuf + match_len > length) {
>>> +                goto bad_len;
>>> +            }
>>> +
>>> +            update->reason = ntohs(nfuf->reason);
>>> +            update->idle_timeout = ntohs(nfuf->idle_timeout);
>>> +            update->hard_timeout = ntohs(nfuf->hard_timeout);
>>> +            update->table_id = nfuf->table_id;
>>> +            update->cookie = nfuf->cookie;
>>> +            update->priority = ntohs(nfuf->priority);
>>> +
>>> +            if (raw == OFPRAW_ONFST13_FLOW_MONITOR_REPLY) {
>>> +                uint16_t padded_match_len = 0;
>>> +                unsigned int instructions_len;
>>> +
>>> +                error = ofputil_pull_ofp11_match(
>>> +                    msg, NULL, NULL, &update->match, &padded_match_len);
>>> +                if (error) {
>>> +                    return error;
>>> +                }
>>> +
>>> +                instructions_len = length - sizeof *nfuf -
>>> padded_match_len;
>>> +                error = ofpacts_pull_openflow_instructions(
>>> +                    msg, instructions_len, oh->version, NULL, NULL,
>>> ofpacts);
>>> +                if (error) {
>>> +                    return error;
>>> +                }
>>> +            } else {
>>> +                error = nx_pull_match(msg, match_len, &update->match,
>>> NULL,
>>> +                                      NULL, false, NULL, NULL);
>>> +                if (error) {
>>> +                    return error;
>>> +                }
>>> +
>>> +                actions_len = length - sizeof *nfuf -
>>> ROUND_UP(match_len, 8);
>>> +                error = ofpacts_pull_openflow_actions(
>>> +                    msg, actions_len, oh->version, NULL, NULL, ofpacts);
>>> +                if (error) {
>>> +                    return error;
>>> +                }
>>> +            }
>>> +
>>> +            update->ofpacts = ofpacts->data;
>>> +            update->ofpacts_len = ofpacts->size;
>>> +            return 0;
>>> +        } else {
>>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
>>> %"PRIu16,
>>> +                         ntohs(nfuh->event));
>>> +            return OFPERR_NXBRC_FM_BAD_EVENT;
>>> +        }
>>> +    }
>>> +    case OFPRAW_OFPST14_FLOW_MONITOR_REPLY: {
>>> +        struct ofp_flow_update_header *ofuh;
>>> +        uint16_t padded_match_len = 0;
>>> +
>>> +        if (msg->size < sizeof(struct ofp_flow_update_header)) {
>>>              goto bad_len;
>>>          }
>>>
>>> -        nfuf = ofpbuf_pull(msg, sizeof *nfuf);
>>> -        match_len = ntohs(nfuf->match_len);
>>> -        if (sizeof *nfuf + match_len > length) {
>>> +        ofuh = msg->data;
>>> +        update->event = ntohs(ofuh->event);
>>> +        length = ntohs(ofuh->length);
>>> +        if (length > msg->size || length % 8) {
>>>              goto bad_len;
>>>          }
>>>
>>> -        update->reason = ntohs(nfuf->reason);
>>> -        update->idle_timeout = ntohs(nfuf->idle_timeout);
>>> -        update->hard_timeout = ntohs(nfuf->hard_timeout);
>>> -        update->table_id = nfuf->table_id;
>>> -        update->cookie = nfuf->cookie;
>>> -        update->priority = ntohs(nfuf->priority);
>>> +        if (update->event == OFPFME_ABBREV) {
>>> +            struct ofp_flow_update_abbrev *ofua;
>>>
>>> -        error = nx_pull_match(msg, match_len, &update->match, NULL,
>>> NULL,
>>> -                              false, NULL, NULL);
>>> -        if (error) {
>>> -            return error;
>>> -        }
>>> +            if (length != sizeof *ofua) {
>>> +                goto bad_len;
>>> +            }
>>>
>>> -        actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
>>> -        error = ofpacts_pull_openflow_actions(msg, actions_len,
>>> oh->version,
>>> -                                              NULL, NULL, ofpacts);
>>> -        if (error) {
>>> -            return error;
>>> -        }
>>> +            ofua = ofpbuf_pull(msg, sizeof *ofua);
>>> +            update->xid = ofua->xid;
>>> +            return 0;
>>> +        } else if (update->event == OFPFME_PAUSED
>>> +                   || update->event == OFPFME_RESUMED) {
>>> +            struct ofp_flow_update_paused *ofup;
>>>
>>> -        update->ofpacts = ofpacts->data;
>>> -        update->ofpacts_len = ofpacts->size;
>>> -        return 0;
>>> -    } else {
>>> -        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
>>> %"PRIu16,
>>> -                     ntohs(nfuh->event));
>>> -        return OFPERR_NXBRC_FM_BAD_EVENT;
>>> -    }
>>> +            if (length != sizeof *ofup) {
>>> +                goto bad_len;
>>> +            }
>>> +
>>> +            ofup = ofpbuf_pull(msg, sizeof *ofup);
>>> +            return 0;
>>> +        } else if (update->event == OFPFME_INITIAL
>>> +                   || update->event == OFPFME_ADDED
>>> +                   || update->event == OFPFME_REMOVED
>>> +                   || update->event == OFPFME_MODIFIED) {
>>> +            struct ofp_flow_update_full *ofuf;
>>> +            unsigned int instructions_len;
>>> +
>>> +            if (length < sizeof *ofuf) {
>>> +                goto bad_len;
>>> +            }
>>>
>>> +            ofuf = ofpbuf_pull(msg, sizeof *ofuf);
>>> +            if (sizeof *ofuf > length) {
>>> +                goto bad_len;
>>> +            }
>>> +
>>> +            update->reason = ofuf->reason;
>>> +            update->idle_timeout = ntohs(ofuf->idle_timeout);
>>> +            update->hard_timeout = ntohs(ofuf->hard_timeout);
>>> +            update->table_id = ofuf->table_id;
>>> +            update->cookie = ofuf->cookie;
>>> +            update->priority = ntohs(ofuf->priority);
>>> +
>>> +            error = ofputil_pull_ofp11_match(
>>> +                msg, NULL, NULL, &update->match, &padded_match_len);
>>> +            if (error) {
>>> +                return error;
>>> +            }
>>> +
>>> +            instructions_len = length - sizeof *ofuf - padded_match_len;
>>> +            error = ofpacts_pull_openflow_instructions(
>>> +                msg, instructions_len, oh->version, NULL, NULL,
>>> ofpacts);
>>> +            if (error) {
>>> +                return error;
>>> +            }
>>> +
>>> +            update->ofpacts = ofpacts->data;
>>> +            update->ofpacts_len = ofpacts->size;
>>> +            return 0;
>>> +        } else {
>>> +            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event
>>> %"PRIu16,
>>> +                         ntohs(ofuh->event));
>>> +            return OFPERR_NXBRC_FM_BAD_EVENT;
>>> +        }
>>> +    }
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>>  bad_len:
>>> -    VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has %"PRIu32" "
>>> -                 "leftover bytes at end", msg->size);
>>> +    VLOG_WARN_RL(&rl, "%s reply has %"PRIu32" leftover bytes at end",
>>> +                ofpraw_get_name(raw), msg->size);
>>>      return OFPERR_OFPBRC_BAD_LEN;
>>>  }
>>>
>>>  uint32_t
>>>  ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
>>>  {
>>> -    const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
>>> +    enum ofperr error;
>>> +    enum ofpraw raw;
>>>
>>> -    return ntohl(cancel->id);
>>> +    error = ofpraw_decode(&raw, oh);
>>> +    if (error) {
>>> +        return error;
>>> +    }
>>> +
>>> +    switch ((int) raw) {
>>> +    case OFPRAW_ONFT13_FLOW_MONITOR_CANCEL:
>>> +    case OFPRAW_NXT_FLOW_MONITOR_CANCEL: {
>>> +        const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
>>> +        return ntohl(cancel->id);
>>> +    }
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>>  }
>>>
>>>  struct ofpbuf *
>>> @@ -666,9 +1051,99 @@ ofputil_encode_flow_monitor_cancel(uint32_t id,
>>> enum ofputil_protocol protocol)
>>>      enum ofp_version version =
>>> ofputil_protocol_to_ofp_version(protocol);
>>>      struct ofpbuf *msg;
>>>
>>> -    msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version, 0);
>>> -    nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
>>> -    nfmc->id = htonl(id);
>>> +    switch (version) {
>>> +    case OFP10_VERSION:
>>> +    case OFP11_VERSION:
>>> +    case OFP12_VERSION:
>>> +    case OFP13_VERSION: {
>>> +        if (version == OFP13_VERSION) {
>>> +            msg = ofpraw_alloc(OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
>>> version, 0);
>>> +        } else {
>>> +            msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version,
>>> 0);
>>> +        }
>>> +        nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
>>> +        nfmc->id = htonl(id);
>>> +        break;
>>> +    }
>>> +    case OFP14_VERSION:
>>> +    case OFP15_VERSION: {
>>> +        struct ofp14_flow_monitor_request *ofmr;
>>> +
>>> +        msg = ofpbuf_new(0);
>>> +
>>> +        ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version, msg);
>>> +
>>> +        size_t start_ofs = msg->size;
>>> +        ofpbuf_put_zeros(msg, sizeof *ofmr);
>>> +
>>> +        ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
>>> +        ofmr->monitor_id = htonl(id);
>>> +        ofmr->command = OFPFMC_DELETE;
>>> +        break;
>>> +    }
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +    return msg;
>>> +}
>>> +
>>> +struct ofpbuf *
>>> +ofputil_encode_flow_monitor_pause(enum ofp_flow_update_event command,
>>> +                                  enum ofputil_protocol protocol)
>>> +{
>>> +    struct ofpbuf *msg;
>>> +    enum ofp_version version =
>>> ofputil_protocol_to_ofp_version(protocol);
>>> +
>>> +    if (!(command == OFPFME_PAUSED || command == OFPFME_RESUMED)) {
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +
>>> +    switch (version) {
>>> +    case OFP10_VERSION:
>>> +    case OFP11_VERSION:
>>> +    case OFP12_VERSION:
>>> +        if (command == OFPFME_PAUSED) {
>>> +            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>>> +                                   version, htonl(0), 0);
>>> +        } else {
>>> +            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>>> +                                   version, htonl(0), 0);
>>> +        }
>>> +        break;
>>> +    case OFP13_VERSION:
>>> +        if (command == OFPFME_PAUSED) {
>>> +            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
>>> +                                   version, htonl(0), 0);
>>> +        } else {
>>> +            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
>>> +                                   version, htonl(0), 0);
>>> +        }
>>> +        break;
>>> +    case OFP14_VERSION:
>>> +    case OFP15_VERSION: {
>>> +        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
>>> version,
>>> +                                                   htonl(0), 1024);
>>> +        struct ofp_flow_update_header *ofuh;
>>> +        size_t start_ofs = msg->size;
>>> +
>>> +        struct ofp_flow_update_paused *ofup;
>>> +
>>> +        ofpbuf_put_zeros(msg, sizeof *ofup);
>>> +        ofup = ofpbuf_at_assert(msg, start_ofs, sizeof *ofup);
>>> +        ofup->event = htons(command);
>>> +        ofup->length = htons(8);
>>> +
>>> +        ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
>>> +        ofuh->length = htons(msg->size - start_ofs);
>>> +        ofuh->event = htons(command);
>>> +
>>> +        ofpmsg_update_length(msg);
>>> +        break;
>>> +    }
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +
>>>      return msg;
>>>  }
>>>
>>> @@ -679,8 +1154,25 @@ ofputil_start_flow_update(struct ovs_list *replies,
>>>      struct ofpbuf *msg;
>>>      enum ofp_version version =
>>> ofputil_protocol_to_ofp_version(protocol);
>>>
>>> -    msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
>>> -                           htonl(0), 1024);
>>> +    switch (version) {
>>> +    case OFP10_VERSION:
>>> +    case OFP11_VERSION:
>>> +    case OFP12_VERSION:
>>> +        msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
>>> +                               htonl(0), 1024);
>>> +        break;
>>> +    case OFP13_VERSION:
>>> +        msg = ofpraw_alloc_xid(OFPRAW_ONFST13_FLOW_MONITOR_REPLY,
>>> version,
>>> +                               htonl(0), 1024);
>>> +        break;
>>> +    case OFP14_VERSION:
>>> +    case OFP15_VERSION:
>>> +        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
>>> version,
>>> +                               htonl(0), 1024);
>>> +        break;
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>>
>>>      ovs_list_init(replies);
>>>      ovs_list_push_back(replies, &msg->list_node);
>>> @@ -695,7 +1187,6 @@ ofputil_append_flow_update(const struct
>>> ofputil_flow_update *update,
>>>          CONST_CAST(struct ofputil_flow_update *, update);
>>>      const struct tun_table *orig_tun_table;
>>>      enum ofp_version version = ofpmp_version(replies);
>>> -    struct nx_flow_update_header *nfuh;
>>>      struct ofpbuf *msg;
>>>      size_t start_ofs;
>>>
>>> @@ -705,32 +1196,80 @@ ofputil_append_flow_update(const struct
>>> ofputil_flow_update *update,
>>>      msg = ofpbuf_from_list(ovs_list_back(replies));
>>>      start_ofs = msg->size;
>>>
>>> -    if (update->event == NXFME_ABBREV) {
>>> -        struct nx_flow_update_abbrev *nfua;
>>> +    switch (version) {
>>> +        case OFP10_VERSION:
>>> +        case OFP11_VERSION:
>>> +        case OFP12_VERSION:
>>> +        case OFP13_VERSION: {
>>> +             struct nx_flow_update_header *nfuh;
>>>
>>> -        nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
>>> -        nfua->xid = update->xid;
>>> -    } else {
>>> -        struct nx_flow_update_full *nfuf;
>>> -        int match_len;
>>> +            if (update->event == OFPFME_ABBREV) {
>>> +                struct nx_flow_update_abbrev *nfua;
>>>
>>> -        ofpbuf_put_zeros(msg, sizeof *nfuf);
>>> -        match_len = nx_put_match(msg, &update->match, htonll(0),
>>> htonll(0));
>>> -        ofpacts_put_openflow_actions(update->ofpacts,
>>> update->ofpacts_len, msg,
>>> -                                     version);
>>> -        nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
>>> -        nfuf->reason = htons(update->reason);
>>> -        nfuf->priority = htons(update->priority);
>>> -        nfuf->idle_timeout = htons(update->idle_timeout);
>>> -        nfuf->hard_timeout = htons(update->hard_timeout);
>>> -        nfuf->match_len = htons(match_len);
>>> -        nfuf->table_id = update->table_id;
>>> -        nfuf->cookie = update->cookie;
>>> -    }
>>> +                nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
>>> +                nfua->xid = update->xid;
>>> +            } else {
>>> +                struct nx_flow_update_full *nfuf;
>>> +                int match_len;
>>> +
>>> +                ofpbuf_put_zeros(msg, sizeof *nfuf);
>>> +                if (version == OFP13_VERSION) {
>>> +                    match_len = oxm_put_match(msg, &update->match,
>>> version);
>>> +                    ofpacts_put_openflow_instructions(
>>> +                        update->ofpacts, update->ofpacts_len, msg,
>>> version);
>>> +                } else {
>>> +                    match_len = nx_put_match(msg, &update->match,
>>> +                                             htonll(0), htonll(0));
>>> +                    ofpacts_put_openflow_actions(
>>> +                        update->ofpacts, update->ofpacts_len, msg,
>>> version);
>>> +                }
>>> +                nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
>>> +                nfuf->reason = htons(update->reason);
>>> +                nfuf->priority = htons(update->priority);
>>> +                nfuf->idle_timeout = htons(update->idle_timeout);
>>> +                nfuf->hard_timeout = htons(update->hard_timeout);
>>> +                nfuf->match_len = htons(match_len);
>>> +                nfuf->table_id = update->table_id;
>>> +                nfuf->cookie = update->cookie;
>>> +            }
>>> +
>>> +            nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
>>> +            nfuh->length = htons(msg->size - start_ofs);
>>> +            nfuh->event =
>>> htons(ofp_to_nx_flow_update_event(update->event));
>>> +            break;
>>> +        }
>>> +        case OFP14_VERSION:
>>> +        case OFP15_VERSION: {
>>> +            struct ofp_flow_update_header *ofuh;
>>>
>>> -    nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
>>> -    nfuh->length = htons(msg->size - start_ofs);
>>> -    nfuh->event = htons(update->event);
>>> +            if (update->event == OFPFME_ABBREV) {
>>> +                struct ofp_flow_update_abbrev *ofua;
>>> +
>>> +                ofua = ofpbuf_put_zeros(msg, sizeof *ofua);
>>> +                ofua->xid = update->xid;
>>> +            } else {
>>> +                struct ofp_flow_update_full *ofuf;
>>> +
>>> +                ofpbuf_put_zeros(msg, sizeof *ofuf);
>>> +                oxm_put_match(msg, &update->match, version);
>>> +                ofpacts_put_openflow_instructions(update->ofpacts,
>>> +                                                  update->ofpacts_len,
>>> +                                                  msg, version);
>>> +                ofuf = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuf);
>>> +                ofuf->reason = update->reason;
>>> +                ofuf->priority = htons(update->priority);
>>> +                ofuf->idle_timeout = htons(update->idle_timeout);
>>> +                ofuf->hard_timeout = htons(update->hard_timeout);
>>> +                ofuf->table_id = update->table_id;
>>> +                ofuf->cookie = update->cookie;
>>> +            }
>>> +
>>> +            ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
>>> +            ofuh->length = htons(msg->size - start_ofs);
>>> +            ofuh->event = htons(update->event);
>>> +            break;
>>> +        }
>>> +    }
>>>
>>>      ofpmp_postappend(replies, start_ofs);
>>>      update_->match.flow.tunnel.metadata.tab = orig_tun_table;
>>> @@ -746,24 +1285,37 @@ ofputil_flow_update_format(struct ds *s,
>>>
>>>      ds_put_cstr(s, "\n event=");
>>>      switch (update->event) {
>>> -    case NXFME_ADDED:
>>> +    case OFPFME_INITIAL:
>>> +        ds_put_cstr(s, "INITIAL");
>>> +        break;
>>> +
>>> +    case OFPFME_ADDED:
>>>          ds_put_cstr(s, "ADDED");
>>>          break;
>>>
>>> -    case NXFME_DELETED:
>>> +    case OFPFME_REMOVED:
>>>          ds_put_format(s, "DELETED reason=%s",
>>>                        ofp_flow_removed_reason_to_string(update->reason,
>>>                                                          reasonbuf,
>>>                                                          sizeof
>>> reasonbuf));
>>>          break;
>>>
>>> -    case NXFME_MODIFIED:
>>> +    case OFPFME_MODIFIED:
>>>          ds_put_cstr(s, "MODIFIED");
>>>          break;
>>>
>>> -    case NXFME_ABBREV:
>>> +    case OFPFME_ABBREV:
>>>          ds_put_format(s, "ABBREV xid=0x%"PRIx32, ntohl(update->xid));
>>>          return;
>>> +
>>> +    case OFPFME_PAUSED:
>>> +        ds_put_cstr(s, "PAUSED");
>>> +        return;
>>> +
>>> +    case OFPFME_RESUMED:
>>> +        ds_put_cstr(s, "RESUMED");
>>> +        return;
>>> +
>>>      }
>>>
>>>      ds_put_format(s, " table=");
>>> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
>>> index b0facbf9f..bd37fa17a 100644
>>> --- a/lib/ofp-print.c
>>> +++ b/lib/ofp-print.c
>>> @@ -744,10 +744,10 @@ ofp_print_nxt_flow_monitor_cancel(struct ds
>>> *string,
>>>  }
>>>
>>>  static enum ofperr
>>> -ofp_print_nxst_flow_monitor_request(struct ds *string,
>>> -                                    const struct ofp_header *oh,
>>> -                                    const struct ofputil_port_map
>>> *port_map,
>>> -                                    const struct ofputil_table_map
>>> *table_map)
>>> +ofp_print_flow_monitor_request(struct ds *string,
>>> +                               const struct ofp_header *oh,
>>> +                               const struct ofputil_port_map *port_map,
>>> +                               const struct ofputil_table_map
>>> *table_map)
>>>  {
>>>      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
>>>      for (;;) {
>>> @@ -765,10 +765,10 @@ ofp_print_nxst_flow_monitor_request(struct ds
>>> *string,
>>>  }
>>>
>>>  static enum ofperr
>>> -ofp_print_nxst_flow_monitor_reply(struct ds *string,
>>> -                                  const struct ofp_header *oh,
>>> -                                  const struct ofputil_port_map
>>> *port_map,
>>> -                                  const struct ofputil_table_map
>>> *table_map)
>>> +ofp_print_flow_monitor_reply(struct ds *string,
>>> +                             const struct ofp_header *oh,
>>> +                             const struct ofputil_port_map *port_map,
>>> +                             const struct ofputil_table_map *table_map)
>>>  {
>>>      uint64_t ofpacts_stub[1024 / 8];
>>>      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
>>> @@ -1147,12 +1147,12 @@ ofp_to_string__(const struct ofp_header *oh,
>>>          break;
>>>
>>>      case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
>>> -        return ofp_print_nxst_flow_monitor_request(string, msg,
>>> port_map,
>>> -                                                   table_map);
>>> +        return ofp_print_flow_monitor_request(string, msg, port_map,
>>> +                                              table_map);
>>>
>>>      case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
>>> -        return ofp_print_nxst_flow_monitor_reply(string, msg, port_map,
>>> -                                                 table_map);
>>> +        return ofp_print_flow_monitor_reply(string, msg, port_map,
>>> +                                            table_map);
>>>
>>>      case OFPTYPE_BUNDLE_CONTROL:
>>>          return ofp_print_bundle_ctrl(string, msg);
>>> diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
>>> index c14834f84..325f45966 100644
>>> --- a/ofproto/connmgr.c
>>> +++ b/ofproto/connmgr.c
>>> @@ -2099,6 +2099,7 @@ ofmonitor_create(const struct
>>> ofputil_flow_monitor_request *request,
>>>      m->id = request->id;
>>>      m->flags = request->flags;
>>>      m->out_port = request->out_port;
>>> +    m->out_group = request->out_group;
>>>      m->table_id = request->table_id;
>>>      minimatch_init(&m->match, &request->match);
>>>
>>> @@ -2134,7 +2135,7 @@ ofmonitor_destroy(struct ofmonitor *m)
>>>
>>>  void
>>>  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
>>> -                 enum nx_flow_update_event event,
>>> +                 enum ofp_flow_update_event event,
>>>                   enum ofp_flow_removed_reason reason,
>>>                   const struct ofconn *abbrev_ofconn, ovs_be32
>>> abbrev_xid,
>>>                   const struct rule_actions *old_actions)
>>> @@ -2144,39 +2145,42 @@ ofmonitor_report(struct connmgr *mgr, struct
>>> rule *rule,
>>>          return;
>>>      }
>>>
>>> -    enum nx_flow_monitor_flags update;
>>> +    enum ofp14_flow_monitor_flags update;
>>>      switch (event) {
>>> -    case NXFME_ADDED:
>>> -        update = NXFMF_ADD;
>>> +    case OFPFME_ADDED:
>>> +        update = OFPFMF_ADD;
>>>          rule->add_seqno = rule->modify_seqno = monitor_seqno++;
>>>          break;
>>>
>>> -    case NXFME_DELETED:
>>> -        update = NXFMF_DELETE;
>>> +    case OFPFME_REMOVED:
>>> +        update = OFPFMF_REMOVED;
>>>          break;
>>>
>>> -    case NXFME_MODIFIED:
>>> -        update = NXFMF_MODIFY;
>>> +    case OFPFME_MODIFIED:
>>> +        update = OFPFMF_MODIFY;
>>>          rule->modify_seqno = monitor_seqno++;
>>>          break;
>>>
>>>      default:
>>> -    case NXFME_ABBREV:
>>> +    case OFPFME_INITIAL:
>>> +    case OFPFME_PAUSED:
>>> +    case OFPFME_RESUMED:
>>> +    case OFPFME_ABBREV:
>>>          OVS_NOT_REACHED();
>>>      }
>>>
>>>      struct ofconn *ofconn;
>>>      LIST_FOR_EACH (ofconn, connmgr_node, &mgr->conns) {
>>>          if (ofconn->monitor_paused) {
>>> -            /* Only send NXFME_DELETED notifications for flows that
>>> were added
>>> +            /* Only send OFPFME_REMOVED notifications for flows that
>>> were added
>>>               * before we paused. */
>>> -            if (event != NXFME_DELETED
>>> +            if (event != OFPFME_REMOVED
>>>                  || rule->add_seqno > ofconn->monitor_paused) {
>>>                  continue;
>>>              }
>>>          }
>>>
>>> -        enum nx_flow_monitor_flags flags = 0;
>>> +        enum ofp14_flow_monitor_flags flags = 0;
>>>          struct ofmonitor *m;
>>>          HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
>>>              if (m->flags & update
>>> @@ -2186,6 +2190,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>>> *rule,
>>>                          && ofpacts_output_to_port(old_actions->ofpacts,
>>>
>>>  old_actions->ofpacts_len,
>>>                                                    m->out_port)))
>>> +                && ofproto_rule_has_out_group(rule, m->out_group)
>>>                  && cls_rule_is_loose_match(&rule->cr, &m->match)) {
>>>                  flags |= m->flags;
>>>              }
>>> @@ -2198,12 +2203,12 @@ ofmonitor_report(struct connmgr *mgr, struct
>>> rule *rule,
>>>                  ofconn->sent_abbrev_update = false;
>>>              }
>>>
>>> -            if (flags & NXFMF_OWN || ofconn != abbrev_ofconn
>>> +            if (flags & OFPFMF_ONLY_OWN || ofconn != abbrev_ofconn
>>>                  || ofconn->monitor_paused) {
>>>                  struct ofputil_flow_update fu;
>>>
>>>                  fu.event = event;
>>> -                fu.reason = event == NXFME_DELETED ? reason : 0;
>>> +                fu.reason = event == OFPFME_REMOVED ? reason : 0;
>>>                  fu.table_id = rule->table_id;
>>>                  fu.cookie = rule->flow_cookie;
>>>                  minimatch_expand(&rule->cr.match, &fu.match);
>>> @@ -2214,7 +2219,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>>> *rule,
>>>                  fu.hard_timeout = rule->hard_timeout;
>>>                  ovs_mutex_unlock(&rule->mutex);
>>>
>>> -                if (flags & NXFMF_ACTIONS) {
>>> +                if (flags & OFPFMF_INSTRUCTIONS) {
>>>                      const struct rule_actions *actions
>>>                          = rule_get_actions(rule);
>>>                      fu.ofpacts = actions->ofpacts;
>>> @@ -2228,7 +2233,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule
>>> *rule,
>>>              } else if (!ofconn->sent_abbrev_update) {
>>>                  struct ofputil_flow_update fu;
>>>
>>> -                fu.event = NXFME_ABBREV;
>>> +                fu.event = OFPFME_ABBREV;
>>>                  fu.xid = abbrev_xid;
>>>                  ofputil_append_flow_update(&fu, &ofconn->updates,
>>>
>>> ofproto_get_tun_tab(rule->ofproto));
>>> @@ -2263,9 +2268,8 @@ ofmonitor_flush(struct connmgr *mgr)
>>>              COVERAGE_INC(ofmonitor_pause);
>>>              ofconn->monitor_paused = monitor_seqno++;
>>>              protocol = ofconn_get_protocol(ofconn);
>>> -            struct ofpbuf *pause = ofpraw_alloc_xid(
>>> -                OFPRAW_NXT_FLOW_MONITOR_PAUSED,
>>> -                ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
>>> +            struct ofpbuf *pause = ofputil_encode_flow_monitor_pause(
>>> +                OFPFME_PAUSED,protocol);
>>>              ofconn_send(ofconn, pause, counter);
>>>          }
>>>      }
>>> @@ -2289,9 +2293,8 @@ ofmonitor_resume(struct ofconn *ofconn)
>>>                                        ofconn_get_protocol(ofconn));
>>>
>>>      protocol = ofconn_get_protocol(ofconn);
>>> -    struct ofpbuf *resumed = ofpraw_alloc_xid(
>>> -            OFPRAW_NXT_FLOW_MONITOR_RESUMED,
>>> -            ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
>>> +    struct ofpbuf *resumed = ofputil_encode_flow_monitor_pause(
>>> +        OFPFME_RESUMED, protocol);
>>>      ovs_list_push_back(&msgs, &resumed->list_node);
>>>      ofconn_send_replies(ofconn, &msgs);
>>>
>>> diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
>>> index 56fdc3504..3471d38f9 100644
>>> --- a/ofproto/connmgr.h
>>> +++ b/ofproto/connmgr.h
>>> @@ -168,10 +168,11 @@ struct ofmonitor {
>>>      struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */
>>>      uint32_t id;
>>>
>>> -    enum nx_flow_monitor_flags flags;
>>> +    enum ofp14_flow_monitor_flags flags;
>>>
>>>      /* Matching. */
>>>      ofp_port_t out_port;
>>> +    uint32_t out_group;
>>>      uint8_t table_id;
>>>      struct minimatch match;
>>>  };
>>> @@ -187,7 +188,8 @@ void ofmonitor_destroy(struct ofmonitor *)
>>>      OVS_REQUIRES(ofproto_mutex);
>>>
>>>  void ofmonitor_report(struct connmgr *, struct rule *,
>>> -                      enum nx_flow_update_event, enum
>>> ofp_flow_removed_reason,
>>> +                      enum ofp_flow_update_event event,
>>> +                      enum ofp_flow_removed_reason,
>>>                        const struct ofconn *abbrev_ofconn, ovs_be32
>>> abbrev_xid,
>>>                        const struct rule_actions *old_actions)
>>>      OVS_REQUIRES(ofproto_mutex);
>>> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
>>> index 57c7d17cb..373526738 100644
>>> --- a/ofproto/ofproto-provider.h
>>> +++ b/ofproto/ofproto-provider.h
>>> @@ -419,7 +419,7 @@ struct rule {
>>>       * 'add_seqno' is the sequence number when this rule was created.
>>>       * 'modify_seqno' is the sequence number when this rule was last
>>> modified.
>>>       * See 'monitor_seqno' in connmgr.c for more information. */
>>> -    enum nx_flow_monitor_flags monitor_flags
>>> OVS_GUARDED_BY(ofproto_mutex);
>>> +    enum ofp14_flow_monitor_flags monitor_flags
>>> OVS_GUARDED_BY(ofproto_mutex);
>>>      uint64_t add_seqno OVS_GUARDED_BY(ofproto_mutex);
>>>      uint64_t modify_seqno OVS_GUARDED_BY(ofproto_mutex);
>>>
>>> @@ -480,6 +480,8 @@ const struct rule_actions *rule_actions_create(const
>>> struct ofpact *, size_t);
>>>  void rule_actions_destroy(const struct rule_actions *);
>>>  bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port)
>>>      OVS_REQUIRES(ofproto_mutex);
>>> +bool ofproto_rule_has_out_group(const struct rule *rule, uint32_t
>>> group_id)
>>> +    OVS_REQUIRES(ofproto_mutex);
>>>
>>>  #define DECL_OFPROTO_COLLECTION(TYPE, NAME)
>>>  \
>>>      DECL_OBJECT_COLLECTION(TYPE, NAME)
>>> \
>>> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
>>> index 11aadbf20..980d4d53b 100644
>>> --- a/ofproto/ofproto.c
>>> +++ b/ofproto/ofproto.c
>>> @@ -3157,7 +3157,7 @@ ofproto_rule_has_out_port(const struct rule *rule,
>>> ofp_port_t port)
>>>  }
>>>
>>>  /* Returns true if 'rule' has group and equals group_id. */
>>> -static bool
>>> +bool
>>>  ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
>>>      OVS_REQUIRES(ofproto_mutex)
>>>  {
>>> @@ -5215,7 +5215,7 @@ add_flow_finish(struct ofproto *ofproto, struct
>>> ofproto_flow_mod *ofm,
>>>      if (old_rule) {
>>>          ovsrcu_postpone(remove_rule_rcu, old_rule);
>>>      } else {
>>> -        ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0,
>>> +        ofmonitor_report(ofproto->connmgr, new_rule, OFPFME_ADDED, 0,
>>>                           req ? req->ofconn : NULL,
>>>                           req ? req->request->xid : 0, NULL);
>>>
>>> @@ -5630,8 +5630,8 @@ replace_rule_finish(struct ofproto *ofproto,
>>> struct ofproto_flow_mod *ofm,
>>>          learned_cookies_dec(ofproto, old_actions, dead_cookies);
>>>
>>>          if (replaced_rule) {
>>> -            enum nx_flow_update_event event = ofm->command == OFPFC_ADD
>>> -                ? NXFME_ADDED : NXFME_MODIFIED;
>>> +            enum ofp_flow_update_event event = ofm->command == OFPFC_ADD
>>> +                ? OFPFME_ADDED : OFPFME_MODIFIED;
>>>
>>>              bool changed_cookie = (new_rule->flow_cookie
>>>                                     != old_rule->flow_cookie);
>>> @@ -5641,7 +5641,7 @@ replace_rule_finish(struct ofproto *ofproto,
>>> struct ofproto_flow_mod *ofm,
>>>                                                    old_actions->ofpacts,
>>>
>>>  old_actions->ofpacts_len);
>>>
>>> -            if (event != NXFME_MODIFIED || changed_actions
>>> +            if (event != OFPFME_MODIFIED || changed_actions
>>>                  || changed_cookie) {
>>>                  ofmonitor_report(ofproto->connmgr, new_rule, event, 0,
>>>                                   req ? req->ofconn : NULL,
>>> @@ -5650,7 +5650,7 @@ replace_rule_finish(struct ofproto *ofproto,
>>> struct ofproto_flow_mod *ofm,
>>>              }
>>>          } else {
>>>              /* XXX: This is slight duplication with
>>> delete_flows_finish__() */
>>> -            ofmonitor_report(ofproto->connmgr, old_rule, NXFME_DELETED,
>>> +            ofmonitor_report(ofproto->connmgr, old_rule, OFPFME_REMOVED,
>>>                               OFPRR_EVICTION,
>>>                               req ? req->ofconn : NULL,
>>>                               req ? req->request->xid : 0, NULL);
>>> @@ -5931,7 +5931,7 @@ delete_flows_finish__(struct ofproto *ofproto,
>>>               * before the rule is actually destroyed. */
>>>              rule->removed_reason = reason;
>>>
>>> -            ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED,
>>> reason,
>>> +            ofmonitor_report(ofproto->connmgr, rule, OFPFME_REMOVED,
>>> reason,
>>>                               req ? req->ofconn : NULL,
>>>                               req ? req->request->xid : 0, NULL);
>>>
>>> @@ -6337,7 +6337,7 @@ handle_barrier_request(struct ofconn *ofconn,
>>> const struct ofp_header *oh)
>>>
>>>  static void
>>>  ofproto_compose_flow_refresh_update(const struct rule *rule,
>>> -                                    enum nx_flow_monitor_flags flags,
>>> +                                    enum ofp14_flow_monitor_flags flags,
>>>                                      struct ovs_list *msgs,
>>>                                      const struct tun_table *tun_table,
>>>                                      enum ofputil_protocol protocol)
>>> @@ -6346,8 +6346,9 @@ ofproto_compose_flow_refresh_update(const struct
>>> rule *rule,
>>>      const struct rule_actions *actions;
>>>      struct ofputil_flow_update fu;
>>>
>>> -    fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
>>> -                ? NXFME_ADDED : NXFME_MODIFIED);
>>> +    fu.event = flags & OFPFMF_INITIAL ? OFPFME_INITIAL :
>>> +               flags & OFPFMF_ADD ?
>>> +                         OFPFME_ADDED : OFPFME_MODIFIED;
>>>      fu.reason = 0;
>>>      ovs_mutex_lock(&rule->mutex);
>>>      fu.idle_timeout = rule->idle_timeout;
>>> @@ -6358,7 +6359,7 @@ ofproto_compose_flow_refresh_update(const struct
>>> rule *rule,
>>>      minimatch_expand(&rule->cr.match, &fu.match);
>>>      fu.priority = rule->cr.priority;
>>>
>>> -    actions = flags & NXFMF_ACTIONS ? rule_get_actions(rule) : NULL;
>>> +    actions = flags & OFPFMF_INSTRUCTIONS ? rule_get_actions(rule) :
>>> NULL;
>>>      fu.ofpacts = actions ? actions->ofpacts : NULL;
>>>      fu.ofpacts_len = actions ? actions->ofpacts_len : 0;
>>>
>>> @@ -6377,7 +6378,7 @@ ofmonitor_compose_refresh_updates(struct
>>> rule_collection *rules,
>>>      struct rule *rule;
>>>
>>>      RULE_COLLECTION_FOR_EACH (rule, rules) {
>>> -        enum nx_flow_monitor_flags flags = rule->monitor_flags;
>>> +        enum ofp14_flow_monitor_flags flags = rule->monitor_flags;
>>>          rule->monitor_flags = 0;
>>>
>>>          ofproto_compose_flow_refresh_update(rule, flags, msgs,
>>> @@ -6391,7 +6392,7 @@ ofproto_collect_ofmonitor_refresh_rule(const
>>> struct ofmonitor *m,
>>>                                         struct rule_collection *rules)
>>>      OVS_REQUIRES(ofproto_mutex)
>>>  {
>>> -    enum nx_flow_monitor_flags update;
>>> +    enum ofp14_flow_monitor_flags update;
>>>
>>>      if (rule_is_hidden(rule)) {
>>>          return;
>>> @@ -6401,11 +6402,15 @@ ofproto_collect_ofmonitor_refresh_rule(const
>>> struct ofmonitor *m,
>>>          return;
>>>      }
>>>
>>> +    if (!ofproto_rule_has_out_group(rule, m->out_group)) {
>>> +        return;
>>> +    }
>>> +
>>>      if (seqno) {
>>>          if (rule->add_seqno > seqno) {
>>> -            update = NXFMF_ADD | NXFMF_MODIFY;
>>> +            update = OFPFMF_ADD | OFPFMF_MODIFY;
>>>          } else if (rule->modify_seqno > seqno) {
>>> -            update = NXFMF_MODIFY;
>>> +            update = OFPFMF_MODIFY;
>>>          } else {
>>>              return;
>>>          }
>>> @@ -6414,13 +6419,13 @@ ofproto_collect_ofmonitor_refresh_rule(const
>>> struct ofmonitor *m,
>>>              return;
>>>          }
>>>      } else {
>>> -        update = NXFMF_INITIAL;
>>> +        update = OFPFMF_INITIAL;
>>>      }
>>>
>>>      if (!rule->monitor_flags) {
>>>          rule_collection_add(rules, rule);
>>>      }
>>> -    rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
>>> +    rule->monitor_flags |= update | (m->flags & OFPFMF_INSTRUCTIONS);
>>>  }
>>>
>>>  static void
>>> @@ -6449,7 +6454,7 @@ ofproto_collect_ofmonitor_initial_rules(struct
>>> ofmonitor *m,
>>>                                          struct rule_collection *rules)
>>>      OVS_REQUIRES(ofproto_mutex)
>>>  {
>>> -    if (m->flags & NXFMF_INITIAL) {
>>> +    if (m->flags & OFPFMF_INITIAL) {
>>>          ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
>>>      }
>>>  }
>>> @@ -6512,16 +6517,50 @@ handle_flow_monitor_request(struct ofconn
>>> *ofconn, const struct ovs_list *msgs)
>>>              }
>>>
>>>              struct ofmonitor *m;
>>> -            error = ofmonitor_create(&request, ofconn, &m);
>>> -            if (error) {
>>> -                goto error;
>>> +            switch (request.command) {
>>> +            case OFPFMC_ADD: {
>>> +                error = ofmonitor_create(&request, ofconn, &m);
>>> +                if (error) {
>>> +                    goto error;
>>> +                }
>>> +
>>> +                if (n_monitors >= allocated_monitors) {
>>> +                    monitors = x2nrealloc(monitors, &allocated_monitors,
>>> +                                          sizeof *monitors);
>>> +                }
>>> +                monitors[n_monitors++] = m;
>>> +                break;
>>>              }
>>> +            case OFPFMC_MODIFY:
>>> +                /* Modify operation is to delete old monitor and create
>>> a
>>> +                 * new one. */
>>> +                m = ofmonitor_lookup(ofconn, request.id);
>>> +                if (!m) {
>>> +                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
>>> +                    goto error;
>>> +                }
>>> +                ofmonitor_destroy(m);
>>> +
>>> +                error = ofmonitor_create(&request, ofconn, &m);
>>> +                if (error) {
>>> +                    goto error;
>>> +                }
>>>
>>> -            if (n_monitors >= allocated_monitors) {
>>> -                monitors = x2nrealloc(monitors, &allocated_monitors,
>>> -                                      sizeof *monitors);
>>> +                if (n_monitors >= allocated_monitors) {
>>> +                    monitors = x2nrealloc(monitors, &allocated_monitors,
>>> +                                          sizeof *monitors);
>>> +                }
>>> +                monitors[n_monitors++] = m;
>>> +                break;
>>> +            case OFPFMC_DELETE:
>>> +                m = ofmonitor_lookup(ofconn, request.id);
>>> +                if (!m) {
>>> +                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
>>> +                    goto error;
>>> +                }
>>> +                ofmonitor_destroy(m);
>>> +                break;
>>>              }
>>> -            monitors[n_monitors++] = m;
>>>              continue;
>>>
>>>          error:
>>> diff --git a/tests/ofp-print.at b/tests/ofp-print.at
>>> index 2c7e163bd..ab5c37649 100644
>>> --- a/tests/ofp-print.at
>>> +++ b/tests/ofp-print.at
>>> @@ -3258,32 +3258,89 @@ NXT_SET_CONTROLLER_ID (xid=0x3): id=123
>>>  ])
>>>  AT_CLEANUP
>>>
>>> -AT_SETUP([NXT_FLOW_MONITOR_CANCEL])
>>> +AT_SETUP([FLOW_MONITOR_CANCEL])
>>>  AT_KEYWORDS([ofp-print])
>>> +
>>> +dnl OpenFlow 1.0-1.2
>>>  AT_CHECK([ovs-ofctl ofp-print "\
>>>  01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \
>>>  01 02 30 40 \
>>>  "], [0], [dnl
>>>  NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640
>>>  ])
>>> +
>>> +dnl OpenFlow 1.3
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +04 04 00 14 00 00 00 06 4f 4e 46 00 00 00 07 4e \
>>> +01 02 30 40 \
>>> +"], [0], [dnl
>>> +ONFT_FLOW_MONITOR_CANCEL (OF1.3) (xid=0x6): id=16920640
>>> +])
>>> +
>>> +dnl OpenFlow 1.4+
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +05 12 00 28 00 00 00 04 00 10 00 00 00 00 00 00 \
>>> +01 02 30 40 00 00 00 00 00 00 00 00 00 00 00 02 \
>>> +00 01 00 04 00 00 00 00 \
>>> +"], [0], [dnl
>>> +OFPST_FLOW_MONITOR request (OF1.4) (xid=0x4):
>>> + id=16920640 command=delete
>>> +])
>>> +
>>>  AT_CLEANUP
>>>
>>> -AT_SETUP([NXT_FLOW_MONITOR_PAUSED])
>>> +AT_SETUP([FLOW_MONITOR_PAUSED])
>>>  AT_KEYWORDS([ofp-print])
>>> +
>>> +dnl OpenFlow 1.0-1.2
>>>  AT_CHECK([ovs-ofctl ofp-print "\
>>>  01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \
>>>  "], [0], [dnl
>>>  NXT_FLOW_MONITOR_PAUSED (xid=0x3):
>>>  ])
>>> +
>>> +dnl OpenFlow 1.3
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 4F \
>>> +"], [0], [dnl
>>> +ONFT_FLOW_MONITOR_PAUSED (OF1.3) (xid=0x3):
>>> +])
>>> +
>>> +dnl OpenFlow 1.4+
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00 \
>>> +00 08 00 05 00 00 00 00 \
>>> +"], [0], [dnl
>>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>>> + event=PAUSED
>>> +])
>>>  AT_CLEANUP
>>>
>>> -AT_SETUP([NXT_FLOW_MONITOR_RESUMED])
>>> +AT_SETUP([FLOW_MONITOR_RESUMED])
>>>  AT_KEYWORDS([ofp-print])
>>> +
>>> +dnl OpenFlow 1.0-1.2
>>>  AT_CHECK([ovs-ofctl ofp-print "\
>>>  01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \
>>>  "], [0], [dnl
>>>  NXT_FLOW_MONITOR_RESUMED (xid=0x3):
>>>  ])
>>> +
>>> +dnl OpenFlow 1.3
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 50 \
>>> +"], [0], [dnl
>>> +ONFT_FLOW_MONITOR_RESUMED (OF1.3) (xid=0x3):
>>> +])
>>> +
>>> +dnl OpenFlow 1.4+
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00
>>> +00 08 00 06 00 00 00 00
>>> +"], [0], [dnl
>>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>>> + event=RESUMED
>>> +])
>>>  AT_CLEANUP
>>>
>>>  AT_SETUP([NXT_SET_FLOW_FORMAT])
>>> @@ -3629,8 +3686,10 @@ NXST_AGGREGATE reply (xid=0x4): packet_count=7
>>> byte_count=420 flow_count=7
>>>  ])
>>>  AT_CLEANUP
>>>
>>> -AT_SETUP([NXST_FLOW_MONITOR request])
>>> +AT_SETUP([FLOW_MONITOR request])
>>>  AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
>>> +
>>> +dnl OpenFlow 1.0-1.2
>>>  AT_CHECK([ovs-ofctl ofp-print "\
>>>  01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00
>>> \
>>>  00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \
>>> @@ -3640,10 +3699,37 @@ NXST_FLOW_MONITOR request (xid=0x4):
>>>   id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL
>>> table=1
>>>   id=8192 flags=delete table=2 in_port=1
>>>  ])
>>> +
>>> +dnl OpenFlow 1.3
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +04 12 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 \
>>> +4f 4e 46 00 00 00 07 4e \
>>> +00 00 10 00 00 3f 00 04 ff ff ff fe 01 00 00 00 00 01 00 04 00 00 00 00
>>> \
>>> +00 00 20 00 00 04 ff ff 00 00 00 02 01 00 00 00 00 01 00 04 00 00 00 00
>>> \
>>> +"], [0], [dnl
>>> +ONFST_FLOW_MONITOR request (OF1.3) (xid=0x6):
>>> + id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL
>>> table=1
>>> + id=8192 flags=delete out_port=2 table=1
>>> +])
>>> +
>>> +dnl OpenFlow 1.4+
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +05 12 00 58 00 00 00 06 00 10 00 00 00 00 00 00 \
>>> +00 00 10 00 ff ff ff fe ff ff ff ff 00 5f 00 00 00 01 00 04 00 00 00 00
>>> \
>>> +00 00 20 00 00 00 00 01 00 00 00 40 00 5f 01 01 00 01 00 04 00 00 00 00
>>> \
>>> +00 00 40 00 00 00 00 02 00 00 00 40 00 5f 02 02 00 01 00 04 00 00 00 00
>>> \
>>> +"], [0], [dnl
>>> +OFPST_FLOW_MONITOR request (OF1.4) (xid=0x6):
>>> + id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL
>>> table=0
>>> + id=8192 flags=initial,add,delete,modify,actions,own out_port=1
>>> out_group=64 table=1
>>> + id=16384 command=delete
>>> +])
>>>  AT_CLEANUP
>>>
>>> -AT_SETUP([NXST_FLOW_MONITOR reply])
>>> +AT_SETUP([FLOW_MONITOR reply])
>>>  AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
>>> +
>>> +dnl OpenFlow 1.0-1.2
>>>  AT_CHECK([ovs-ofctl ofp-print "\
>>>  01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00
>>> \
>>>  00 20 00 01 00 05 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0
>>> \
>>> @@ -3654,6 +3740,32 @@ NXST_FLOW_MONITOR reply (xid=0x4):
>>>   event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16
>>> cookie=0x123456789abcdef0 in_port=1
>>>   event=ABBREV xid=0x186a0
>>>  ])
>>> +
>>> +dnl OpenFlow 1.3
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +04 13 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 4f 4e 46 00 00 00 07 4e
>>> \
>>> +00 28 00 01 00 05 80 00 00 00 00 00 00 0c 00 00 \
>>> +12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
>>> +00 00 00 01 00 00 00 00 \
>>> +00 08 00 03 00 01 86 a0 \
>>> +"], [0], [dnl
>>> +ONFST_FLOW_MONITOR reply (OF1.3) (xid=0x6):
>>> + event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0
>>> in_port=1
>>> + event=ABBREV xid=0x186a0
>>> +])
>>> +
>>> +dnl OpenFlow 1.4+
>>> +AT_CHECK([ovs-ofctl ofp-print "\
>>> +05 13 00 40 00 00 00 00 00 10 00 00 00 00 00 00 \
>>> +00 28 00 02 00 05 00 00 00 00 80 00 00 00 00 00 \
>>> +12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
>>> +00 00 00 01 00 00 00 00 \
>>> +00 08 00 04 00 01 86 a0 \
>>> +"], [0], [dnl
>>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>>> + event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0
>>> in_port=1
>>> + event=ABBREV xid=0x186a0
>>> +])
>>>  AT_CLEANUP
>>>
>>>
>>> diff --git a/tests/ofproto.at b/tests/ofproto.at
>>> index 899c0be55..3502454b9 100644
>>> --- a/tests/ofproto.at
>>> +++ b/tests/ofproto.at
>>> @@ -4592,8 +4592,11 @@ ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=123,actions=output:1
>>>  ovs-ofctl -O $2 monitor br0 watch: --detach --no-chdir --pidfile
>>> >monitor.log 2>&1
>>>  AT_CAPTURE_FILE([monitor.log])
>>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>> +
>>> +# For OF(1.4+), replace INITIAL to ADDED
>>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> -  [NXST_FLOW_MONITOR reply$3:
>>> +  [$4 reply$3:
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
>>>  OFPT_BARRIER_REPLY$3:
>>>  ])
>>> @@ -4607,7 +4610,7 @@ ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7
>>>  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8
>>>  ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9
>>>  ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10
>>> -ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,actions=output:11
>>> +ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=65535,dl_vlan_pcp=3,actions=output:11
>>>  ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12
>>>  ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13
>>>  ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14
>>> @@ -4625,71 +4628,74 @@ ovs-ofctl mod-flows br0
>>> cookie=5,dl_vlan=123,actions=output:3
>>>  ovs-ofctl del-flows br0 dl_vlan=123
>>>  ovs-ofctl del-flows br0
>>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>> +
>>> +# For OF(1.4+), replace INITIAL to ADDED
>>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log |
>>> multiline_sort], [0],
>>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +[$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0
>>> actions=output:6
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1
>>> actions=output:7
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
>>> actions=output:9
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
>>> actions=output:10
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> - event=ADDED table=0 cookie=0 in_port=0,vlan_tci=0x0000
>>> actions=output:11
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>> + event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3
>>> actions=output:11
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0
>>> actions=output:12
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1
>>> actions=output:13
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
>>> actions=output:15
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
>>> actions=output:16
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0
>>> actions=output:18
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1
>>> actions=output:19
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=0 actions=output:23
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:3
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0
>>> actions=output:3
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1
>>> actions=output:3
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123
>>> actions=output:3
>>>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0
>>> actions=output:3
>>>   event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1
>>> actions=output:3
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123
>>> actions=output:3
>>>   event=DELETED reason=delete table=0 cookie=0x5
>>> in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
>>>   event=DELETED reason=delete table=0 cookie=0x5
>>> in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=0
>>> actions=output:23
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0
>>> actions=output:20
>>>   event=DELETED reason=delete table=0 cookie=0
>>> in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
>>>   event=DELETED reason=delete table=0 cookie=0
>>> in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
>>> + event=DELETED reason=delete table=0 cookie=0
>>> in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124
>>> actions=output:2
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095
>>> actions=output:14
>>>   event=DELETED reason=delete table=0 cookie=0
>>> in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
>>>   event=DELETED reason=delete table=0 cookie=0
>>> in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0
>>> actions=output:21
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1
>>> actions=output:22
>>> - event=DELETED reason=delete table=0 cookie=0 in_port=0,vlan_tci=0x0000
>>> actions=output:11
>>>  OFPT_BARRIER_REPLY$3:
>>>  ])
>>>
>>> @@ -4703,13 +4709,13 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
>>>  AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW
>>> reply:
>>>  ])
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log |
>>> multiline_sort], [0],
>>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +[$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=1 actions=output:2
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=2 actions=output:1
>>>  OFPT_BARRIER_REPLY$3:
>>>  send: OFPT_FLOW_MOD$3: DEL actions=drop
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=1 actions=output:2
>>>   event=DELETED reason=delete table=0 cookie=0 in_port=2 actions=output:1
>>>  OFPT_BARRIER_REPLY$3:
>>> @@ -4732,8 +4738,11 @@ ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=123,actions=output:1
>>>  ovs-ofctl -O $2 monitor br0 watch:\!own --detach --no-chdir --pidfile
>>> >monitor.log 2>&1
>>>  AT_CAPTURE_FILE([monitor.log])
>>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>> +
>>> +# For OF(1.4+), replace INITIAL to ADDED
>>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> -  [NXST_FLOW_MONITOR reply$3:
>>> +  [$4 reply$3:
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
>>>  OFPT_BARRIER_REPLY$3:
>>>  ])
>>> @@ -4747,14 +4756,17 @@ ovs-appctl -t ovs-ofctl ofctl/send $send_buf
>>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>>  AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW
>>> reply:
>>>  ])
>>> +
>>> +# For OF(1.4+), replace INITIAL to ADDED
>>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +[$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=1 actions=output:2
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 in_port=2 actions=output:1
>>>  OFPT_BARRIER_REPLY$3:
>>>  send: OFPT_FLOW_MOD$3: DEL actions=drop
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=ABBREV xid=0x12345678
>>>  OFPT_BARRIER_REPLY$3:
>>>  ])
>>> @@ -4775,8 +4787,11 @@ ovs-ofctl add-flow br0
>>> in_port=0,dl_vlan=123,actions=output:2
>>>  ovs-ofctl -O $2 monitor br0 watch:out_port=2 --detach --no-chdir
>>> --pidfile >monitor.log 2>&1
>>>  AT_CAPTURE_FILE([monitor.log])
>>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>> +
>>> +# For OF(1.4+), replace INITIAL to ADDED
>>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> -  [NXST_FLOW_MONITOR reply$3:
>>> +  [$4 reply$3:
>>>   event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
>>>  OFPT_BARRIER_REPLY$3:
>>>  ])
>>> @@ -4796,17 +4811,19 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
>>>  ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:2
>>>  ovs-appctl -t ovs-ofctl ofctl/barrier
>>>
>>> +# For OF(1.4+), replace INITIAL to ADDED
>>> +sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> -[NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +[$4 reply$3 (xid=0x0):
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122
>>> actions=output:1,output:2
>>>  OFPT_BARRIER_REPLY$3:
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123
>>> actions=output:1,output:2
>>>  OFPT_BARRIER_REPLY$3:
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1
>>>  OFPT_BARRIER_REPLY$3:
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3 (xid=0x0):
>>>   event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
>>>  OFPT_BARRIER_REPLY$3:
>>>  ])
>>> @@ -4869,7 +4886,7 @@ ovs-appctl -t ovs-ofctl ofctl/unblock
>>>  # A barrier doesn't work for this purpose.
>>>  #    https://www.mail-archive.com/dev@openvswitch.org/msg27013.html
>>>  #    https://www.mail-archive.com/dev@openvswitch.org/msg27675.html
>>> -OVS_WAIT_UNTIL([grep NXT_FLOW_MONITOR_RESUMED monitor.log])
>>> +OVS_WAIT_UNTIL([grep RESUMED monitor.log])
>>>
>>>  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>>>
>>> @@ -4897,24 +4914,30 @@ AT_CHECK([test $adds = $deletes])
>>>  # and MODIFIED lines lines depends on hash order, that is, it varies
>>>  # as we change the hash function or change architecture.  Therefore,
>>>  # we use a couple of tests below to accept both orders.
>>> +
>>> +# Rename all version specific strings to a generic one
>>> +sed -i'.raw' -e 's|NXT_FLOW_MONITOR|FLOW_MONITOR|' -e
>>> 's|ONFT_FLOW_MONITOR|FLOW_MONITOR|' monitor.log
>>> +sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n
>>> event=PAUSED|FLOW_MONITOR_PAUSED$3:|' monitor.log
>>> +sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n
>>> event=RESUMED|FLOW_MONITOR_RESUMED$3:|' monitor.log
>>> +
>>>  AT_CHECK([ofctl_strip < monitor.log | sed -n -e '
>>>  /reg1=0x22$/p
>>>  /cookie=0x[[23]]/p
>>> -/NXT_FLOW_MONITOR_PAUSED$3:/p
>>> -/NXT_FLOW_MONITOR_RESUMED$3:/p
>>> +/FLOW_MONITOR_PAUSED$3:/p
>>> +/FLOW_MONITOR_RESUMED$3:/p
>>>  ' > monitor.log.subset])
>>>  AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl
>>>   event=ADDED table=0 cookie=0x1 reg1=0x22
>>> -NXT_FLOW_MONITOR_PAUSED$3:
>>> +FLOW_MONITOR_PAUSED$3:
>>>   event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
>>>   event=ADDED table=0 cookie=0x3 in_port=1
>>> -NXT_FLOW_MONITOR_RESUMED$3:
>>> +FLOW_MONITOR_RESUMED$3:
>>>  ])
>>>  AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl
>>> -NXT_FLOW_MONITOR_PAUSED$3:
>>> +FLOW_MONITOR_PAUSED$3:
>>>   event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
>>>   event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
>>> -NXT_FLOW_MONITOR_RESUMED$3:
>>> +FLOW_MONITOR_RESUMED$3:
>>>  ])
>>>
>>>  OVS_VSWITCHD_STOP
>>> @@ -4931,7 +4954,7 @@ ovs-ofctl -O $2 monitor br0 watch:udp,udp_dst=8
>>> --detach --no-chdir --pidfile >m
>>>  AT_CAPTURE_FILE([monitor.log])
>>>
>>>  # Wait till reply comes backs with OF Version
>>> -OVS_WAIT_UNTIL([grep "NXST_FLOW_MONITOR reply$3" monitor.log])
>>> +OVS_WAIT_UNTIL([grep "$4 reply$3" monitor.log])
>>>  ovs-appctl -t ovs-ofctl exit
>>>
>>>  # Make sure protocol type in messages from vswitchd, matches that of
>>> requested protocol
>>> @@ -4940,17 +4963,64 @@ ovs-ofctl add-flow br0
>>> sctp,sctp_dst=9,action=normal
>>>
>>>  OVS_WAIT_UNTIL([grep "event=ADDED " monitor.log])
>>>  AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0], [dnl
>>> -NXST_FLOW_MONITOR reply$3:
>>> -NXST_FLOW_MONITOR reply$3 (xid=0x0):
>>> +$4 reply$3:
>>> +$4 reply$3 (xid=0x0):
>>>   event=ADDED table=0 cookie=0 sctp,tp_dst=9 actions=NORMAL
>>>  ])
>>>  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>>>  OVS_VSWITCHD_STOP
>>>  AT_CLEANUP
>>>  ])
>>> -CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [])
>>> -CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)])
>>> -CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)])
>>> +CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [],
>>>  NXST_FLOW_MONITOR)
>>> +CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)],
>>> NXST_FLOW_MONITOR)
>>> +CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)],
>>> NXST_FLOW_MONITOR)
>>> +CHECK_FLOW_MONITORING([1.3], [OpenFlow13], [ (OF1.3)],
>>> ONFST_FLOW_MONITOR)
>>> +CHECK_FLOW_MONITORING([1.4], [OpenFlow14], [ (OF1.4)],
>>> OFPST_FLOW_MONITOR)
>>> +CHECK_FLOW_MONITORING([1.5], [OpenFlow15], [ (OF1.5)],
>>> OFPST_FLOW_MONITOR)
>>> +
>>> +AT_SETUP([ofproto - OpenFlow14 flow monitoring with out_group])
>>> +AT_KEYWORDS([monitor])
>>> +OVS_VSWITCHD_START
>>> +
>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0
>>> group_id=1,type=all,bucket=output:1])
>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0
>>> group_id=2,type=all,bucket=output:2])
>>> +
>>> +ovs-ofctl -OOpenFlow14 add-flow br0
>>> in_port=0,dl_vlan=121,actions=output:1
>>> +ovs-ofctl -OOpenFlow14 add-flow br0
>>> in_port=0,dl_vlan=122,actions=group:1
>>> +ovs-ofctl -OOpenFlow14 add-flow br0
>>> in_port=0,dl_vlan=123,actions=group:2
>>> +
>>> +# Start a monitor watching the flow table and check the initial reply.
>>> +ovs-ofctl -OOpenFlow14 monitor br0 watch:out_group=2 --detach
>>> --no-chdir --pidfile >monitor.log 2>&1
>>> +AT_CAPTURE_FILE([monitor.log])
>>> +ovs-appctl -t ovs-ofctl ofctl/barrier
>>> +AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> +[OFPST_FLOW_MONITOR reply (OF1.4):
>>> + event=INITIAL table=0 cookie=0 in_port=0,dl_vlan=123 actions=group:2
>>> +OFPT_BARRIER_REPLY (OF1.4):
>>> +])
>>> +
>>> +ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
>>> +
>>> +# Add, modify flows and check the updates.
>>> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=121,actions=group:2
>>> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=122,actions=group:2
>>> +ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=123,actions=group:1
>>> +ovs-appctl -t ovs-ofctl ofctl/barrier
>>> +ovs-ofctl -OOpenFlow14 add-flow br0
>>> in_port=0,dl_vlan=124,actions=group:2
>>> +
>>> +AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log],
>>> [0],
>>> +[OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>>> + event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=121 actions=group:2
>>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>>> + event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=group:2
>>> +OFPT_BARRIER_REPLY (OF1.4):
>>> +OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
>>> + event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=group:2
>>> +])
>>> +
>>> +OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
>>> +OVS_VSWITCHD_STOP
>>> +AT_CLEANUP
>>>
>>>  AT_SETUP([ofproto - event filtering (OpenFlow 1.3)])
>>>  AT_KEYWORDS([monitor])
>>> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
>>> index 2017c6eba..d9336a43b 100644
>>> --- a/utilities/ovs-ofctl.8.in
>>> +++ b/utilities/ovs-ofctl.8.in
>>> @@ -626,6 +626,9 @@ monitored.
>>>  If set, only flows that output to \fIport\fR are monitored.  The
>>>  \fIport\fR may be an OpenFlow port number or keyword
>>>  (e.g. \fBLOCAL\fR).
>>> +.IP "\fBout_group=\fIgroup\fR"
>>> +If set, only flows that output to \fIgroup\fR number are monitored.
>>> +This field requires OpenFlow 1.4 (-OOpenFlow14) or later.
>>>  .IP "\fIfield\fB=\fIvalue\fR"
>>>  Monitors only flows that have \fIfield\fR specified as the given
>>>  \fIvalue\fR.  Any syntax valid for matching on \fBdump\-flows\fR may
>>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
>>> index 614b73006..5e6823240 100644
>>> --- a/utilities/ovs-ofctl.c
>>> +++ b/utilities/ovs-ofctl.c
>>> @@ -2313,6 +2313,12 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
>>>
>>>              msg = ofpbuf_new(0);
>>>              ofputil_append_flow_monitor_request(&fmr, msg, protocol);
>>> +
>>> +            if (verbosity) {
>>> +                ofpmsg_update_length(msg);
>>> +                ofp_print(stdout, msg->data, msg->size, NULL,
>>> +                          NULL, verbosity + 2);
>>> +            }
>>>              dump_transaction(vconn, msg);
>>>              fflush(stdout);
>>>          } else if (!strcmp(arg, "resume")) {
>>> --
>>> 2.29.2
>>>
>>>
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 02884b774..47ad9de2a 100644
--- a/NEWS
+++ b/NEWS
@@ -25,8 +25,10 @@  v2.16.0 - xx xxx xxxx
    - In ovs-vsctl and vtep-ctl, the "find" command now accept new
      operators {in} and {not-in}.
    - OpenFlow:
-     * Extend Flow Monitoring support for OpenFlow 1.0-1.2 with Nicira
-       Extensions
+     * Extended Flow Monitoring support for all supported OpenFlow versions
+         OpenFlow versions 1.0-1.2 with Nicira Extensions
+         OpenFlow versions 1.3 with Open Network Foundation extension
+         OpenFlow versions 1.4+, as defined in the OpenFlow specification
    - Userspace datapath:
      * Auto load balancing of PMDs now partially supports cross-NUMA polling
        cases, e.g if all PMD threads are running on the same NUMA node.
diff --git a/include/openflow/openflow-1.3.h b/include/openflow/openflow-1.3.h
index c48a8ea7f..1a818dbb4 100644
--- a/include/openflow/openflow-1.3.h
+++ b/include/openflow/openflow-1.3.h
@@ -374,4 +374,93 @@  struct ofp13_async_config {
 };
 OFP_ASSERT(sizeof(struct ofp13_async_config) == 24);
 
+struct onf_flow_monitor_request {
+    ovs_be32   id;            /* Controller-assigned ID for this monitor. */
+    ovs_be16   flags;         /* ONFFMF_*. */
+    ovs_be16   match_len;     /* Length of oxm_fields. */
+    ovs_be32   out_port;      /* Required output port, if not OFPP_NONE. */
+    uint8_t    table_id;      /* One table’s ID or 0xff for all tables. */
+    uint8_t    zeros[3];      /* Align to 64 bits (must be zero). */
+    /* Followed by an ofp11_match structure. */
+};
+OFP_ASSERT(sizeof(struct onf_flow_monitor_request) == 16);
+
+/* Header for experimenter requests and replies. */
+struct onf_experimenter_header {
+    struct ofp_header header;
+    ovs_be32   vendor;        /* ONF_EXPERIMENTER_ID. */
+    ovs_be32   subtype;       /* One of ONFT_*. */
+};
+OFP_ASSERT(sizeof(struct onf_experimenter_header) == 16);
+
+enum onf_flow_monitor_msg_type {
+    ONFT_FLOW_MONITOR_CANCEL = 1870,
+    ONFT_FLOW_MONITOR_PAUSED = 1871,
+    ONFT_FLOW_MONITOR_RESUMED = 1872
+};
+
+/* ’flags’ bits in struct onf_flow_monitor_request. */
+enum onf_flow_monitor_flags {
+    /* When to send updates. */
+    ONFFMF_INITIAL = 1 << 0,  /* Initially matching flows. */
+    ONFFMF_ADD = 1 << 1,      /* New matching flows as they are added. */
+    ONFFMF_DELETE = 1 << 2,   /* Old matching flows as they are removed. */
+    ONFFMF_MODIFY = 1 << 3,   /* Matching flows as they are changed. */
+
+    /* What to include in updates. */
+    ONFFMF_ACTIONS = 1 << 4,  /* If set, actions are included. */
+    ONFFMF_OWN = 1 << 5,      /* If set, include own changes in full. */
+};
+
+/* ONFST_FLOW_MONITOR reply header. */
+struct onf_flow_update_header {
+    ovs_be16   length;        /* Length of this entry. */
+    ovs_be16   event;         /* One of ONFFME_*. */
+    /* ...other data depending on ’event’... */
+};
+OFP_ASSERT(sizeof(struct onf_flow_update_header) == 4);
+
+/* ’event’ values in struct onf_flow_update_header. */
+enum onf_flow_update_event {
+    /* struct onf_flow_update_full. */
+    ONFFME_ADDED = 0,         /* Flow was added. */
+    ONFFME_DELETED = 1,       /* Flow was deleted. */
+    ONFFME_MODIFIED = 2,      /* Flow (generally its actions) was changed. */
+
+    /* struct onf_flow_update_abbrev. */
+    ONFFME_ABBREV = 3,        /* Abbreviated reply. */
+};
+
+/* ONFST_FLOW_MONITOR reply for ONFFME_ADDED, ONFFME_DELETED, and
+* ONFFME_MODIFIED. */
+struct onf_flow_update_full {
+    ovs_be16   length;        /* Length is 24. */
+    ovs_be16   event;         /* One of ONFFME_*. */
+    ovs_be16   reason;        /* OFPRR_* for ONFFME_DELETED, else zero. */
+    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   match_len;     /* Length of oxm_fields. */
+    uint8_t    table_id;      /* ID of flow’s table. */
+    uint8_t    pad;           /* Reserved, currently zeroed. */
+    ovs_be64   cookie;        /* Opaque controller-issued identifier. */
+    /* Followed by:
+     *   - Exactly match_len (possibly 0) bytes containing the oxm_fields, then
+     *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
+     *     all-zero bytes, then
+     *   - Instructions to fill out the remainder ’length’ bytes (always a
+     *     multiple of 8). If ONFFMF_ACTIONS was not specified, or ’event’ is
+     *     ONFFME_DELETED, no actions are included.
+     */
+};
+OFP_ASSERT(sizeof(struct onf_flow_update_full) == 24);
+
+/* ONFST_FLOW_MONITOR reply for ONFFME_ABBREV. */
+struct onf_flow_update_abbrev {
+    ovs_be16   length;     /* Length is 8. */
+    ovs_be16   event;      /* ONFFME_ABBREV. */
+    ovs_be32   xid;        /* Controller-specified xid from flow_mod. */
+};
+OFP_ASSERT(sizeof(struct onf_flow_update_abbrev) == 8);
+
 #endif /* openflow/openflow-1.3.h */
diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h
index be191180b..8e6a163fd 100644
--- a/include/openflow/openflow-1.4.h
+++ b/include/openflow/openflow-1.4.h
@@ -358,27 +358,100 @@  OFP_ASSERT(sizeof(struct ofp14_flow_monitor_request) == 16);
 
 /* Flow monitor commands */
 enum ofp14_flow_monitor_command {
-    OFPFMC14_ADD = 0, /* New flow monitor. */
-    OFPFMC14_MODIFY = 1, /* Modify existing flow monitor. */
-    OFPFMC14_DELETE = 2, /* Delete/cancel existing flow monitor. */
+    OFPFMC_ADD = 0, /* New flow monitor. */
+    OFPFMC_MODIFY = 1, /* Modify existing flow monitor. */
+    OFPFMC_DELETE = 2, /* Delete/cancel existing flow monitor. */
 };
 
 /* 'flags' bits in struct of_flow_monitor_request. */
 enum ofp14_flow_monitor_flags {
     /* When to send updates. */
     /* Common to NX and OpenFlow 1.4 */
-    OFPFMF14_INITIAL = 1 << 0,     /* Initially matching flows. */
-    OFPFMF14_ADD = 1 << 1,         /* New matching flows as they are added. */
-    OFPFMF14_REMOVED = 1 << 2,     /* Old matching flows as they are removed. */
-    OFPFMF14_MODIFY = 1 << 3,      /* Matching flows as they are changed. */
+    OFPFMF_INITIAL = 1 << 0,     /* Initially matching flows. */
+    OFPFMF_ADD = 1 << 1,         /* New matching flows as they are added. */
+    OFPFMF_REMOVED = 1 << 2,     /* Old matching flows as they are removed. */
+    OFPFMF_MODIFY = 1 << 3,      /* Matching flows as they are changed. */
 
     /* What to include in updates. */
     /* Common to NX and OpenFlow 1.4 */
-    OFPFMF14_INSTRUCTIONS = 1 << 4, /* If set, instructions are included. */
-    OFPFMF14_NO_ABBREV = 1 << 5,    /* If set, include own changes in full. */
+    OFPFMF_INSTRUCTIONS = 1 << 4, /* If set, instructions are included. */
+    OFPFMF_NO_ABBREV = 1 << 5,    /* If set, include own changes in full. */
     /* OpenFlow 1.4 */
-    OFPFMF14_ONLY_OWN = 1 << 6,     /* If set, don't include other controllers.
+    OFPFMF_ONLY_OWN = 1 << 6,     /* If set, don't include other controllers.
                                      */
 };
 
+/* OFPMP_FLOW_MONITOR reply header.
+ *
+ * The body of an OFPMP_FLOW_MONITOR reply is an array of variable-length
+ * structures, each of which begins with this header.  The ’length’ member may
+ * be used to traverse the array, and the ’event’ member may be used to
+ * determine the particular structure.
+ * Every instance is a multiple of 8 bytes long. */
+struct ofp_flow_update_header {
+    ovs_be16 length;                /* Length of this entry. */
+    ovs_be16 event;                 /* One of OFPFME_*. */
+    /* ...other data depending on ’event’... */
+};
+OFP_ASSERT(sizeof(struct ofp_flow_update_header) == 4);
+
+/* ’event’ values in struct ofp_flow_update_header. */
+enum ofp_flow_update_event {
+    /* struct ofp_flow_update_full. */
+    OFPFME_INITIAL = 0,          /* Flow present when flow monitor created. */
+    OFPFME_ADDED = 1,            /* Flow was added. */
+    OFPFME_REMOVED = 2,          /* Flow was removed. */
+    OFPFME_MODIFIED = 3,         /* Flow instructions were changed. */
+
+    /* struct ofp_flow_update_abbrev. */
+    OFPFME_ABBREV = 4,           /* Abbreviated reply. */
+
+    /* struct ofp_flow_update_header. */
+    OFPFME_PAUSED = 5,           /* Monitoring paused (out of buffer space). */
+    OFPFME_RESUMED = 6,          /* Monitoring resumed. */
+};
+
+/* OFPMP_FLOW_MONITOR reply for OFPFME_INITIAL, OFPFME_ADDED, OFPFME_REMOVED,
+ * and OFPFME_MODIFIED. */
+struct ofp_flow_update_full {
+    ovs_be16 length;           /* Length is 32 + match + instructions. */
+    ovs_be16 event;            /* One of OFPFME_*. */
+    uint8_t table_id;          /* ID of flow’s table. */
+    uint8_t reason;            /* OFPRR_* for OFPFME_REMOVED, else zero. */
+    ovs_be16 idle_timeout;     /* Number of seconds idle before expiration. */
+    ovs_be16 hard_timeout;     /* Number of seconds before expiration. */
+    ovs_be16 priority;         /* Priority of the entry. */
+    uint8_t zeros[4];          /* Reserved, currently zeroed. */
+    ovs_be64 cookie;           /* Opaque controller-issued identifier. */
+    /* Instruction set.
+     *      If OFPFMF_INSTRUCTIONS was not specified, or ’event’ is
+     *     OFPFME_REMOVED, no instructions are included.
+     */
+};
+OFP_ASSERT(sizeof(struct ofp_flow_update_full) == 24);
+
+/* OFPMP_FLOW_MONITOR reply for OFPFME_ABBREV.
+ *
+ * When the controller does not specify OFPFMF_NO_ABBREV in a monitor request,
+ * any flow tables changes due to the controller’s own requests (on the same
+ * OpenFlow channel) will be abbreviated, when possible, to this form, which
+ * simply specifies the ’xid’ of the OpenFlow request (e.g. an OFPT_FLOW_MOD)
+ * that caused the change.
+ * Some changes cannot be abbreviated and will be sent in full.
+ */
+struct ofp_flow_update_abbrev {
+    ovs_be16 length;     /* Length is 8. */
+    ovs_be16 event;      /* OFPFME_ABBREV. */
+    ovs_be32 xid;        /* Controller-specified xid from flow_mod. */
+};
+OFP_ASSERT(sizeof(struct ofp_flow_update_abbrev) == 8);
+
+/* OFPMP_FLOW_MONITOR reply for OFPFME_PAUSED and OFPFME_RESUMED.*/
+struct ofp_flow_update_paused {
+    ovs_be16 length;      /* Length is 8. */
+    ovs_be16 event;       /* One of OFPFME_*. */
+    uint8_t zeros[4];     /* Reserved, currently zeroed. */
+};
+OFP_ASSERT(sizeof(struct ofp_flow_update_paused) == 8);
+
 #endif /* openflow/openflow-1.4.h */
diff --git a/include/openvswitch/ofp-monitor.h b/include/openvswitch/ofp-monitor.h
index 835efd0f3..7c7cfcff4 100644
--- a/include/openvswitch/ofp-monitor.h
+++ b/include/openvswitch/ofp-monitor.h
@@ -61,8 +61,10 @@  void ofputil_flow_removed_format(struct ds *,
 /* Abstract nx_flow_monitor_request. */
 struct ofputil_flow_monitor_request {
     uint32_t id;
-    enum nx_flow_monitor_flags flags;
+    enum ofp14_flow_monitor_command command;
+    enum ofp14_flow_monitor_flags flags;
     ofp_port_t out_port;
+    uint32_t out_group;
     uint8_t table_id;
     struct match match;
 };
@@ -85,7 +87,7 @@  char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
 
 /* Abstract nx_flow_update. */
 struct ofputil_flow_update {
-    enum nx_flow_update_event event;
+    enum ofp_flow_update_event event;
 
     /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */
     enum ofp_flow_removed_reason reason;
@@ -119,6 +121,9 @@  uint32_t ofputil_decode_flow_monitor_cancel(const struct ofp_header *);
 struct ofpbuf *ofputil_encode_flow_monitor_cancel(
     uint32_t id, enum ofputil_protocol protocol);
 
+struct ofpbuf * ofputil_encode_flow_monitor_pause(
+    enum ofp_flow_update_event command, enum ofputil_protocol protocol);
+
 struct ofputil_requestforward {
     ovs_be32 xid;
     /* Also used for OF 1.0-1.3 when using Nicira Extension: */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index c5fde0270..921a937e5 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -453,14 +453,33 @@  enum ofpraw {
 
     /* OFPST 1.4+ (16): uint8_t[8][]. */
     OFPRAW_OFPST14_FLOW_MONITOR_REQUEST,
+    /* ONFST 1.3 (1870): uint8_t[8][]. */
+    OFPRAW_ONFST13_FLOW_MONITOR_REQUEST,
     /* NXST 1.0-1.2 (2): uint8_t[8][]. */
     OFPRAW_NXST_FLOW_MONITOR_REQUEST,
 
     /* OFPST 1.4+ (16): uint8_t[8][]. */
     OFPRAW_OFPST14_FLOW_MONITOR_REPLY,
+    /* ONFST 1.3 (1870): uint8_t[8][]. */
+    OFPRAW_ONFST13_FLOW_MONITOR_REPLY,
     /* NXST 1.0-1.2 (2): uint8_t[8][]. */
     OFPRAW_NXST_FLOW_MONITOR_REPLY,
 
+    /* ONFT 1.3 (1870): struct nx_flow_monitor_cancel. */
+    OFPRAW_ONFT13_FLOW_MONITOR_CANCEL,
+    /* NXT 1.0-1.2 (21): struct nx_flow_monitor_cancel. */
+    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
+
+    /* ONFT 1.3 (1871): void. */
+    OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
+    /* NXT 1.0-1.2 (22): void. */
+    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+
+    /* ONFT 1.3 (1872): void. */
+    OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
+    /* NXT 1.0-1.2 (23): void. */
+    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
+
 /* Nicira extension messages.
  *
  * Nicira extensions that correspond to standard OpenFlow messages are listed
@@ -481,15 +500,6 @@  enum ofpraw {
     /* NXT 1.0+ (20): struct nx_controller_id. */
     OFPRAW_NXT_SET_CONTROLLER_ID,
 
-    /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */
-    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
-
-    /* NXT 1.0+ (22): void. */
-    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
-
-    /* NXT 1.0+ (23): void. */
-    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
-
     /* NXT 1.0+ (24): struct nx_tlv_table_mod, struct nx_tlv_map[]. */
     OFPRAW_NXT_TLV_TABLE_MOD,
 
@@ -741,8 +751,10 @@  enum ofptype {
                                       * OFPRAW_OFPST14_PORT_DESC_REPLY. */
 
     OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /* OFPRAW_OFPST14_FLOW_MONITOR_REQUEST.
+                                         * OFPRAW_ONFST13_FLOW_MONITOR_REQUEST.
                                          * OFPRAW_NXST_FLOW_MONITOR_REQUEST. */
     OFPTYPE_FLOW_MONITOR_STATS_REPLY,   /* OFPRAW_OFPST14_FLOW_MONITOR_REPLY.
+                                         * OFPRAW_ONFST13_FLOW_MONITOR_REPLY.
                                          * OFPRAW_NXST_FLOW_MONITOR_REPLY. */
 
     /* Nicira extensions. */
@@ -762,9 +774,12 @@  enum ofptype {
     OFPTYPE_CT_FLUSH_ZONE,            /* OFPRAW_NXT_CT_FLUSH_ZONE. */
 
     /* Flow monitor extension. */
-    OFPTYPE_FLOW_MONITOR_CANCEL,        /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
-    OFPTYPE_FLOW_MONITOR_PAUSED,        /* OFPRAW_NXT_FLOW_MONITOR_PAUSED. */
-    OFPTYPE_FLOW_MONITOR_RESUMED,       /* OFPRAW_NXT_FLOW_MONITOR_RESUMED. */
+    OFPTYPE_FLOW_MONITOR_CANCEL,  /* OFPRAW_NXT_FLOW_MONITOR_CANCEL.
+                                   * OFPRAW_ONFT13_FLOW_MONITOR_CANCEL. */
+    OFPTYPE_FLOW_MONITOR_PAUSED,  /* OFPRAW_NXT_FLOW_MONITOR_PAUSED.
+                                   * OFPRAW_ONFT13_FLOW_MONITOR_PAUSED. */
+    OFPTYPE_FLOW_MONITOR_RESUMED, /* OFPRAW_NXT_FLOW_MONITOR_RESUMED.
+                                   * OFPRAW_ONFT13_FLOW_MONITOR_RESUMED */
 };
 
 /* Decoding messages into OFPTYPE_* values. */
diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
index 51f01b100..1756c9e8e 100644
--- a/lib/ofp-monitor.c
+++ b/lib/ofp-monitor.c
@@ -328,6 +328,98 @@  ofputil_flow_removed_format(struct ds *s,
     ds_put_format(s, " pkts%"PRIu64" bytes%"PRIu64"\n",
                   fr->packet_count, fr->byte_count);
 }
+
+static uint16_t
+nx_to_ofp_flow_monitor_flags(uint16_t flags)
+{
+    uint16_t oxm_flags = 0;
+
+    if (flags & NXFMF_INITIAL) {
+        oxm_flags |= OFPFMF_INITIAL;
+    }
+    if (flags & NXFMF_ADD) {
+        oxm_flags |= OFPFMF_ADD;
+    }
+    if (flags & NXFMF_DELETE) {
+        oxm_flags |= OFPFMF_REMOVED;
+    }
+    if (flags & NXFMF_MODIFY) {
+        oxm_flags |= OFPFMF_MODIFY;
+    }
+    if (flags & NXFMF_ACTIONS) {
+        oxm_flags |= OFPFMF_INSTRUCTIONS;
+    }
+    if (flags & NXFMF_OWN) {
+        oxm_flags |= OFPFMF_ONLY_OWN;
+    }
+
+    return oxm_flags;
+}
+
+static uint16_t
+ofp_to_nx_flow_monitor_flags(uint16_t flags)
+{
+    uint16_t nx_flags = 0;
+
+    if (flags & OFPFMF_INITIAL) {
+        nx_flags |= NXFMF_INITIAL;
+    }
+    if (flags & OFPFMF_ADD) {
+        nx_flags |= NXFMF_ADD;
+    }
+    if (flags & OFPFMF_REMOVED) {
+        nx_flags |= NXFMF_DELETE;
+    }
+    if (flags & OFPFMF_MODIFY) {
+        nx_flags |= NXFMF_MODIFY;
+    }
+    if (flags & OFPFMF_INSTRUCTIONS) {
+        nx_flags |= NXFMF_ACTIONS;
+    }
+    if (flags & OFPFMF_ONLY_OWN) {
+        nx_flags |= NXFMF_OWN;
+    }
+
+    return nx_flags;
+}
+
+static enum ofp_flow_update_event
+nx_to_ofp_flow_update_event(enum nx_flow_update_event event)
+{
+    switch (event) {
+    case NXFME_ADDED:
+        return OFPFME_ADDED;
+    case NXFME_DELETED:
+        return OFPFME_REMOVED;
+    case NXFME_MODIFIED:
+        return OFPFME_MODIFIED;
+    case NXFME_ABBREV:
+        return OFPFME_ABBREV;
+     default:
+        OVS_NOT_REACHED();
+    }
+}
+
+static enum nx_flow_update_event
+ofp_to_nx_flow_update_event(enum ofp_flow_update_event event)
+{
+    switch (event) {
+    case OFPFME_INITIAL:
+    case OFPFME_ADDED:
+        return NXFME_ADDED;
+    case OFPFME_REMOVED:
+        return NXFME_DELETED;
+    case OFPFME_MODIFIED:
+        return NXFME_MODIFIED;
+    case OFPFME_ABBREV:
+        return NXFME_ABBREV;
+    default:
+    case OFPFME_PAUSED:
+    case OFPFME_RESUMED:
+        OVS_NOT_REACHED();
+    }
+}
+
 
 /* ofputil_flow_monitor_request */
 
@@ -345,43 +437,129 @@  int
 ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
                                     struct ofpbuf *msg)
 {
-    struct nx_flow_monitor_request *nfmr;
     uint16_t flags;
+    enum ofperr error;
+    enum ofpraw raw;
 
-    if (!msg->header) {
-        ofpraw_pull_assert(msg);
+    error = (msg->header ? ofpraw_decode(&raw, msg->header)
+             : ofpraw_pull(&raw, msg));
+    if (error) {
+        return error;
     }
 
     if (!msg->size) {
         return EOF;
     }
 
-    nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
-    if (!nfmr) {
-        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
-                     "leftover bytes at end", msg->size);
-        return OFPERR_OFPBRC_BAD_LEN;
-    }
+    switch ((int) raw) {
+    case OFPRAW_NXST_FLOW_MONITOR_REQUEST: {
+        struct nx_flow_monitor_request *nfmr;
 
-    flags = ntohs(nfmr->flags);
-    if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
-        || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
-                     | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
-        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16, flags);
-        return OFPERR_OFPMOFC_BAD_FLAGS;
+        nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
+        if (!nfmr) {
+            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" "
+                    "leftover bytes at end", msg->size);
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+
+        flags = ntohs(nfmr->flags);
+        if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
+            || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
+                         | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
+            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
+                         flags);
+            return OFPERR_OFPMOFC_BAD_FLAGS;
+        }
+
+        if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
+            return OFPERR_NXBRC_MUST_BE_ZERO;
+        }
+
+        rq->id = ntohl(nfmr->id);
+        rq->command = OFPFMC_ADD;
+        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
+        rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
+        rq->table_id = nfmr->table_id;
+        rq->out_group = OFPG_ANY;
+
+        return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
+                NULL, false, NULL, NULL);
     }
+    case OFPRAW_ONFST13_FLOW_MONITOR_REQUEST: {
+        struct onf_flow_monitor_request *ofmr;
+
+        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
+        if (!ofmr) {
+            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR request has %"PRIu32" "
+                         "leftover bytes at end", msg->size);
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+
+        flags = ntohs(ofmr->flags);
+        if (!(flags & (ONFFMF_ADD | ONFFMF_DELETE | ONFFMF_MODIFY))
+            || flags & ~(ONFFMF_INITIAL | ONFFMF_ADD | ONFFMF_DELETE
+                         | ONFFMF_MODIFY | ONFFMF_ACTIONS | ONFFMF_OWN)) {
+            VLOG_WARN_RL(&rl, "ONFST_FLOW_MONITOR has bad flags %#"PRIx16,
+                         flags);
+            return OFPERR_OFPMOFC_BAD_FLAGS;
+        }
+
+        if (!is_all_zeros(ofmr->zeros, sizeof ofmr->zeros)) {
+            return OFPERR_NXBRC_MUST_BE_ZERO;
+        }
+
+        rq->id = ntohl(ofmr->id);
+        rq->command = OFPFMC_ADD;
+        rq->flags = nx_to_ofp_flow_monitor_flags(flags);
+        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
+        if (error) {
+            return error;
+        }
+        rq->table_id = ofmr->table_id;
+        rq->out_group = OFPG_ANY;
 
-    if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
-        return OFPERR_NXBRC_MUST_BE_ZERO;
+        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match, NULL);
     }
+    case OFPRAW_OFPST14_FLOW_MONITOR_REQUEST: {
+        struct ofp14_flow_monitor_request *ofmr;
+
+        ofmr = ofpbuf_try_pull(msg, sizeof *ofmr);
+        if (!ofmr) {
+            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR request has %"PRIu32" "
+                    "leftover bytes at end", msg->size);
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
 
-    rq->id = ntohl(nfmr->id);
-    rq->flags = flags;
-    rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
-    rq->table_id = nfmr->table_id;
+        flags = ntohs(ofmr->flags);
+        rq->id = ntohl(ofmr->monitor_id);
+        rq->command = ofmr->command;
+
+        if (ofmr->command == OFPFMC_DELETE) {
+            return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match, NULL);
+        }
 
-    return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
-                         NULL, false, NULL, NULL);
+        if (!(flags & (OFPFMF_ADD | OFPFMF_REMOVED | OFPFMF_MODIFY))
+                || flags & ~(OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED
+                    | OFPFMF_MODIFY | OFPFMF_INSTRUCTIONS | OFPFMF_ONLY_OWN)) {
+            VLOG_WARN_RL(&rl, "OFPST_FLOW_MONITOR has bad flags %#"PRIx16,
+                         flags);
+            return OFPERR_OFPMOFC_BAD_FLAGS;
+        }
+
+        rq->command = ofmr->command;
+        rq->flags = flags;
+        error = ofputil_port_from_ofp11(ofmr->out_port, &rq->out_port);
+        if (error) {
+            return error;
+        }
+        rq->out_group = ntohl(ofmr->out_group);
+        rq->table_id = ofmr->table_id;
+
+        return ofputil_pull_ofp11_match(msg, NULL, NULL, &rq->match, NULL);
+    }
+    default:
+        OVS_NOT_REACHED();
+    }
 }
 
 void
@@ -389,66 +567,143 @@  ofputil_append_flow_monitor_request(
     const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg,
     enum ofputil_protocol protocol)
 {
-    struct nx_flow_monitor_request *nfmr;
     size_t start_ofs;
     int match_len;
     enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
 
     if (!msg->size) {
-        ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version, msg);
-    }
+        switch (version) {
+        case OFP10_VERSION:
+        case OFP11_VERSION:
+        case OFP12_VERSION: {
+            struct nx_flow_monitor_request *nfmr;
+
+            if (!msg->size) {
+                ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, version, msg);
+            }
 
-    start_ofs = msg->size;
-    ofpbuf_put_zeros(msg, sizeof *nfmr);
-    match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
-
-    nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
-    nfmr->id = htonl(rq->id);
-    nfmr->flags = htons(rq->flags);
-    nfmr->out_port = htons(ofp_to_u16(rq->out_port));
-    nfmr->match_len = htons(match_len);
-    nfmr->table_id = rq->table_id;
+            start_ofs = msg->size;
+            ofpbuf_put_zeros(msg, sizeof *nfmr);
+            match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
+
+            nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
+            nfmr->id = htonl(rq->id);
+            nfmr->flags = htons(ofp_to_nx_flow_monitor_flags(rq->flags));
+            nfmr->out_port = htons(ofp_to_u16(rq->out_port));
+            nfmr->match_len = htons(match_len);
+            nfmr->table_id = rq->table_id;
+            break;
+        }
+        case OFP13_VERSION: {
+            struct onf_flow_monitor_request *ofmr;
+
+            if (!msg->size) {
+                ofpraw_put(OFPRAW_ONFST13_FLOW_MONITOR_REQUEST, version, msg);
+            }
+
+            start_ofs = msg->size;
+            ofpbuf_put_zeros(msg, sizeof *ofmr);
+            match_len = oxm_put_match(msg, &rq->match, version);
+
+            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
+            ofmr->id = htonl(rq->id);
+            ofmr->flags = htons(ofp_to_nx_flow_monitor_flags(rq->flags));
+            ofmr->match_len = htons(match_len);
+            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
+            ofmr->table_id = rq->table_id;
+            break;
+        }
+        case OFP14_VERSION:
+        case OFP15_VERSION: {
+            struct ofp14_flow_monitor_request *ofmr;
+
+            if (!msg->size) {
+                ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version, msg);
+            }
+
+            start_ofs = msg->size;
+            ofpbuf_put_zeros(msg, sizeof *ofmr);
+            oxm_put_match(msg, &rq->match, version);
+
+            ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
+            ofmr->monitor_id = htonl(rq->id);
+            ofmr->command = OFPFMC_ADD;
+            ofmr->out_port = ofputil_port_to_ofp11(rq->out_port);
+            ofmr->out_group = htonl(rq->out_group);
+            ofmr->flags = htons(rq->flags);
+            ofmr->table_id = rq->table_id;
+            break;
+        }
+        default:
+            OVS_NOT_REACHED();
+        }
+    }
 }
 
 static const char *
-nx_flow_monitor_flags_to_name(uint32_t bit)
+ofp_flow_monitor_flags_to_name(uint32_t bit)
 {
-    enum nx_flow_monitor_flags fmf = bit;
+    enum ofp14_flow_monitor_flags fmf = bit;
 
     switch (fmf) {
-    case NXFMF_INITIAL: return "initial";
-    case NXFMF_ADD: return "add";
-    case NXFMF_DELETE: return "delete";
-    case NXFMF_MODIFY: return "modify";
-    case NXFMF_ACTIONS: return "actions";
-    case NXFMF_OWN: return "own";
+    case OFPFMF_INITIAL: return "initial";
+    case OFPFMF_ADD: return "add";
+    case OFPFMF_REMOVED: return "delete";
+    case OFPFMF_MODIFY: return "modify";
+    case OFPFMF_INSTRUCTIONS: return "actions";
+    case OFPFMF_NO_ABBREV: return "no-abbrev";
+    case OFPFMF_ONLY_OWN: return "own";
     }
 
     return NULL;
 }
 
+static const char *
+ofp_flow_monitor_command_to_string(enum ofp14_flow_monitor_command command)
+{
+    switch (command) {
+    case OFPFMC_ADD: return "add";
+    case OFPFMC_MODIFY: return "modify";
+    case OFPFMC_DELETE: return "delete";
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
 void
 ofputil_flow_monitor_request_format(
     struct ds *s, const struct ofputil_flow_monitor_request *request,
     const struct ofputil_port_map *port_map,
     const struct ofputil_table_map *table_map)
 {
+    if (request->command == OFPFMC_DELETE) {
+        ds_put_format(s, "\n id=%"PRIu32" command=%s", request->id,
+                      ofp_flow_monitor_command_to_string(request->command));
+        return;
+    }
     ds_put_format(s, "\n id=%"PRIu32" flags=", request->id);
-    ofp_print_bit_names(s, request->flags, nx_flow_monitor_flags_to_name, ',');
+    ofp_print_bit_names(s, request->flags,
+                        ofp_flow_monitor_flags_to_name, ',');
 
     if (request->out_port != OFPP_NONE) {
         ds_put_cstr(s, " out_port=");
         ofputil_format_port(request->out_port, port_map, s);
     }
 
+    if (request->out_group && (request->out_group != OFPG_ANY)) {
+        ds_put_format(s, " out_group=%d", request->out_group);
+    }
+
     if (request->table_id != 0xff) {
         ds_put_format(s, " table=");
         ofputil_format_table(request->table_id, table_map, s);
     }
 
-    ds_put_char(s, ' ');
-    match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
-    ds_chomp(s, ' ');
+    if (request->command != OFPFMC_DELETE) {
+        ds_put_char(s, ' ');
+        match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
+        ds_chomp(s, ' ');
+    }
 }
 
 static char * OVS_WARN_UNUSED_RESULT
@@ -464,9 +719,10 @@  parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
 
     fmr->id = atomic_count_inc(&id);
 
-    fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
-                  | NXFMF_OWN | NXFMF_ACTIONS);
+    fmr->flags = (OFPFMF_INITIAL | OFPFMF_ADD | OFPFMF_REMOVED | OFPFMF_MODIFY
+                  | OFPFMF_ONLY_OWN | OFPFMF_INSTRUCTIONS);
     fmr->out_port = OFPP_NONE;
+    fmr->out_group = OFPG_ANY;
     fmr->table_id = 0xff;
     match_init_catchall(&fmr->match);
 
@@ -476,17 +732,19 @@  parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
         char *error = NULL;
 
         if (!strcmp(name, "!initial")) {
-            fmr->flags &= ~NXFMF_INITIAL;
+            fmr->flags &= ~OFPFMF_INITIAL;
         } else if (!strcmp(name, "!add")) {
-            fmr->flags &= ~NXFMF_ADD;
+            fmr->flags &= ~OFPFMF_ADD;
         } else if (!strcmp(name, "!delete")) {
-            fmr->flags &= ~NXFMF_DELETE;
+            fmr->flags &= ~OFPFMF_REMOVED;
         } else if (!strcmp(name, "!modify")) {
-            fmr->flags &= ~NXFMF_MODIFY;
+            fmr->flags &= ~OFPFMF_MODIFY;
         } else if (!strcmp(name, "!actions")) {
-            fmr->flags &= ~NXFMF_ACTIONS;
+            fmr->flags &= ~OFPFMF_INSTRUCTIONS;
+        } else if (!strcmp(name, "!abbrev")) {
+            fmr->flags &= ~OFPFMF_NO_ABBREV;
         } else if (!strcmp(name, "!own")) {
-            fmr->flags &= ~NXFMF_OWN;
+            fmr->flags &= ~OFPFMF_ONLY_OWN;
         } else if (ofp_parse_protocol(name, &p)) {
             match_set_dl_type(&fmr->match, htons(p->dl_type));
             if (p->nw_proto) {
@@ -511,6 +769,8 @@  parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
                 }
             } else if (!strcmp(name, "out_port")) {
                 fmr->out_port = u16_to_ofp(atoi(value));
+            } else if (!strcmp(name, "out_group")) {
+                fmr->out_group = atoi(value);
             } else {
                 return xasprintf("%s: unknown keyword %s", str_, name);
             }
@@ -562,101 +822,226 @@  int
 ofputil_decode_flow_update(struct ofputil_flow_update *update,
                            struct ofpbuf *msg, struct ofpbuf *ofpacts)
 {
-    struct nx_flow_update_header *nfuh;
     unsigned int length;
     struct ofp_header *oh;
+    enum ofperr error;
+    enum ofpraw raw;
 
     if (!msg->header) {
         ofpraw_pull_assert(msg);
     }
 
+    error = ofpraw_decode(&raw, msg->header);
+    if (error) {
+        return error;
+    }
+
     ofpbuf_clear(ofpacts);
     if (!msg->size) {
         return EOF;
     }
 
-    if (msg->size < sizeof(struct nx_flow_update_header)) {
-        goto bad_len;
-    }
-
     oh = msg->header;
 
-    nfuh = msg->data;
-    update->event = ntohs(nfuh->event);
-    length = ntohs(nfuh->length);
-    if (length > msg->size || length % 8) {
-        goto bad_len;
-    }
+    switch ((int) raw) {
+    case OFPRAW_ONFST13_FLOW_MONITOR_REPLY:
+    case OFPRAW_NXST_FLOW_MONITOR_REPLY: {
+        struct nx_flow_update_header *nfuh;
 
-    if (update->event == NXFME_ABBREV) {
-        struct nx_flow_update_abbrev *nfua;
+        if (msg->size < sizeof(struct nx_flow_update_header)) {
+            goto bad_len;
+        }
 
-        if (length != sizeof *nfua) {
+        nfuh = msg->data;
+        update->event = nx_to_ofp_flow_update_event(ntohs(nfuh->event));
+        length = ntohs(nfuh->length);
+        if (length > msg->size || length % 8) {
             goto bad_len;
         }
 
-        nfua = ofpbuf_pull(msg, sizeof *nfua);
-        update->xid = nfua->xid;
-        return 0;
-    } else if (update->event == NXFME_ADDED
-               || update->event == NXFME_DELETED
-               || update->event == NXFME_MODIFIED) {
-        struct nx_flow_update_full *nfuf;
-        unsigned int actions_len;
-        unsigned int match_len;
-        enum ofperr error;
+        if (update->event == OFPFME_ABBREV) {
+            struct nx_flow_update_abbrev *nfua;
+
+            if (length != sizeof *nfua) {
+                goto bad_len;
+            }
+
+            nfua = ofpbuf_pull(msg, sizeof *nfua);
+            update->xid = nfua->xid;
+            return 0;
+        } else if (update->event == OFPFME_ADDED
+                   || update->event == OFPFME_REMOVED
+                   || update->event == OFPFME_MODIFIED) {
+            struct nx_flow_update_full *nfuf;
+            unsigned int actions_len;
+            unsigned int match_len;
+
+            if (length < sizeof *nfuf) {
+                goto bad_len;
+            }
 
-        if (length < sizeof *nfuf) {
+            nfuf = ofpbuf_pull(msg, sizeof *nfuf);
+            match_len = ntohs(nfuf->match_len);
+            if (sizeof *nfuf + match_len > length) {
+                goto bad_len;
+            }
+
+            update->reason = ntohs(nfuf->reason);
+            update->idle_timeout = ntohs(nfuf->idle_timeout);
+            update->hard_timeout = ntohs(nfuf->hard_timeout);
+            update->table_id = nfuf->table_id;
+            update->cookie = nfuf->cookie;
+            update->priority = ntohs(nfuf->priority);
+
+            if (raw == OFPRAW_ONFST13_FLOW_MONITOR_REPLY) {
+                uint16_t padded_match_len = 0;
+                unsigned int instructions_len;
+
+                error = ofputil_pull_ofp11_match(
+                    msg, NULL, NULL, &update->match, &padded_match_len);
+                if (error) {
+                    return error;
+                }
+
+                instructions_len = length - sizeof *nfuf - padded_match_len;
+                error = ofpacts_pull_openflow_instructions(
+                    msg, instructions_len, oh->version, NULL, NULL, ofpacts);
+                if (error) {
+                    return error;
+                }
+            } else {
+                error = nx_pull_match(msg, match_len, &update->match, NULL,
+                                      NULL, false, NULL, NULL);
+                if (error) {
+                    return error;
+                }
+
+                actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
+                error = ofpacts_pull_openflow_actions(
+                    msg, actions_len, oh->version, NULL, NULL, ofpacts);
+                if (error) {
+                    return error;
+                }
+            }
+
+            update->ofpacts = ofpacts->data;
+            update->ofpacts_len = ofpacts->size;
+            return 0;
+        } else {
+            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
+                         ntohs(nfuh->event));
+            return OFPERR_NXBRC_FM_BAD_EVENT;
+        }
+    }
+    case OFPRAW_OFPST14_FLOW_MONITOR_REPLY: {
+        struct ofp_flow_update_header *ofuh;
+        uint16_t padded_match_len = 0;
+
+        if (msg->size < sizeof(struct ofp_flow_update_header)) {
             goto bad_len;
         }
 
-        nfuf = ofpbuf_pull(msg, sizeof *nfuf);
-        match_len = ntohs(nfuf->match_len);
-        if (sizeof *nfuf + match_len > length) {
+        ofuh = msg->data;
+        update->event = ntohs(ofuh->event);
+        length = ntohs(ofuh->length);
+        if (length > msg->size || length % 8) {
             goto bad_len;
         }
 
-        update->reason = ntohs(nfuf->reason);
-        update->idle_timeout = ntohs(nfuf->idle_timeout);
-        update->hard_timeout = ntohs(nfuf->hard_timeout);
-        update->table_id = nfuf->table_id;
-        update->cookie = nfuf->cookie;
-        update->priority = ntohs(nfuf->priority);
+        if (update->event == OFPFME_ABBREV) {
+            struct ofp_flow_update_abbrev *ofua;
 
-        error = nx_pull_match(msg, match_len, &update->match, NULL, NULL,
-                              false, NULL, NULL);
-        if (error) {
-            return error;
-        }
+            if (length != sizeof *ofua) {
+                goto bad_len;
+            }
 
-        actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
-        error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
-                                              NULL, NULL, ofpacts);
-        if (error) {
-            return error;
-        }
+            ofua = ofpbuf_pull(msg, sizeof *ofua);
+            update->xid = ofua->xid;
+            return 0;
+        } else if (update->event == OFPFME_PAUSED
+                   || update->event == OFPFME_RESUMED) {
+            struct ofp_flow_update_paused *ofup;
 
-        update->ofpacts = ofpacts->data;
-        update->ofpacts_len = ofpacts->size;
-        return 0;
-    } else {
-        VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
-                     ntohs(nfuh->event));
-        return OFPERR_NXBRC_FM_BAD_EVENT;
-    }
+            if (length != sizeof *ofup) {
+                goto bad_len;
+            }
+
+            ofup = ofpbuf_pull(msg, sizeof *ofup);
+            return 0;
+        } else if (update->event == OFPFME_INITIAL
+                   || update->event == OFPFME_ADDED
+                   || update->event == OFPFME_REMOVED
+                   || update->event == OFPFME_MODIFIED) {
+            struct ofp_flow_update_full *ofuf;
+            unsigned int instructions_len;
+
+            if (length < sizeof *ofuf) {
+                goto bad_len;
+            }
 
+            ofuf = ofpbuf_pull(msg, sizeof *ofuf);
+            if (sizeof *ofuf > length) {
+                goto bad_len;
+            }
+
+            update->reason = ofuf->reason;
+            update->idle_timeout = ntohs(ofuf->idle_timeout);
+            update->hard_timeout = ntohs(ofuf->hard_timeout);
+            update->table_id = ofuf->table_id;
+            update->cookie = ofuf->cookie;
+            update->priority = ntohs(ofuf->priority);
+
+            error = ofputil_pull_ofp11_match(
+                msg, NULL, NULL, &update->match, &padded_match_len);
+            if (error) {
+                return error;
+            }
+
+            instructions_len = length - sizeof *ofuf - padded_match_len;
+            error = ofpacts_pull_openflow_instructions(
+                msg, instructions_len, oh->version, NULL, NULL, ofpacts);
+            if (error) {
+                return error;
+            }
+
+            update->ofpacts = ofpacts->data;
+            update->ofpacts_len = ofpacts->size;
+            return 0;
+        } else {
+            VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
+                         ntohs(ofuh->event));
+            return OFPERR_NXBRC_FM_BAD_EVENT;
+        }
+    }
+    default:
+        OVS_NOT_REACHED();
+    }
 bad_len:
-    VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has %"PRIu32" "
-                 "leftover bytes at end", msg->size);
+    VLOG_WARN_RL(&rl, "%s reply has %"PRIu32" leftover bytes at end",
+                ofpraw_get_name(raw), msg->size);
     return OFPERR_OFPBRC_BAD_LEN;
 }
 
 uint32_t
 ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
 {
-    const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
+    enum ofperr error;
+    enum ofpraw raw;
 
-    return ntohl(cancel->id);
+    error = ofpraw_decode(&raw, oh);
+    if (error) {
+        return error;
+    }
+
+    switch ((int) raw) {
+    case OFPRAW_ONFT13_FLOW_MONITOR_CANCEL:
+    case OFPRAW_NXT_FLOW_MONITOR_CANCEL: {
+        const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
+        return ntohl(cancel->id);
+    }
+    default:
+        OVS_NOT_REACHED();
+    }
 }
 
 struct ofpbuf *
@@ -666,9 +1051,99 @@  ofputil_encode_flow_monitor_cancel(uint32_t id, enum ofputil_protocol protocol)
     enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
     struct ofpbuf *msg;
 
-    msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version, 0);
-    nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
-    nfmc->id = htonl(id);
+    switch (version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        if (version == OFP13_VERSION) {
+            msg = ofpraw_alloc(OFPRAW_ONFT13_FLOW_MONITOR_CANCEL, version, 0);
+        } else {
+            msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, version, 0);
+        }
+        nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
+        nfmc->id = htonl(id);
+        break;
+    }
+    case OFP14_VERSION:
+    case OFP15_VERSION: {
+        struct ofp14_flow_monitor_request *ofmr;
+
+        msg = ofpbuf_new(0);
+
+        ofpraw_put(OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, version, msg);
+
+        size_t start_ofs = msg->size;
+        ofpbuf_put_zeros(msg, sizeof *ofmr);
+
+        ofmr = ofpbuf_at_assert(msg, start_ofs, sizeof *ofmr);
+        ofmr->monitor_id = htonl(id);
+        ofmr->command = OFPFMC_DELETE;
+        break;
+    }
+    default:
+        OVS_NOT_REACHED();
+    }
+    return msg;
+}
+
+struct ofpbuf *
+ofputil_encode_flow_monitor_pause(enum ofp_flow_update_event command,
+                                  enum ofputil_protocol protocol)
+{
+    struct ofpbuf *msg;
+    enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
+
+    if (!(command == OFPFME_PAUSED || command == OFPFME_RESUMED)) {
+        OVS_NOT_REACHED();
+    }
+
+    switch (version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        if (command == OFPFME_PAUSED) {
+            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+                                   version, htonl(0), 0);
+        } else {
+            msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED,
+                                   version, htonl(0), 0);
+        }
+        break;
+    case OFP13_VERSION:
+        if (command == OFPFME_PAUSED) {
+            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_PAUSED,
+                                   version, htonl(0), 0);
+        } else {
+            msg = ofpraw_alloc_xid(OFPRAW_ONFT13_FLOW_MONITOR_RESUMED,
+                                   version, htonl(0), 0);
+        }
+        break;
+    case OFP14_VERSION:
+    case OFP15_VERSION: {
+        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY, version,
+                                                   htonl(0), 1024);
+        struct ofp_flow_update_header *ofuh;
+        size_t start_ofs = msg->size;
+
+        struct ofp_flow_update_paused *ofup;
+
+        ofpbuf_put_zeros(msg, sizeof *ofup);
+        ofup = ofpbuf_at_assert(msg, start_ofs, sizeof *ofup);
+        ofup->event = htons(command);
+        ofup->length = htons(8);
+
+        ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
+        ofuh->length = htons(msg->size - start_ofs);
+        ofuh->event = htons(command);
+
+        ofpmsg_update_length(msg);
+        break;
+    }
+    default:
+        OVS_NOT_REACHED();
+    }
+
     return msg;
 }
 
@@ -679,8 +1154,25 @@  ofputil_start_flow_update(struct ovs_list *replies,
     struct ofpbuf *msg;
     enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
 
-    msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
-                           htonl(0), 1024);
+    switch (version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, version,
+                               htonl(0), 1024);
+        break;
+    case OFP13_VERSION:
+        msg = ofpraw_alloc_xid(OFPRAW_ONFST13_FLOW_MONITOR_REPLY, version,
+                               htonl(0), 1024);
+        break;
+    case OFP14_VERSION:
+    case OFP15_VERSION:
+        msg = ofpraw_alloc_xid(OFPRAW_OFPST14_FLOW_MONITOR_REPLY, version,
+                               htonl(0), 1024);
+        break;
+    default:
+        OVS_NOT_REACHED();
+    }
 
     ovs_list_init(replies);
     ovs_list_push_back(replies, &msg->list_node);
@@ -695,7 +1187,6 @@  ofputil_append_flow_update(const struct ofputil_flow_update *update,
         CONST_CAST(struct ofputil_flow_update *, update);
     const struct tun_table *orig_tun_table;
     enum ofp_version version = ofpmp_version(replies);
-    struct nx_flow_update_header *nfuh;
     struct ofpbuf *msg;
     size_t start_ofs;
 
@@ -705,32 +1196,80 @@  ofputil_append_flow_update(const struct ofputil_flow_update *update,
     msg = ofpbuf_from_list(ovs_list_back(replies));
     start_ofs = msg->size;
 
-    if (update->event == NXFME_ABBREV) {
-        struct nx_flow_update_abbrev *nfua;
+    switch (version) {
+        case OFP10_VERSION:
+        case OFP11_VERSION:
+        case OFP12_VERSION:
+        case OFP13_VERSION: {
+             struct nx_flow_update_header *nfuh;
 
-        nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
-        nfua->xid = update->xid;
-    } else {
-        struct nx_flow_update_full *nfuf;
-        int match_len;
+            if (update->event == OFPFME_ABBREV) {
+                struct nx_flow_update_abbrev *nfua;
 
-        ofpbuf_put_zeros(msg, sizeof *nfuf);
-        match_len = nx_put_match(msg, &update->match, htonll(0), htonll(0));
-        ofpacts_put_openflow_actions(update->ofpacts, update->ofpacts_len, msg,
-                                     version);
-        nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
-        nfuf->reason = htons(update->reason);
-        nfuf->priority = htons(update->priority);
-        nfuf->idle_timeout = htons(update->idle_timeout);
-        nfuf->hard_timeout = htons(update->hard_timeout);
-        nfuf->match_len = htons(match_len);
-        nfuf->table_id = update->table_id;
-        nfuf->cookie = update->cookie;
-    }
+                nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
+                nfua->xid = update->xid;
+            } else {
+                struct nx_flow_update_full *nfuf;
+                int match_len;
+
+                ofpbuf_put_zeros(msg, sizeof *nfuf);
+                if (version == OFP13_VERSION) {
+                    match_len = oxm_put_match(msg, &update->match, version);
+                    ofpacts_put_openflow_instructions(
+                        update->ofpacts, update->ofpacts_len, msg, version);
+                } else {
+                    match_len = nx_put_match(msg, &update->match,
+                                             htonll(0), htonll(0));
+                    ofpacts_put_openflow_actions(
+                        update->ofpacts, update->ofpacts_len, msg, version);
+                }
+                nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
+                nfuf->reason = htons(update->reason);
+                nfuf->priority = htons(update->priority);
+                nfuf->idle_timeout = htons(update->idle_timeout);
+                nfuf->hard_timeout = htons(update->hard_timeout);
+                nfuf->match_len = htons(match_len);
+                nfuf->table_id = update->table_id;
+                nfuf->cookie = update->cookie;
+            }
+
+            nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
+            nfuh->length = htons(msg->size - start_ofs);
+            nfuh->event = htons(ofp_to_nx_flow_update_event(update->event));
+            break;
+        }
+        case OFP14_VERSION:
+        case OFP15_VERSION: {
+            struct ofp_flow_update_header *ofuh;
 
-    nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
-    nfuh->length = htons(msg->size - start_ofs);
-    nfuh->event = htons(update->event);
+            if (update->event == OFPFME_ABBREV) {
+                struct ofp_flow_update_abbrev *ofua;
+
+                ofua = ofpbuf_put_zeros(msg, sizeof *ofua);
+                ofua->xid = update->xid;
+            } else {
+                struct ofp_flow_update_full *ofuf;
+
+                ofpbuf_put_zeros(msg, sizeof *ofuf);
+                oxm_put_match(msg, &update->match, version);
+                ofpacts_put_openflow_instructions(update->ofpacts,
+                                                  update->ofpacts_len,
+                                                  msg, version);
+                ofuf = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuf);
+                ofuf->reason = update->reason;
+                ofuf->priority = htons(update->priority);
+                ofuf->idle_timeout = htons(update->idle_timeout);
+                ofuf->hard_timeout = htons(update->hard_timeout);
+                ofuf->table_id = update->table_id;
+                ofuf->cookie = update->cookie;
+            }
+
+            ofuh = ofpbuf_at_assert(msg, start_ofs, sizeof *ofuh);
+            ofuh->length = htons(msg->size - start_ofs);
+            ofuh->event = htons(update->event);
+            break;
+        }
+    }
 
     ofpmp_postappend(replies, start_ofs);
     update_->match.flow.tunnel.metadata.tab = orig_tun_table;
@@ -746,24 +1285,37 @@  ofputil_flow_update_format(struct ds *s,
 
     ds_put_cstr(s, "\n event=");
     switch (update->event) {
-    case NXFME_ADDED:
+    case OFPFME_INITIAL:
+        ds_put_cstr(s, "INITIAL");
+        break;
+
+    case OFPFME_ADDED:
         ds_put_cstr(s, "ADDED");
         break;
 
-    case NXFME_DELETED:
+    case OFPFME_REMOVED:
         ds_put_format(s, "DELETED reason=%s",
                       ofp_flow_removed_reason_to_string(update->reason,
                                                         reasonbuf,
                                                         sizeof reasonbuf));
         break;
 
-    case NXFME_MODIFIED:
+    case OFPFME_MODIFIED:
         ds_put_cstr(s, "MODIFIED");
         break;
 
-    case NXFME_ABBREV:
+    case OFPFME_ABBREV:
         ds_put_format(s, "ABBREV xid=0x%"PRIx32, ntohl(update->xid));
         return;
+
+    case OFPFME_PAUSED:
+        ds_put_cstr(s, "PAUSED");
+        return;
+
+    case OFPFME_RESUMED:
+        ds_put_cstr(s, "RESUMED");
+        return;
+
     }
 
     ds_put_format(s, " table=");
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index b0facbf9f..bd37fa17a 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -744,10 +744,10 @@  ofp_print_nxt_flow_monitor_cancel(struct ds *string,
 }
 
 static enum ofperr
-ofp_print_nxst_flow_monitor_request(struct ds *string,
-                                    const struct ofp_header *oh,
-                                    const struct ofputil_port_map *port_map,
-                                    const struct ofputil_table_map *table_map)
+ofp_print_flow_monitor_request(struct ds *string,
+                               const struct ofp_header *oh,
+                               const struct ofputil_port_map *port_map,
+                               const struct ofputil_table_map *table_map)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     for (;;) {
@@ -765,10 +765,10 @@  ofp_print_nxst_flow_monitor_request(struct ds *string,
 }
 
 static enum ofperr
-ofp_print_nxst_flow_monitor_reply(struct ds *string,
-                                  const struct ofp_header *oh,
-                                  const struct ofputil_port_map *port_map,
-                                  const struct ofputil_table_map *table_map)
+ofp_print_flow_monitor_reply(struct ds *string,
+                             const struct ofp_header *oh,
+                             const struct ofputil_port_map *port_map,
+                             const struct ofputil_table_map *table_map)
 {
     uint64_t ofpacts_stub[1024 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
@@ -1147,12 +1147,12 @@  ofp_to_string__(const struct ofp_header *oh,
         break;
 
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
-        return ofp_print_nxst_flow_monitor_request(string, msg, port_map,
-                                                   table_map);
+        return ofp_print_flow_monitor_request(string, msg, port_map,
+                                              table_map);
 
     case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
-        return ofp_print_nxst_flow_monitor_reply(string, msg, port_map,
-                                                 table_map);
+        return ofp_print_flow_monitor_reply(string, msg, port_map,
+                                            table_map);
 
     case OFPTYPE_BUNDLE_CONTROL:
         return ofp_print_bundle_ctrl(string, msg);
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index c14834f84..325f45966 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -2099,6 +2099,7 @@  ofmonitor_create(const struct ofputil_flow_monitor_request *request,
     m->id = request->id;
     m->flags = request->flags;
     m->out_port = request->out_port;
+    m->out_group = request->out_group;
     m->table_id = request->table_id;
     minimatch_init(&m->match, &request->match);
 
@@ -2134,7 +2135,7 @@  ofmonitor_destroy(struct ofmonitor *m)
 
 void
 ofmonitor_report(struct connmgr *mgr, struct rule *rule,
-                 enum nx_flow_update_event event,
+                 enum ofp_flow_update_event event,
                  enum ofp_flow_removed_reason reason,
                  const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid,
                  const struct rule_actions *old_actions)
@@ -2144,39 +2145,42 @@  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
         return;
     }
 
-    enum nx_flow_monitor_flags update;
+    enum ofp14_flow_monitor_flags update;
     switch (event) {
-    case NXFME_ADDED:
-        update = NXFMF_ADD;
+    case OFPFME_ADDED:
+        update = OFPFMF_ADD;
         rule->add_seqno = rule->modify_seqno = monitor_seqno++;
         break;
 
-    case NXFME_DELETED:
-        update = NXFMF_DELETE;
+    case OFPFME_REMOVED:
+        update = OFPFMF_REMOVED;
         break;
 
-    case NXFME_MODIFIED:
-        update = NXFMF_MODIFY;
+    case OFPFME_MODIFIED:
+        update = OFPFMF_MODIFY;
         rule->modify_seqno = monitor_seqno++;
         break;
 
     default:
-    case NXFME_ABBREV:
+    case OFPFME_INITIAL:
+    case OFPFME_PAUSED:
+    case OFPFME_RESUMED:
+    case OFPFME_ABBREV:
         OVS_NOT_REACHED();
     }
 
     struct ofconn *ofconn;
     LIST_FOR_EACH (ofconn, connmgr_node, &mgr->conns) {
         if (ofconn->monitor_paused) {
-            /* Only send NXFME_DELETED notifications for flows that were added
+            /* Only send OFPFME_REMOVED notifications for flows that were added
              * before we paused. */
-            if (event != NXFME_DELETED
+            if (event != OFPFME_REMOVED
                 || rule->add_seqno > ofconn->monitor_paused) {
                 continue;
             }
         }
 
-        enum nx_flow_monitor_flags flags = 0;
+        enum ofp14_flow_monitor_flags flags = 0;
         struct ofmonitor *m;
         HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
             if (m->flags & update
@@ -2186,6 +2190,7 @@  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
                         && ofpacts_output_to_port(old_actions->ofpacts,
                                                   old_actions->ofpacts_len,
                                                   m->out_port)))
+                && ofproto_rule_has_out_group(rule, m->out_group)
                 && cls_rule_is_loose_match(&rule->cr, &m->match)) {
                 flags |= m->flags;
             }
@@ -2198,12 +2203,12 @@  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
                 ofconn->sent_abbrev_update = false;
             }
 
-            if (flags & NXFMF_OWN || ofconn != abbrev_ofconn
+            if (flags & OFPFMF_ONLY_OWN || ofconn != abbrev_ofconn
                 || ofconn->monitor_paused) {
                 struct ofputil_flow_update fu;
 
                 fu.event = event;
-                fu.reason = event == NXFME_DELETED ? reason : 0;
+                fu.reason = event == OFPFME_REMOVED ? reason : 0;
                 fu.table_id = rule->table_id;
                 fu.cookie = rule->flow_cookie;
                 minimatch_expand(&rule->cr.match, &fu.match);
@@ -2214,7 +2219,7 @@  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
                 fu.hard_timeout = rule->hard_timeout;
                 ovs_mutex_unlock(&rule->mutex);
 
-                if (flags & NXFMF_ACTIONS) {
+                if (flags & OFPFMF_INSTRUCTIONS) {
                     const struct rule_actions *actions
                         = rule_get_actions(rule);
                     fu.ofpacts = actions->ofpacts;
@@ -2228,7 +2233,7 @@  ofmonitor_report(struct connmgr *mgr, struct rule *rule,
             } else if (!ofconn->sent_abbrev_update) {
                 struct ofputil_flow_update fu;
 
-                fu.event = NXFME_ABBREV;
+                fu.event = OFPFME_ABBREV;
                 fu.xid = abbrev_xid;
                 ofputil_append_flow_update(&fu, &ofconn->updates,
                                            ofproto_get_tun_tab(rule->ofproto));
@@ -2263,9 +2268,8 @@  ofmonitor_flush(struct connmgr *mgr)
             COVERAGE_INC(ofmonitor_pause);
             ofconn->monitor_paused = monitor_seqno++;
             protocol = ofconn_get_protocol(ofconn);
-            struct ofpbuf *pause = ofpraw_alloc_xid(
-                OFPRAW_NXT_FLOW_MONITOR_PAUSED,
-                ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
+            struct ofpbuf *pause = ofputil_encode_flow_monitor_pause(
+                OFPFME_PAUSED,protocol);
             ofconn_send(ofconn, pause, counter);
         }
     }
@@ -2289,9 +2293,8 @@  ofmonitor_resume(struct ofconn *ofconn)
                                       ofconn_get_protocol(ofconn));
 
     protocol = ofconn_get_protocol(ofconn);
-    struct ofpbuf *resumed = ofpraw_alloc_xid(
-            OFPRAW_NXT_FLOW_MONITOR_RESUMED,
-            ofputil_protocol_to_ofp_version(protocol), htonl(0), 0);
+    struct ofpbuf *resumed = ofputil_encode_flow_monitor_pause(
+        OFPFME_RESUMED, protocol);
     ovs_list_push_back(&msgs, &resumed->list_node);
     ofconn_send_replies(ofconn, &msgs);
 
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index 56fdc3504..3471d38f9 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -168,10 +168,11 @@  struct ofmonitor {
     struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */
     uint32_t id;
 
-    enum nx_flow_monitor_flags flags;
+    enum ofp14_flow_monitor_flags flags;
 
     /* Matching. */
     ofp_port_t out_port;
+    uint32_t out_group;
     uint8_t table_id;
     struct minimatch match;
 };
@@ -187,7 +188,8 @@  void ofmonitor_destroy(struct ofmonitor *)
     OVS_REQUIRES(ofproto_mutex);
 
 void ofmonitor_report(struct connmgr *, struct rule *,
-                      enum nx_flow_update_event, enum ofp_flow_removed_reason,
+                      enum ofp_flow_update_event event,
+                      enum ofp_flow_removed_reason,
                       const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid,
                       const struct rule_actions *old_actions)
     OVS_REQUIRES(ofproto_mutex);
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 57c7d17cb..373526738 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -419,7 +419,7 @@  struct rule {
      * 'add_seqno' is the sequence number when this rule was created.
      * 'modify_seqno' is the sequence number when this rule was last modified.
      * See 'monitor_seqno' in connmgr.c for more information. */
-    enum nx_flow_monitor_flags monitor_flags OVS_GUARDED_BY(ofproto_mutex);
+    enum ofp14_flow_monitor_flags monitor_flags OVS_GUARDED_BY(ofproto_mutex);
     uint64_t add_seqno OVS_GUARDED_BY(ofproto_mutex);
     uint64_t modify_seqno OVS_GUARDED_BY(ofproto_mutex);
 
@@ -480,6 +480,8 @@  const struct rule_actions *rule_actions_create(const struct ofpact *, size_t);
 void rule_actions_destroy(const struct rule_actions *);
 bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port)
     OVS_REQUIRES(ofproto_mutex);
+bool ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
+    OVS_REQUIRES(ofproto_mutex);
 
 #define DECL_OFPROTO_COLLECTION(TYPE, NAME)                             \
     DECL_OBJECT_COLLECTION(TYPE, NAME)                                  \
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 11aadbf20..980d4d53b 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3157,7 +3157,7 @@  ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port)
 }
 
 /* Returns true if 'rule' has group and equals group_id. */
-static bool
+bool
 ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
     OVS_REQUIRES(ofproto_mutex)
 {
@@ -5215,7 +5215,7 @@  add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
     if (old_rule) {
         ovsrcu_postpone(remove_rule_rcu, old_rule);
     } else {
-        ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0,
+        ofmonitor_report(ofproto->connmgr, new_rule, OFPFME_ADDED, 0,
                          req ? req->ofconn : NULL,
                          req ? req->request->xid : 0, NULL);
 
@@ -5630,8 +5630,8 @@  replace_rule_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
         learned_cookies_dec(ofproto, old_actions, dead_cookies);
 
         if (replaced_rule) {
-            enum nx_flow_update_event event = ofm->command == OFPFC_ADD
-                ? NXFME_ADDED : NXFME_MODIFIED;
+            enum ofp_flow_update_event event = ofm->command == OFPFC_ADD
+                ? OFPFME_ADDED : OFPFME_MODIFIED;
 
             bool changed_cookie = (new_rule->flow_cookie
                                    != old_rule->flow_cookie);
@@ -5641,7 +5641,7 @@  replace_rule_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
                                                   old_actions->ofpacts,
                                                   old_actions->ofpacts_len);
 
-            if (event != NXFME_MODIFIED || changed_actions
+            if (event != OFPFME_MODIFIED || changed_actions
                 || changed_cookie) {
                 ofmonitor_report(ofproto->connmgr, new_rule, event, 0,
                                  req ? req->ofconn : NULL,
@@ -5650,7 +5650,7 @@  replace_rule_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
             }
         } else {
             /* XXX: This is slight duplication with delete_flows_finish__() */
-            ofmonitor_report(ofproto->connmgr, old_rule, NXFME_DELETED,
+            ofmonitor_report(ofproto->connmgr, old_rule, OFPFME_REMOVED,
                              OFPRR_EVICTION,
                              req ? req->ofconn : NULL,
                              req ? req->request->xid : 0, NULL);
@@ -5931,7 +5931,7 @@  delete_flows_finish__(struct ofproto *ofproto,
              * before the rule is actually destroyed. */
             rule->removed_reason = reason;
 
-            ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED, reason,
+            ofmonitor_report(ofproto->connmgr, rule, OFPFME_REMOVED, reason,
                              req ? req->ofconn : NULL,
                              req ? req->request->xid : 0, NULL);
 
@@ -6337,7 +6337,7 @@  handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
 
 static void
 ofproto_compose_flow_refresh_update(const struct rule *rule,
-                                    enum nx_flow_monitor_flags flags,
+                                    enum ofp14_flow_monitor_flags flags,
                                     struct ovs_list *msgs,
                                     const struct tun_table *tun_table,
                                     enum ofputil_protocol protocol)
@@ -6346,8 +6346,9 @@  ofproto_compose_flow_refresh_update(const struct rule *rule,
     const struct rule_actions *actions;
     struct ofputil_flow_update fu;
 
-    fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
-                ? NXFME_ADDED : NXFME_MODIFIED);
+    fu.event = flags & OFPFMF_INITIAL ? OFPFME_INITIAL :
+               flags & OFPFMF_ADD ?
+                         OFPFME_ADDED : OFPFME_MODIFIED;
     fu.reason = 0;
     ovs_mutex_lock(&rule->mutex);
     fu.idle_timeout = rule->idle_timeout;
@@ -6358,7 +6359,7 @@  ofproto_compose_flow_refresh_update(const struct rule *rule,
     minimatch_expand(&rule->cr.match, &fu.match);
     fu.priority = rule->cr.priority;
 
-    actions = flags & NXFMF_ACTIONS ? rule_get_actions(rule) : NULL;
+    actions = flags & OFPFMF_INSTRUCTIONS ? rule_get_actions(rule) : NULL;
     fu.ofpacts = actions ? actions->ofpacts : NULL;
     fu.ofpacts_len = actions ? actions->ofpacts_len : 0;
 
@@ -6377,7 +6378,7 @@  ofmonitor_compose_refresh_updates(struct rule_collection *rules,
     struct rule *rule;
 
     RULE_COLLECTION_FOR_EACH (rule, rules) {
-        enum nx_flow_monitor_flags flags = rule->monitor_flags;
+        enum ofp14_flow_monitor_flags flags = rule->monitor_flags;
         rule->monitor_flags = 0;
 
         ofproto_compose_flow_refresh_update(rule, flags, msgs,
@@ -6391,7 +6392,7 @@  ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
                                        struct rule_collection *rules)
     OVS_REQUIRES(ofproto_mutex)
 {
-    enum nx_flow_monitor_flags update;
+    enum ofp14_flow_monitor_flags update;
 
     if (rule_is_hidden(rule)) {
         return;
@@ -6401,11 +6402,15 @@  ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
         return;
     }
 
+    if (!ofproto_rule_has_out_group(rule, m->out_group)) {
+        return;
+    }
+
     if (seqno) {
         if (rule->add_seqno > seqno) {
-            update = NXFMF_ADD | NXFMF_MODIFY;
+            update = OFPFMF_ADD | OFPFMF_MODIFY;
         } else if (rule->modify_seqno > seqno) {
-            update = NXFMF_MODIFY;
+            update = OFPFMF_MODIFY;
         } else {
             return;
         }
@@ -6414,13 +6419,13 @@  ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
             return;
         }
     } else {
-        update = NXFMF_INITIAL;
+        update = OFPFMF_INITIAL;
     }
 
     if (!rule->monitor_flags) {
         rule_collection_add(rules, rule);
     }
-    rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
+    rule->monitor_flags |= update | (m->flags & OFPFMF_INSTRUCTIONS);
 }
 
 static void
@@ -6449,7 +6454,7 @@  ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m,
                                         struct rule_collection *rules)
     OVS_REQUIRES(ofproto_mutex)
 {
-    if (m->flags & NXFMF_INITIAL) {
+    if (m->flags & OFPFMF_INITIAL) {
         ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
     }
 }
@@ -6512,16 +6517,50 @@  handle_flow_monitor_request(struct ofconn *ofconn, const struct ovs_list *msgs)
             }
 
             struct ofmonitor *m;
-            error = ofmonitor_create(&request, ofconn, &m);
-            if (error) {
-                goto error;
+            switch (request.command) {
+            case OFPFMC_ADD: {
+                error = ofmonitor_create(&request, ofconn, &m);
+                if (error) {
+                    goto error;
+                }
+
+                if (n_monitors >= allocated_monitors) {
+                    monitors = x2nrealloc(monitors, &allocated_monitors,
+                                          sizeof *monitors);
+                }
+                monitors[n_monitors++] = m;
+                break;
             }
+            case OFPFMC_MODIFY:
+                /* Modify operation is to delete old monitor and create a
+                 * new one. */
+                m = ofmonitor_lookup(ofconn, request.id);
+                if (!m) {
+                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
+                    goto error;
+                }
+                ofmonitor_destroy(m);
+
+                error = ofmonitor_create(&request, ofconn, &m);
+                if (error) {
+                    goto error;
+                }
 
-            if (n_monitors >= allocated_monitors) {
-                monitors = x2nrealloc(monitors, &allocated_monitors,
-                                      sizeof *monitors);
+                if (n_monitors >= allocated_monitors) {
+                    monitors = x2nrealloc(monitors, &allocated_monitors,
+                                          sizeof *monitors);
+                }
+                monitors[n_monitors++] = m;
+                break;
+            case OFPFMC_DELETE:
+                m = ofmonitor_lookup(ofconn, request.id);
+                if (!m) {
+                    error = OFPERR_OFPMOFC_UNKNOWN_MONITOR;
+                    goto error;
+                }
+                ofmonitor_destroy(m);
+                break;
             }
-            monitors[n_monitors++] = m;
             continue;
 
         error:
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 2c7e163bd..ab5c37649 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -3258,32 +3258,89 @@  NXT_SET_CONTROLLER_ID (xid=0x3): id=123
 ])
 AT_CLEANUP
 
-AT_SETUP([NXT_FLOW_MONITOR_CANCEL])
+AT_SETUP([FLOW_MONITOR_CANCEL])
 AT_KEYWORDS([ofp-print])
+
+dnl OpenFlow 1.0-1.2
 AT_CHECK([ovs-ofctl ofp-print "\
 01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \
 01 02 30 40 \
 "], [0], [dnl
 NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640
 ])
+
+dnl OpenFlow 1.3
+AT_CHECK([ovs-ofctl ofp-print "\
+04 04 00 14 00 00 00 06 4f 4e 46 00 00 00 07 4e \
+01 02 30 40 \
+"], [0], [dnl
+ONFT_FLOW_MONITOR_CANCEL (OF1.3) (xid=0x6): id=16920640
+])
+
+dnl OpenFlow 1.4+
+AT_CHECK([ovs-ofctl ofp-print "\
+05 12 00 28 00 00 00 04 00 10 00 00 00 00 00 00 \
+01 02 30 40 00 00 00 00 00 00 00 00 00 00 00 02 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW_MONITOR request (OF1.4) (xid=0x4):
+ id=16920640 command=delete
+])
+
 AT_CLEANUP
 
-AT_SETUP([NXT_FLOW_MONITOR_PAUSED])
+AT_SETUP([FLOW_MONITOR_PAUSED])
 AT_KEYWORDS([ofp-print])
+
+dnl OpenFlow 1.0-1.2
 AT_CHECK([ovs-ofctl ofp-print "\
 01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \
 "], [0], [dnl
 NXT_FLOW_MONITOR_PAUSED (xid=0x3):
 ])
+
+dnl OpenFlow 1.3
+AT_CHECK([ovs-ofctl ofp-print "\
+04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 4F \
+"], [0], [dnl
+ONFT_FLOW_MONITOR_PAUSED (OF1.3) (xid=0x3):
+])
+
+dnl OpenFlow 1.4+
+AT_CHECK([ovs-ofctl ofp-print "\
+05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00 \
+00 08 00 05 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
+ event=PAUSED
+])
 AT_CLEANUP
 
-AT_SETUP([NXT_FLOW_MONITOR_RESUMED])
+AT_SETUP([FLOW_MONITOR_RESUMED])
 AT_KEYWORDS([ofp-print])
+
+dnl OpenFlow 1.0-1.2
 AT_CHECK([ovs-ofctl ofp-print "\
 01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \
 "], [0], [dnl
 NXT_FLOW_MONITOR_RESUMED (xid=0x3):
 ])
+
+dnl OpenFlow 1.3
+AT_CHECK([ovs-ofctl ofp-print "\
+04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 50 \
+"], [0], [dnl
+ONFT_FLOW_MONITOR_RESUMED (OF1.3) (xid=0x3):
+])
+
+dnl OpenFlow 1.4+
+AT_CHECK([ovs-ofctl ofp-print "\
+05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00
+00 08 00 06 00 00 00 00
+"], [0], [dnl
+OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
+ event=RESUMED
+])
 AT_CLEANUP
 
 AT_SETUP([NXT_SET_FLOW_FORMAT])
@@ -3629,8 +3686,10 @@  NXST_AGGREGATE reply (xid=0x4): packet_count=7 byte_count=420 flow_count=7
 ])
 AT_CLEANUP
 
-AT_SETUP([NXST_FLOW_MONITOR request])
+AT_SETUP([FLOW_MONITOR request])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+
+dnl OpenFlow 1.0-1.2
 AT_CHECK([ovs-ofctl ofp-print "\
 01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
 00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \
@@ -3640,10 +3699,37 @@  NXST_FLOW_MONITOR request (xid=0x4):
  id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=1
  id=8192 flags=delete table=2 in_port=1
 ])
+
+dnl OpenFlow 1.3
+AT_CHECK([ovs-ofctl ofp-print "\
+04 12 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 \
+4f 4e 46 00 00 00 07 4e \
+00 00 10 00 00 3f 00 04 ff ff ff fe 01 00 00 00 00 01 00 04 00 00 00 00 \
+00 00 20 00 00 04 ff ff 00 00 00 02 01 00 00 00 00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+ONFST_FLOW_MONITOR request (OF1.3) (xid=0x6):
+ id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=1
+ id=8192 flags=delete out_port=2 table=1
+])
+
+dnl OpenFlow 1.4+
+AT_CHECK([ovs-ofctl ofp-print "\
+05 12 00 58 00 00 00 06 00 10 00 00 00 00 00 00 \
+00 00 10 00 ff ff ff fe ff ff ff ff 00 5f 00 00 00 01 00 04 00 00 00 00 \
+00 00 20 00 00 00 00 01 00 00 00 40 00 5f 01 01 00 01 00 04 00 00 00 00 \
+00 00 40 00 00 00 00 02 00 00 00 40 00 5f 02 02 00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW_MONITOR request (OF1.4) (xid=0x6):
+ id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=0
+ id=8192 flags=initial,add,delete,modify,actions,own out_port=1 out_group=64 table=1
+ id=16384 command=delete
+])
 AT_CLEANUP
 
-AT_SETUP([NXST_FLOW_MONITOR reply])
+AT_SETUP([FLOW_MONITOR reply])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+
+dnl OpenFlow 1.0-1.2
 AT_CHECK([ovs-ofctl ofp-print "\
 01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
 00 20 00 01 00 05 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \
@@ -3654,6 +3740,32 @@  NXST_FLOW_MONITOR reply (xid=0x4):
  event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16 cookie=0x123456789abcdef0 in_port=1
  event=ABBREV xid=0x186a0
 ])
+
+dnl OpenFlow 1.3
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 4f 4e 46 00 00 00 07 4e \
+00 28 00 01 00 05 80 00 00 00 00 00 00 0c 00 00 \
+12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
+00 00 00 01 00 00 00 00 \
+00 08 00 03 00 01 86 a0 \
+"], [0], [dnl
+ONFST_FLOW_MONITOR reply (OF1.3) (xid=0x6):
+ event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0 in_port=1
+ event=ABBREV xid=0x186a0
+])
+
+dnl OpenFlow 1.4+
+AT_CHECK([ovs-ofctl ofp-print "\
+05 13 00 40 00 00 00 00 00 10 00 00 00 00 00 00 \
+00 28 00 02 00 05 00 00 00 00 80 00 00 00 00 00 \
+12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \
+00 00 00 01 00 00 00 00 \
+00 08 00 04 00 01 86 a0 \
+"], [0], [dnl
+OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
+ event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0 in_port=1
+ event=ABBREV xid=0x186a0
+])
 AT_CLEANUP
 
 
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 899c0be55..3502454b9 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -4592,8 +4592,11 @@  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:1
 ovs-ofctl -O $2 monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1
 AT_CAPTURE_FILE([monitor.log])
 ovs-appctl -t ovs-ofctl ofctl/barrier
+
+# For OF(1.4+), replace INITIAL to ADDED
+sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
-  [NXST_FLOW_MONITOR reply$3:
+  [$4 reply$3:
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
 OFPT_BARRIER_REPLY$3:
 ])
@@ -4607,7 +4610,7 @@  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7
 ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8
 ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9
 ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10
-ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,actions=output:11
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=3,actions=output:11
 ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12
 ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13
 ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14
@@ -4625,71 +4628,74 @@  ovs-ofctl mod-flows br0 cookie=5,dl_vlan=123,actions=output:3
 ovs-ofctl del-flows br0 dl_vlan=123
 ovs-ofctl del-flows br0
 ovs-appctl -t ovs-ofctl ofctl/barrier
+
+# For OF(1.4+), replace INITIAL to ADDED
+sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log | multiline_sort], [0],
-[NXST_FLOW_MONITOR reply$3 (xid=0x0):
+[$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:6
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:7
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:9
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:10
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
- event=ADDED table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:15
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:16
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=0 actions=output:23
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:3
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
  event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
  event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
  event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
  event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=DELETED reason=delete table=0 cookie=0 in_port=0 actions=output:23
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
  event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
- event=DELETED reason=delete table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
 OFPT_BARRIER_REPLY$3:
 ])
 
@@ -4703,13 +4709,13 @@  ovs-appctl -t ovs-ofctl ofctl/barrier
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
 ])
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log | multiline_sort], [0],
-[NXST_FLOW_MONITOR reply$3 (xid=0x0):
+[$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=1 actions=output:2
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=2 actions=output:1
 OFPT_BARRIER_REPLY$3:
 send: OFPT_FLOW_MOD$3: DEL actions=drop
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=DELETED reason=delete table=0 cookie=0 in_port=1 actions=output:2
  event=DELETED reason=delete table=0 cookie=0 in_port=2 actions=output:1
 OFPT_BARRIER_REPLY$3:
@@ -4732,8 +4738,11 @@  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:1
 ovs-ofctl -O $2 monitor br0 watch:\!own --detach --no-chdir --pidfile >monitor.log 2>&1
 AT_CAPTURE_FILE([monitor.log])
 ovs-appctl -t ovs-ofctl ofctl/barrier
+
+# For OF(1.4+), replace INITIAL to ADDED
+sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
-  [NXST_FLOW_MONITOR reply$3:
+  [$4 reply$3:
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
 OFPT_BARRIER_REPLY$3:
 ])
@@ -4747,14 +4756,17 @@  ovs-appctl -t ovs-ofctl ofctl/send $send_buf
 ovs-appctl -t ovs-ofctl ofctl/barrier
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
 ])
+
+# For OF(1.4+), replace INITIAL to ADDED
+sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
-[NXST_FLOW_MONITOR reply$3 (xid=0x0):
+[$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=1 actions=output:2
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=2 actions=output:1
 OFPT_BARRIER_REPLY$3:
 send: OFPT_FLOW_MOD$3: DEL actions=drop
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=ABBREV xid=0x12345678
 OFPT_BARRIER_REPLY$3:
 ])
@@ -4775,8 +4787,11 @@  ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:2
 ovs-ofctl -O $2 monitor br0 watch:out_port=2 --detach --no-chdir --pidfile >monitor.log 2>&1
 AT_CAPTURE_FILE([monitor.log])
 ovs-appctl -t ovs-ofctl ofctl/barrier
+
+# For OF(1.4+), replace INITIAL to ADDED
+sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
-  [NXST_FLOW_MONITOR reply$3:
+  [$4 reply$3:
  event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
 OFPT_BARRIER_REPLY$3:
 ])
@@ -4796,17 +4811,19 @@  ovs-appctl -t ovs-ofctl ofctl/barrier
 ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:2
 ovs-appctl -t ovs-ofctl ofctl/barrier
 
+# For OF(1.4+), replace INITIAL to ADDED
+sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
-[NXST_FLOW_MONITOR reply$3 (xid=0x0):
+[$4 reply$3 (xid=0x0):
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1,output:2
 OFPT_BARRIER_REPLY$3:
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1,output:2
 OFPT_BARRIER_REPLY$3:
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1
 OFPT_BARRIER_REPLY$3:
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3 (xid=0x0):
  event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2
 OFPT_BARRIER_REPLY$3:
 ])
@@ -4869,7 +4886,7 @@  ovs-appctl -t ovs-ofctl ofctl/unblock
 # A barrier doesn't work for this purpose.
 #    https://www.mail-archive.com/dev@openvswitch.org/msg27013.html
 #    https://www.mail-archive.com/dev@openvswitch.org/msg27675.html
-OVS_WAIT_UNTIL([grep NXT_FLOW_MONITOR_RESUMED monitor.log])
+OVS_WAIT_UNTIL([grep RESUMED monitor.log])
 
 OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
 
@@ -4897,24 +4914,30 @@  AT_CHECK([test $adds = $deletes])
 # and MODIFIED lines lines depends on hash order, that is, it varies
 # as we change the hash function or change architecture.  Therefore,
 # we use a couple of tests below to accept both orders.
+
+# Rename all version specific strings to a generic one
+sed -i'.raw' -e 's|NXT_FLOW_MONITOR|FLOW_MONITOR|' -e 's|ONFT_FLOW_MONITOR|FLOW_MONITOR|' monitor.log
+sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n event=PAUSED|FLOW_MONITOR_PAUSED$3:|' monitor.log
+sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n event=RESUMED|FLOW_MONITOR_RESUMED$3:|' monitor.log
+
 AT_CHECK([ofctl_strip < monitor.log | sed -n -e '
 /reg1=0x22$/p
 /cookie=0x[[23]]/p
-/NXT_FLOW_MONITOR_PAUSED$3:/p
-/NXT_FLOW_MONITOR_RESUMED$3:/p
+/FLOW_MONITOR_PAUSED$3:/p
+/FLOW_MONITOR_RESUMED$3:/p
 ' > monitor.log.subset])
 AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl
  event=ADDED table=0 cookie=0x1 reg1=0x22
-NXT_FLOW_MONITOR_PAUSED$3:
+FLOW_MONITOR_PAUSED$3:
  event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
  event=ADDED table=0 cookie=0x3 in_port=1
-NXT_FLOW_MONITOR_RESUMED$3:
+FLOW_MONITOR_RESUMED$3:
 ])
 AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl
-NXT_FLOW_MONITOR_PAUSED$3:
+FLOW_MONITOR_PAUSED$3:
  event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
  event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
-NXT_FLOW_MONITOR_RESUMED$3:
+FLOW_MONITOR_RESUMED$3:
 ])
 
 OVS_VSWITCHD_STOP
@@ -4931,7 +4954,7 @@  ovs-ofctl -O $2 monitor br0 watch:udp,udp_dst=8 --detach --no-chdir --pidfile >m
 AT_CAPTURE_FILE([monitor.log])
 
 # Wait till reply comes backs with OF Version
-OVS_WAIT_UNTIL([grep "NXST_FLOW_MONITOR reply$3" monitor.log])
+OVS_WAIT_UNTIL([grep "$4 reply$3" monitor.log])
 ovs-appctl -t ovs-ofctl exit
 
 # Make sure protocol type in messages from vswitchd, matches that of requested protocol
@@ -4940,17 +4963,64 @@  ovs-ofctl add-flow br0 sctp,sctp_dst=9,action=normal
 
 OVS_WAIT_UNTIL([grep "event=ADDED " monitor.log])
 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
-NXST_FLOW_MONITOR reply$3:
-NXST_FLOW_MONITOR reply$3 (xid=0x0):
+$4 reply$3:
+$4 reply$3 (xid=0x0):
  event=ADDED table=0 cookie=0 sctp,tp_dst=9 actions=NORMAL
 ])
 OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 ])
-CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [])
-CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)])
-CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)])
+CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [],         NXST_FLOW_MONITOR)
+CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)], NXST_FLOW_MONITOR)
+CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)], NXST_FLOW_MONITOR)
+CHECK_FLOW_MONITORING([1.3], [OpenFlow13], [ (OF1.3)], ONFST_FLOW_MONITOR)
+CHECK_FLOW_MONITORING([1.4], [OpenFlow14], [ (OF1.4)], OFPST_FLOW_MONITOR)
+CHECK_FLOW_MONITORING([1.5], [OpenFlow15], [ (OF1.5)], OFPST_FLOW_MONITOR)
+
+AT_SETUP([ofproto - OpenFlow14 flow monitoring with out_group])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0 group_id=1,type=all,bucket=output:1])
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0 group_id=2,type=all,bucket=output:2])
+
+ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=121,actions=output:1
+ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=122,actions=group:1
+ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=123,actions=group:2
+
+# Start a monitor watching the flow table and check the initial reply.
+ovs-ofctl -OOpenFlow14 monitor br0 watch:out_group=2 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+ovs-appctl -t ovs-ofctl ofctl/barrier
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+[OFPST_FLOW_MONITOR reply (OF1.4):
+ event=INITIAL table=0 cookie=0 in_port=0,dl_vlan=123 actions=group:2
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+
+# Add, modify flows and check the updates.
+ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=121,actions=group:2
+ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=122,actions=group:2
+ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=123,actions=group:1
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=124,actions=group:2
+
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+[OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
+ event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=121 actions=group:2
+OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
+ event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=group:2
+OFPT_BARRIER_REPLY (OF1.4):
+OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=group:2
+])
+
+OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
 
 AT_SETUP([ofproto - event filtering (OpenFlow 1.3)])
 AT_KEYWORDS([monitor])
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 2017c6eba..d9336a43b 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -626,6 +626,9 @@  monitored.
 If set, only flows that output to \fIport\fR are monitored.  The
 \fIport\fR may be an OpenFlow port number or keyword
 (e.g. \fBLOCAL\fR).
+.IP "\fBout_group=\fIgroup\fR"
+If set, only flows that output to \fIgroup\fR number are monitored.
+This field requires OpenFlow 1.4 (-OOpenFlow14) or later.
 .IP "\fIfield\fB=\fIvalue\fR"
 Monitors only flows that have \fIfield\fR specified as the given
 \fIvalue\fR.  Any syntax valid for matching on \fBdump\-flows\fR may
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 614b73006..5e6823240 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -2313,6 +2313,12 @@  ofctl_monitor(struct ovs_cmdl_context *ctx)
 
             msg = ofpbuf_new(0);
             ofputil_append_flow_monitor_request(&fmr, msg, protocol);
+
+            if (verbosity) {
+                ofpmsg_update_length(msg);
+                ofp_print(stdout, msg->data, msg->size, NULL,
+                          NULL, verbosity + 2);
+            }
             dump_transaction(vconn, msg);
             fflush(stdout);
         } else if (!strcmp(arg, "resume")) {