[ovs-dev,1/2] ofctl: This patch add support for setting the first egress table for egress processing.
diff mbox

Message ID 1450858032-16796-1-git-send-email-niti.rohilla@tcs.com
State Rejected
Headers show

Commit Message

niti1489@gmail.com Dec. 23, 2015, 8:07 a.m. UTC
From: Niti Rohilla <niti.rohilla@tcs.com>

"ovs-ofctl set-first-egress-table <SWITCH> <table_id>" can be used to set first egress
table.
This patch enhances TABLE_FEATURES_REQUEST message to set the first egress table and
TABLE_FEATURES_REPLY to identify the table which is configured as first egress table.

Signed-off-by: Niti Rohilla <niti.rohilla@tcs.com>
---
 include/openflow/openflow-1.5.h | 168 +++++++++++++++
 lib/ofp-actions.c               |  23 ++
 lib/ofp-actions.h               |   4 +-
 lib/ofp-msgs.h                  |  16 +-
 lib/ofp-parse.c                 |  24 +++
 lib/ofp-parse.h                 |   6 +
 lib/ofp-print.c                 |   6 +
 lib/ofp-util.c                  | 460 +++++++++++++++++++++++++++++++++++-----
 lib/ofp-util.h                  |  16 +-
 ofproto/ofproto-provider.h      |   6 +
 ofproto/ofproto.c               |  57 ++++-
 tests/ofp-print.at              | 222 +++++++++++++++++++
 tests/ofproto.at                | 233 ++++++++++++++++++++
 utilities/ovs-ofctl.c           |  36 +++-
 14 files changed, 1206 insertions(+), 71 deletions(-)

Comments

Ben Pfaff Jan. 11, 2016, 6:27 p.m. UTC | #1
On Wed, Dec 23, 2015 at 01:37:12PM +0530, niti1489@gmail.com wrote:
> From: Niti Rohilla <niti.rohilla@tcs.com>
> 
> "ovs-ofctl set-first-egress-table <SWITCH> <table_id>" can be used to set first egress
> table.
> This patch enhances TABLE_FEATURES_REQUEST message to set the first egress table and
> TABLE_FEATURES_REPLY to identify the table which is configured as first egress table.
> 
> Signed-off-by: Niti Rohilla <niti.rohilla@tcs.com>

This appears to completely cut and paste and modify the OF1.3 table
features support, as well as all of the OF1.3 table features protocol
definitions.

This also hard-codes OFPAT_ values.

This also adds a global variable to a utility library.

None of this is acceptable.  Rejected.x
niti1489@gmail.com Jan. 13, 2016, 9:48 a.m. UTC | #2
Hi Ben,

I can incorporate the egress table implemenation in OF1.3 table features
code and will remove the implementation added for OF1.5.

I will replace the pad[5] field in of13_table_features structure with
command and features field as specified in OF1.5.1 specifications. I can
also remove the ofp15_table_feature_prop_type enums, OF1.5 protocol
definition and code implementation added for the same. Kindly suggest is
this approach is fine.

I can replace the hard coded values for OFPAT_ with OFPACT_OUTPUT and
OFPACT_GROUP.

I have created a global variable "egress_table_id" that contain the table
id of first egress table. This variable is declared as global because I
need the first egress table id to add the checks in ofp-action.c file (in
ofpact_check__()) to prevent adding the output and group action in action
set of egress tables and to disallow the ingress flow tables to direct the
packet to egress flow tables via GOTO_TABLE instruction.

We need access to the ofproto structure in ofpact_check__().
ofpact_check__() is called from various places. One scenraio is,
ofp_print_flow_mod() -> ofputil_decode_flow_mod() ->
ofpacts_check_consistency() -> ofpacts_check() -> ofpact_check__(). We
don't have access to ofproto strcuture in ofp_print_flow_mod(). Please
suggest if there is any other possible way through which I can get the
first egress table id (stored in ofproto strcuture) in ofp-actions.c file.

I want to clarify one more doubt. In the 2nd patch, when a packet start
egress processing then output port is added to the action_set and we should
also set the actset_output variable. So can we directly add the output port
in action_set and actset_output or we should call xlate_write_action() to
add the output port in action_set and actset_output variable. Please
suggest which approach should be followed for this.

Thanks
Niti Rohilla

On Mon, Jan 11, 2016 at 11:57 PM, Ben Pfaff <blp@ovn.org> wrote:

> On Wed, Dec 23, 2015 at 01:37:12PM +0530, niti1489@gmail.com wrote:
> > From: Niti Rohilla <niti.rohilla@tcs.com>
> >
> > "ovs-ofctl set-first-egress-table <SWITCH> <table_id>" can be used to
> set first egress
> > table.
> > This patch enhances TABLE_FEATURES_REQUEST message to set the first
> egress table and
> > TABLE_FEATURES_REPLY to identify the table which is configured as first
> egress table.
> >
> > Signed-off-by: Niti Rohilla <niti.rohilla@tcs.com>
>
> This appears to completely cut and paste and modify the OF1.3 table
> features support, as well as all of the OF1.3 table features protocol
> definitions.
>
> This also hard-codes OFPAT_ values.
>
> This also adds a global variable to a utility library.
>
> None of this is acceptable.  Rejected.x
>
Ben Pfaff Jan. 13, 2016, 5:16 p.m. UTC | #3
On Wed, Jan 13, 2016 at 03:18:02PM +0530, niti Rohilla wrote:
> I can incorporate the egress table implemenation in OF1.3 table features
> code and will remove the implementation added for OF1.5.

Good.

> I will replace the pad[5] field in of13_table_features structure with
> command and features field as specified in OF1.5.1 specifications. I can
> also remove the ofp15_table_feature_prop_type enums, OF1.5 protocol
> definition and code implementation added for the same. Kindly suggest is
> this approach is fine.

OK.

> I can replace the hard coded values for OFPAT_ with OFPACT_OUTPUT and
> OFPACT_GROUP.

OK.

> I have created a global variable "egress_table_id" that contain the table
> id of first egress table. This variable is declared as global because I
> need the first egress table id to add the checks in ofp-action.c file (in
> ofpact_check__()) to prevent adding the output and group action in action
> set of egress tables and to disallow the ingress flow tables to direct the
> packet to egress flow tables via GOTO_TABLE instruction.
> 
> We need access to the ofproto structure in ofpact_check__().
> ofpact_check__() is called from various places. One scenraio is,
> ofp_print_flow_mod() -> ofputil_decode_flow_mod() ->
> ofpacts_check_consistency() -> ofpacts_check() -> ofpact_check__(). We
> don't have access to ofproto strcuture in ofp_print_flow_mod(). Please
> suggest if there is any other possible way through which I can get the
> first egress table id (stored in ofproto strcuture) in ofp-actions.c file.

ofp_print_flow_mod() doesn't need to know the real egress_table_id, so
you can use some default that will accept all flows.

> I want to clarify one more doubt. In the 2nd patch, when a packet start
> egress processing then output port is added to the action_set and we should
> also set the actset_output variable. So can we directly add the output port
> in action_set and actset_output or we should call xlate_write_action() to
> add the output port in action_set and actset_output variable. Please
> suggest which approach should be followed for this.

I would add it directly.
niti1489@gmail.com Jan. 14, 2016, 1:55 p.m. UTC | #4
Hi Ben,

With reference to global variable "egress_table_id", I have a doubt. I can
send '0' as the default value from ofp_print_flow_mod() to
ofpact_check__(). Right now, ofpact_check__() access the first egress table
id from global variable "egress_table_id", if I remove the global variable
then egress table id has to sent be from the all the functions that
internally call ofpact_check__(). For example,

ofctl_add_flow()->ofctl_flow_mod()->parse_ofp_flow_mod_str()->parse_ofp_str()->parse_ofp_str__()->ofpacts_check()->ofpact_check__()

In this hierarchy, first I have to get the first egress table from the
switch using fetch_table_features() and egress table id has to be sent from
ofctl_add_flow() and I need to change the definition of all the functions
to include one more parameter to send the first egress table id to
ofpact_check__().

Functions that modify the flows needs to first query the first egress table
from the switch and send it to ofpact_check__ and functions that only parse
flows for e.g ofctl_parse_flows() can send the default value i.e., but
definition of all the functions included in the hierarchy has to be changed
to include one more parameter. It requires changing the definition of many
functions.

That is why I have used the global variable. Please suggest which approach
should be followed.

Thanks
Niti Rohilla

On Wed, Jan 13, 2016 at 10:46 PM, Ben Pfaff <blp@ovn.org> wrote:

> On Wed, Jan 13, 2016 at 03:18:02PM +0530, niti Rohilla wrote:
> > I can incorporate the egress table implemenation in OF1.3 table features
> > code and will remove the implementation added for OF1.5.
>
> Good.
>
> > I will replace the pad[5] field in of13_table_features structure with
> > command and features field as specified in OF1.5.1 specifications. I can
> > also remove the ofp15_table_feature_prop_type enums, OF1.5 protocol
> > definition and code implementation added for the same. Kindly suggest is
> > this approach is fine.
>
> OK.
>
> > I can replace the hard coded values for OFPAT_ with OFPACT_OUTPUT and
> > OFPACT_GROUP.
>
> OK.
>
> > I have created a global variable "egress_table_id" that contain the table
> > id of first egress table. This variable is declared as global because I
> > need the first egress table id to add the checks in ofp-action.c file (in
> > ofpact_check__()) to prevent adding the output and group action in action
> > set of egress tables and to disallow the ingress flow tables to direct
> the
> > packet to egress flow tables via GOTO_TABLE instruction.
> >
> > We need access to the ofproto structure in ofpact_check__().
> > ofpact_check__() is called from various places. One scenraio is,
> > ofp_print_flow_mod() -> ofputil_decode_flow_mod() ->
> > ofpacts_check_consistency() -> ofpacts_check() -> ofpact_check__(). We
> > don't have access to ofproto strcuture in ofp_print_flow_mod(). Please
> > suggest if there is any other possible way through which I can get the
> > first egress table id (stored in ofproto strcuture) in ofp-actions.c
> file.
>
> ofp_print_flow_mod() doesn't need to know the real egress_table_id, so
> you can use some default that will accept all flows.
>
> > I want to clarify one more doubt. In the 2nd patch, when a packet start
> > egress processing then output port is added to the action_set and we
> should
> > also set the actset_output variable. So can we directly add the output
> port
> > in action_set and actset_output or we should call xlate_write_action() to
> > add the output port in action_set and actset_output variable. Please
> > suggest which approach should be followed for this.
>
> I would add it directly.
>
Ben Pfaff Jan. 14, 2016, 5:36 p.m. UTC | #5
I don't think there's any need to check whether it's an egress table
along that path.  ovs-ofctl can try to insert the flow either way.

Regardless, a global variable is unacceptable.

On Thu, Jan 14, 2016 at 07:25:20PM +0530, niti Rohilla wrote:
> Hi Ben,
> 
> With reference to global variable "egress_table_id", I have a doubt. I can
> send '0' as the default value from ofp_print_flow_mod() to
> ofpact_check__(). Right now, ofpact_check__() access the first egress table
> id from global variable "egress_table_id", if I remove the global variable
> then egress table id has to sent be from the all the functions that
> internally call ofpact_check__(). For example,
> 
> ofctl_add_flow()->ofctl_flow_mod()->parse_ofp_flow_mod_str()->parse_ofp_str()->parse_ofp_str__()->ofpacts_check()->ofpact_check__()
> 
> In this hierarchy, first I have to get the first egress table from the
> switch using fetch_table_features() and egress table id has to be sent from
> ofctl_add_flow() and I need to change the definition of all the functions
> to include one more parameter to send the first egress table id to
> ofpact_check__().
> 
> Functions that modify the flows needs to first query the first egress table
> from the switch and send it to ofpact_check__ and functions that only parse
> flows for e.g ofctl_parse_flows() can send the default value i.e., but
> definition of all the functions included in the hierarchy has to be changed
> to include one more parameter. It requires changing the definition of many
> functions.
> 
> That is why I have used the global variable. Please suggest which approach
> should be followed.
> 
> Thanks
> Niti Rohilla
> 
> On Wed, Jan 13, 2016 at 10:46 PM, Ben Pfaff <blp@ovn.org> wrote:
> 
> > On Wed, Jan 13, 2016 at 03:18:02PM +0530, niti Rohilla wrote:
> > > I can incorporate the egress table implemenation in OF1.3 table features
> > > code and will remove the implementation added for OF1.5.
> >
> > Good.
> >
> > > I will replace the pad[5] field in of13_table_features structure with
> > > command and features field as specified in OF1.5.1 specifications. I can
> > > also remove the ofp15_table_feature_prop_type enums, OF1.5 protocol
> > > definition and code implementation added for the same. Kindly suggest is
> > > this approach is fine.
> >
> > OK.
> >
> > > I can replace the hard coded values for OFPAT_ with OFPACT_OUTPUT and
> > > OFPACT_GROUP.
> >
> > OK.
> >
> > > I have created a global variable "egress_table_id" that contain the table
> > > id of first egress table. This variable is declared as global because I
> > > need the first egress table id to add the checks in ofp-action.c file (in
> > > ofpact_check__()) to prevent adding the output and group action in action
> > > set of egress tables and to disallow the ingress flow tables to direct
> > the
> > > packet to egress flow tables via GOTO_TABLE instruction.
> > >
> > > We need access to the ofproto structure in ofpact_check__().
> > > ofpact_check__() is called from various places. One scenraio is,
> > > ofp_print_flow_mod() -> ofputil_decode_flow_mod() ->
> > > ofpacts_check_consistency() -> ofpacts_check() -> ofpact_check__(). We
> > > don't have access to ofproto strcuture in ofp_print_flow_mod(). Please
> > > suggest if there is any other possible way through which I can get the
> > > first egress table id (stored in ofproto strcuture) in ofp-actions.c
> > file.
> >
> > ofp_print_flow_mod() doesn't need to know the real egress_table_id, so
> > you can use some default that will accept all flows.
> >
> > > I want to clarify one more doubt. In the 2nd patch, when a packet start
> > > egress processing then output port is added to the action_set and we
> > should
> > > also set the actset_output variable. So can we directly add the output
> > port
> > > in action_set and actset_output or we should call xlate_write_action() to
> > > add the output port in action_set and actset_output variable. Please
> > > suggest which approach should be followed for this.
> >
> > I would add it directly.
> >

Patch
diff mbox

diff --git a/include/openflow/openflow-1.5.h b/include/openflow/openflow-1.5.h
index b3deb2d..20cc0b8 100644
--- a/include/openflow/openflow-1.5.h
+++ b/include/openflow/openflow-1.5.h
@@ -167,4 +167,172 @@  struct ofp15_group_desc_stats {
 };
 OFP_ASSERT(sizeof(struct ofp15_group_desc_stats) == 16);
 
+/* Table Features request commands */
+enum ofp15_table_features_command {
+    OFPTFC_REPLACE = 0,    /* Replace full pipeline. */
+    OFPTFC_MODIFY  = 1,    /* Modify flow tables capabilities. */
+    OFPTFC_ENABLE  = 2,    /* Enable flow tables in the pipeline. */
+    OFPTFC_DISABLE = 3,    /* Disable flow tables in pipeline. */
+};
+
+/* Flags of features supported by the table. */
+enum ofp15_table_feature_flag {
+    OFPTFF_INGRESS_TABLE = 1 << 0, /* Can be configured as ingress table. */
+    OFPTFF_EGRESS_TABLE  = 1 << 1, /* Can be configured as egress table. */
+    OFPTFF_FIRST_EGRESS  = 1 << 4, /* Is the first egress table. */
+};
+
+/* Common header for all Table Feature Properties */
+struct ofp15_table_feature_prop_header {
+    ovs_be16    type;   /* One of OFPTFPT_*. */
+    ovs_be16    length; /* Length in bytes of this property. */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_header) == 4);
+
+/* Body for ofp_multipart_request of type OFPMP_TABLE_FEATURES./
+ * Body of reply to OFPMP_TABLE_FEATURES request. */
+struct ofp15_table_features {
+    ovs_be16 length;          /* Length is padded to 64 bits. */
+    uint8_t table_id;         /* Identifier of table. Lower numbered tables
+                               * are consulted first. */
+    uint8_t command;          /* One of OFPTFC_*. */
+    ovs_be32 features;        /* Bitmap of OFPTFF_* values. */
+    char name[OFP_MAX_TABLE_NAME_LEN];
+    ovs_be64 metadata_match;  /* Bits of metadata table can match. */
+    ovs_be64 metadata_write;  /* Bits of metadata table can write. */
+
+    /* In OF1.3 this field was named 'config' and it was useless because OF1.3
+     * did not define any OFPTC_* bits.
+     *
+     * OF1.4 renamed this field to 'capabilities' and added OFPTC14_EVICTION
+     * and OFPTC14_VACANCY_EVENTS. */
+    ovs_be32 capabilities;    /* Bitmap of OFPTC_* values */
+
+    ovs_be32 max_entries;     /* Max number of entries supported. */
+
+    /* Table Feature Property list */
+    /* struct ofp15_table_feature_prop_header properties[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_features) == 64);
+
+/* Table Feature property types.
+ * Low order bit cleared indicates a property for a regular Flow Entry.
+ * Low order bit set indicates a property for the Table-Miss Flow Entry. */
+enum ofp15_table_feature_prop_type {
+    OFPTFPT15_INSTRUCTIONS         = 0, /* Instructions property. */
+    OFPTFPT15_INSTRUCTIONS_MISS    = 1, /* Instructions for table-miss. */
+    OFPTFPT15_NEXT_TABLES          = 2, /* Next Table property. */
+    OFPTFPT15_NEXT_TABLES_MISS     = 3, /* Next Table for table-miss. */
+    OFPTFPT15_WRITE_ACTIONS        = 4, /* Write Actions property. */
+    OFPTFPT15_WRITE_ACTIONS_MISS   = 5, /* Write Actions for table-miss. */
+    OFPTFPT15_APPLY_ACTIONS        = 6, /* Apply Actions property. */
+    OFPTFPT15_APPLY_ACTIONS_MISS   = 7, /* Apply Actions for table-miss. */
+    OFPTFPT15_MATCH                = 8, /* Match property. */
+    OFPTFPT15_WILDCARDS            = 10, /* Wildcards property. */
+    OFPTFPT15_WRITE_SETFIELD       = 12, /* Write Set-Field property. */
+    OFPTFPT15_WRITE_SETFIELD_MISS  = 13, /* Write Set-Field for table-miss. */
+    OFPTFPT15_APPLY_SETFIELD       = 14, /* Apply Set-Field property. */
+    OFPTFPT15_APPLY_SETFIELD_MISS  = 15, /* Apply Set-Field for table-miss. */
+    OFPTFPT15_TABLE_SYNC_FROM      = 16, /* Table synchronisation property. */
+    OFPTFPT15_WRITE_COPYFIELD      = 18, /* Write Copy-Field property. */
+    OFPTFPT15_WRITE_COPYFIELD_MISS = 19, /* Write Copy-Field for table-miss. */
+    OFPTFPT15_APPLY_COPYFIELD      = 20, /* Apply Copy-Field property. */
+    OFPTFPT15_APPLY_COPYFIELD_MISS = 21, /* Apply Copy-Field for table-miss. */
+    OFPTFPT15_PACKET_TYPES         = 22, /* Packet types property. */
+    OFPTFPT15_EXPERIMENTER         = 0xFFFE, /* Experimenter property. */
+    OFPTFPT15_EXPERIMENTER_MISS    = 0xFFFF, /* Experimenter for table-miss. */
+};
+
+/* Instructions property */
+struct ofp15_table_feature_prop_instructions {
+    ovs_be16    type;    /* One of OFPTFPT15_INSTRUCTIONS,
+                          * OFPTFPT15_INSTRUCTIONS_MISS. */
+    ovs_be16    length;  /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the instruction ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* struct ofp11_instruction instruction_ids[0];  List of instructions
+                                                     without any data */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_instructions) == 4);
+
+/* Next Tables property */
+struct ofp15_table_feature_prop_next_tables {
+    ovs_be16    type;   /* One of OFPTFPT15_NEXT_TABLES,
+                         * OFPTFPT15_NEXT_TABLES_MISS.
+                         * OFPTFPT15_TABLE_SYNC_FROM. */
+    ovs_be16    length; /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the table_ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* uint8_t     next_table_ids[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_next_tables) == 4);
+
+/* Actions property */
+struct ofp15_table_feature_prop_actions {
+    ovs_be16    type;   /* One of OFPTFPT15_WRITE_ACTIONS,
+                         * OFPTFPT15_WRITE_ACTIONS_MISS,
+                         * OFPTFPT15_APPLY_ACTIONS,
+                         * OFPTFPT15_APPLY_ACTIONS_MISS. */
+    ovs_be16    length; /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the action_ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* struct ofp_action_header action_ids[0];     List of actions
+                                                   without any data */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_actions) == 4);
+
+/* Match, Wildcard or Set-Field property */
+struct ofp15_table_feature_prop_oxm {
+    ovs_be16    type;   /* One of OFPTFPT15_MATCH, OFPTFPT15_WILDCARDS,
+                         * OFPTFPT15_WRITE_SETFIELD,
+                         * OFPTFPT15_WRITE_SETFIELD_MISS,
+                         * OFPTFPT15_APPLY_SETFIELD,
+                         * OFPTFPT15_APPLY_SETFIELD_MISS.
+                         * OFPTFPT15_WRITE_COPYFIELD,
+                         * OFPTFPT15_WRITE_COPYFIELD_MISS,
+                         * OFPTFPT15_APPLY_COPYFIELD,
+                         * OFPTFPT15_APPLY_COPYFIELD_MISS. */
+    ovs_be16    length; /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the oxm_ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* ovs_be32    oxm_ids[0];     Array of OXM headers */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_oxm) == 4);
+
+/* Packet types property */
+struct ofp15_table_feature_prop_oxm_values {
+    ovs_be16 type;    /* OFPTFPT15_PACKET_TYPES. */
+    ovs_be16 length;  /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the oxm values, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /*uint32_t oxm_values[0];     Array of OXM values */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_oxm_values) == 4);
+
+/* Experimenter table feature property */
+struct ofp15_table_feature_prop_experimenter {
+    ovs_be16    type;     /* One of OFPTFPT15_EXPERIMENTER,
+                           * OFPTFPT15_EXPERIMENTER_MISS. */
+    ovs_be16    length;   /* Length in bytes of this property. */
+    ovs_be32    experimenter; /* Experimenter ID which takes the same form
+                               * as in struct ofp_experimenter_header. */
+    ovs_be32    exp_type;     /* Experimenter defined. */
+    /* Followed by:
+     *   - Exactly (length - 12) bytes containing the experimenter data, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* ovs_be32    experimenter_data[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp15_table_feature_prop_experimenter) == 12);
+
 #endif /* openflow/openflow-1.5.h */
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index f900aa9..3137861 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -7828,3 +7828,26 @@  pad_ofpat(struct ofpbuf *openflow, size_t start_ofs)
     oah->len = htons(openflow->size - start_ofs);
 }
 
+/* If first egress table is set then flow tables used for egress
+ * processing must forbid the use of output action or group action in
+ * write-action instruction and must advertise the same in flow table
+ * features as mentioned in the specification.
+ *
+ * OFPAT_* values, i.e. 0 for OFPACT_OUTPUT and 22 for OFPACT_GROUP is
+ * used for comparison.
+ */
+
+void
+ofpact_put_ofp15_write_actions(uint64_t *ofpacts_bitmap,
+                               enum ofp_version version)
+{
+    const struct ofpact_map *x;
+
+    for (x = get_ofpact_map(version); x->ofpat >= 0; x++) {
+        if (x->ofpat == 0 || x->ofpat == 22) {
+            continue;
+        } else {
+            *ofpacts_bitmap |= (UINT64_C(1) << x->ofpact);
+        }
+    }
+}
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 18c7395..3f0fde4 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -1021,5 +1021,7 @@  enum ofperr ovs_instruction_type_from_inst_type(
 ovs_be32 ovsinst_bitmap_to_openflow(uint32_t ovsinst_bitmap, enum ofp_version);
 uint32_t ovsinst_bitmap_from_openflow(ovs_be32 ofpit_bitmap,
                                       enum ofp_version);
-
+void
+ofpact_put_ofp15_write_actions(uint64_t *ofpacts_bitmap,
+                               enum ofp_version version);
 #endif /* ofp-actions.h */
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
index 6770fa4..ea1195d 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -372,12 +372,18 @@  enum ofpraw {
     /* OFPST 1.3+ (11): struct ofp13_meter_features. */
     OFPRAW_OFPST13_METER_FEATURES_REPLY,
 
-    /* OFPST 1.3+ (12): void. */
+    /* OFPST 1.3-1.4 (12): void. */
     OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
 
-    /* OFPST 1.3+ (12): struct ofp13_table_features, uint8_t[8][]. */
+    /* OFPST 1.5+ (12): struct ofp15_table_features[]. */
+    OFPRAW_OFPST15_TABLE_FEATURES_REQUEST,
+
+    /* OFPST 1.3-1.4 (12): struct ofp13_table_features, uint8_t[8][]. */
     OFPRAW_OFPST13_TABLE_FEATURES_REPLY,
 
+    /* OFPST 1.5+ (12): struct ofp15_table_features, uint8_t[8][]. */
+    OFPRAW_OFPST15_TABLE_FEATURES_REPLY,
+
     /* OFPST 1.4+ (14): void. */
     OFPRAW_OFPST14_TABLE_DESC_REQUEST,
 
@@ -628,9 +634,11 @@  enum ofptype {
 
     OFPTYPE_METER_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_METER_FEATURES_REPLY. */
 
-    OFPTYPE_TABLE_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST. */
+    OFPTYPE_TABLE_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST.
+                                           * OFPRAW_OFPST15_TABLE_FEATURES_REQUEST. */
 
-    OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */
+    OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY.
+                                         * OFPRAW_OFPST15_TABLE_FEATURES_REPLY. */
 
     OFPTYPE_TABLE_DESC_REQUEST,      /* OFPRAW_OFPST14_TABLE_DESC_REQUEST. */
 
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index fdc30d9..4e91851 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -1695,3 +1695,27 @@  parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *ttm,
 
     return NULL;
 }
+
+/* Convert 'table_id' and table feature flag 'OFPTFF_FIRST_EGRESS' into 'tf'
+ * for sending a set_table_features command to a switch.
+ *
+ * Stores a bitmap of the OpenFlow versions that are usable for 'tf' into
+ * '*usable_versions'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * OVS_WARN_UNUSED_RESULT
+parse_ofp_table_features(struct ofputil_table_features *tf, const char *table_id,
+                         uint32_t *usable_versions)
+{
+    char *error = str_to_u8(table_id, "table_id", &tf->table_id);
+    if (error) {
+        return error;
+    }
+
+    *usable_versions = (1u << OFP15_VERSION);
+
+    tf->features = OFPTFF_FIRST_EGRESS;
+
+    return NULL;
+}
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index a1c147b..fba2efd 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -36,6 +36,7 @@  struct ofputil_meter_mod;
 struct ofputil_table_mod;
 struct ofputil_tlv_table_mod;
 struct simap;
+struct ofputil_table_features;
 enum ofputil_protocol;
 
 char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
@@ -52,6 +53,11 @@  char *parse_ofp_table_mod(struct ofputil_table_mod *,
                           uint32_t *usable_versions)
     OVS_WARN_UNUSED_RESULT;
 
+char *parse_ofp_table_features(struct ofputil_table_features *tf,
+                               const char *table_id,
+                               uint32_t *usable_versions)
+    OVS_WARN_UNUSED_RESULT;
+
 char *parse_ofp_flow_mod_file(const char *file_name, int command,
                               struct ofputil_flow_mod **fms, size_t *n_fms,
                               enum ofputil_protocol *usable_protocols)
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 930b01a..2939993 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -2851,6 +2851,12 @@  ofp_print_table_features(struct ds *s,
 
     }
 
+    if(features->features >= 0) {
+        ds_put_format(s, "    features: %s\n",
+                     (features->features & OFPTFF_FIRST_EGRESS) ?
+                      "first egress table" : "none");
+    }
+
     if (features->max_entries) {
         ds_put_format(s, "    max_entries=%"PRIu32"\n", features->max_entries);
     }
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 126b555..8d08902 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -4507,6 +4507,7 @@  ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
 
     return b;
 }
+
 
 /* Table features. */
 
@@ -4628,25 +4629,10 @@  parse_oxms(struct ofpbuf *payload, bool loose,
     return 0;
 }
 
-/* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract
- * ofputil_table_features in 'tf'.
- *
- * If 'loose' is true, this function ignores properties and values that it does
- * not understand, as a controller would want to do when interpreting
- * capabilities provided by a switch.  If 'loose' is false, this function
- * treats unknown properties and values as an error, as a switch would want to
- * do when interpreting a configuration request made by a controller.
- *
- * A single OpenFlow message can specify features for multiple tables.  Calling
- * this function multiple times for a single 'msg' iterates through the tables
- * in the message.  The caller must initially leave 'msg''s layer pointers null
- * and not modify them between calls.
- *
- * Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise
- * a positive "enum ofperr" value. */
-int
-ofputil_decode_table_features(struct ofpbuf *msg,
-                              struct ofputil_table_features *tf, bool loose)
+static int
+ofputil_decode_ofpst13_table_features(struct ofpbuf *msg,
+                                      struct ofputil_table_features *tf,
+                                      bool loose)
 {
     const struct ofp_header *oh;
     struct ofp13_table_features *otf;
@@ -4654,10 +4640,6 @@  ofputil_decode_table_features(struct ofpbuf *msg,
     unsigned int len;
 
     memset(tf, 0, sizeof *tf);
-
-    if (!msg->header) {
-        ofpraw_pull_assert(msg);
-    }
     oh = msg->header;
 
     if (!msg->size) {
@@ -4694,6 +4676,7 @@  ofputil_decode_table_features(struct ofpbuf *msg,
         tf->supports_vacancy_events = -1;
     }
     tf->max_entries = ntohl(otf->max_entries);
+    tf->features = -1;
 
     while (properties.size > 0) {
         struct ofpbuf payload;
@@ -4795,10 +4778,241 @@  ofputil_decode_table_features(struct ofpbuf *msg,
     return 0;
 }
 
+static int
+ofputil_decode_ofpst15_table_features(struct ofpbuf *msg,
+                                      struct ofputil_table_features *tf,
+                                      bool loose, enum ofpraw raw)
+{
+    const struct ofp_header *oh;
+    struct ofp15_table_features *otf;
+    struct ofpbuf properties;
+    unsigned int len;
+    uint32_t features;
+    uint32_t caps;
+
+    memset(tf, 0, sizeof *tf);
+    oh = msg->header;
+
+    if (!msg->size) {
+        return EOF;
+    }
+
+    if (msg->size < sizeof *otf) {
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+
+    otf = msg->data;
+    len = ntohs(otf->length);
+    if (len < sizeof *otf || len % 8 || len > msg->size) {
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+    ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
+    ofpbuf_pull(&properties, sizeof *otf);
+
+    tf->table_id = otf->table_id;
+    if (tf->table_id == OFPTT_ALL) {
+        return OFPERR_OFPTFFC_BAD_TABLE;
+    }
+
+    if (raw == OFPRAW_OFPST15_TABLE_FEATURES_REQUEST) {
+
+        /* Return an error if TABLE_FEATURES_REQUEST attempts to set table 0
+         * as first egress table. */
+        if (tf->table_id == 0) {
+            return OFPERR_OFPTFFC_BAD_TABLE;
+        }
+
+        /* Return an error if TABLE_FEATURES_REQUEST contain properties. */
+        if (properties.size > 0) {
+            return OFPERR_OFPBPC_BAD_LEN;
+        }
+    }
+
+    ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
+    tf->metadata_match = otf->metadata_match;
+    tf->metadata_write = otf->metadata_write;
+    tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
+
+    caps = ntohl(otf->capabilities);
+    tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0;
+    tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0;
+
+    tf->max_entries = ntohl(otf->max_entries);
+
+    /* Return an error if any flag other than OFPTFF_FIRST_EGRESS is set. */
+    features = ntohl(otf->features);
+    if ((features & OFPTFF_FIRST_EGRESS) != 0) {
+        tf->features = OFPTFF_FIRST_EGRESS;
+    } else if ((features & OFPTFF_INGRESS_TABLE) != 0 ||
+               (features & OFPTFF_EGRESS_TABLE) != 0 ) {
+        return OFPERR_OFPBFC_BAD_FLAGS;
+    }
+
+    while (properties.size > 0) {
+        struct ofpbuf payload;
+        enum ofperr error;
+        uint16_t type;
+
+        error = pull_table_feature_property(&properties, &payload, &type);
+        if (error) {
+            return error;
+        }
+
+        switch ((enum ofp15_table_feature_prop_type) type) {
+        case OFPTFPT15_INSTRUCTIONS:
+            error = parse_instruction_ids(&payload, loose,
+                                          &tf->nonmiss.instructions);
+            break;
+
+        case OFPTFPT15_INSTRUCTIONS_MISS:
+            error = parse_instruction_ids(&payload, loose,
+                                          &tf->miss.instructions);
+            break;
+
+        case OFPTFPT15_NEXT_TABLES:
+            error = parse_table_features_next_table(&payload,
+                                                    tf->nonmiss.next);
+            break;
+
+        case OFPTFPT15_NEXT_TABLES_MISS:
+            error = parse_table_features_next_table(&payload, tf->miss.next);
+            break;
+
+        case OFPTFPT15_WRITE_ACTIONS:
+            error = parse_action_bitmap(&payload, oh->version,
+                                        &tf->nonmiss.write.ofpacts);
+            break;
+
+        case OFPTFPT15_WRITE_ACTIONS_MISS:
+            error = parse_action_bitmap(&payload, oh->version,
+                                        &tf->miss.write.ofpacts);
+            break;
+
+        case OFPTFPT15_APPLY_ACTIONS:
+            error = parse_action_bitmap(&payload, oh->version,
+                                        &tf->nonmiss.apply.ofpacts);
+            break;
+
+        case OFPTFPT15_APPLY_ACTIONS_MISS:
+            error = parse_action_bitmap(&payload, oh->version,
+                                        &tf->miss.apply.ofpacts);
+            break;
+
+        case OFPTFPT15_MATCH:
+            error = parse_oxms(&payload, loose, &tf->match, &tf->mask);
+            break;
+
+        case OFPTFPT15_WILDCARDS:
+            error = parse_oxms(&payload, loose, &tf->wildcard, NULL);
+            break;
+
+        case OFPTFPT15_WRITE_SETFIELD:
+            error = parse_oxms(&payload, loose,
+                               &tf->nonmiss.write.set_fields, NULL);
+            break;
+
+        case OFPTFPT15_WRITE_SETFIELD_MISS:
+            error = parse_oxms(&payload, loose,
+                               &tf->miss.write.set_fields, NULL);
+            break;
+
+        case OFPTFPT15_APPLY_SETFIELD:
+            error = parse_oxms(&payload, loose,
+                               &tf->nonmiss.apply.set_fields, NULL);
+            break;
+
+        case OFPTFPT15_APPLY_SETFIELD_MISS:
+            error = parse_oxms(&payload, loose,
+                               &tf->miss.apply.set_fields, NULL);
+            break;
+
+        case OFPTFPT15_TABLE_SYNC_FROM:
+        case OFPTFPT15_WRITE_COPYFIELD:
+        case OFPTFPT15_WRITE_COPYFIELD_MISS:
+        case OFPTFPT15_APPLY_COPYFIELD:
+        case OFPTFPT15_APPLY_COPYFIELD_MISS:
+        case OFPTFPT15_PACKET_TYPES:
+            log_property(loose, "unsupported table features property %"PRIu16,
+                         type);
+            break;
+
+        case OFPTFPT15_EXPERIMENTER:
+        case OFPTFPT15_EXPERIMENTER_MISS:
+        default:
+            log_property(loose, "unknown table features property %"PRIu16,
+                         type);
+            error = loose ? 0 : OFPERR_OFPBPC_BAD_TYPE;
+            break;
+        }
+        if (error) {
+            return error;
+        }
+    }
+
+    /* Fix inconsistencies:
+     *
+     *     - Turn on 'match' bits that are set in 'mask', because maskable
+     *       fields are matchable.
+     *
+     *     - Turn on 'wildcard' bits that are set in 'mask', because a field
+     *       that is arbitrarily maskable can be wildcarded entirely.
+     *
+     *     - Turn off 'wildcard' bits that are not in 'match', because a field
+     *       must be matchable for it to be meaningfully wildcarded. */
+    bitmap_or(tf->match.bm, tf->mask.bm, MFF_N_IDS);
+    bitmap_or(tf->wildcard.bm, tf->mask.bm, MFF_N_IDS);
+    bitmap_and(tf->wildcard.bm, tf->match.bm, MFF_N_IDS);
+
+    return 0;
+}
+
+/* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract
+ * ofputil_table_features in 'tf'.
+ *
+ * If 'loose' is true, this function ignores properties and values that it does
+ * not understand, as a controller would want to do when interpreting
+ * capabilities provided by a switch.  If 'loose' is false, this function
+ * treats unknown properties and values as an error, as a switch would want to
+ * do when interpreting a configuration request made by a controller.
+ *
+ * A single OpenFlow message can specify features for multiple tables.  Calling
+ * this function multiple times for a single 'msg' iterates through the tables
+ * in the message.  The caller must initially leave 'msg''s layer pointers null
+ * and not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise
+ * a positive "enum ofperr" value. */
+int
+ofputil_decode_table_features(struct ofpbuf *msg,
+                              struct ofputil_table_features *tf, bool loose)
+{
+    enum ofpraw raw;
+    enum ofperr error;
+    error = (msg->header ? ofpraw_decode(&raw, msg->header)
+             : ofpraw_pull(&raw, msg));
+    if (error) {
+        return error;
+    }
+
+    switch ((int)raw) {
+    case OFPRAW_OFPST13_TABLE_FEATURES_REQUEST:
+    case OFPRAW_OFPST13_TABLE_FEATURES_REPLY:
+        return ofputil_decode_ofpst13_table_features(msg, tf, loose);
+
+    case OFPRAW_OFPST15_TABLE_FEATURES_REQUEST:
+    case OFPRAW_OFPST15_TABLE_FEATURES_REPLY:
+        return ofputil_decode_ofpst15_table_features(msg, tf, loose, raw);
+
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
 /* Encodes and returns a request to obtain the table features of a switch.
  * The message is encoded for OpenFlow version 'ofp_version'. */
 struct ofpbuf *
-ofputil_encode_table_features_request(enum ofp_version ofp_version)
+ofputil_encode_table_features_request(const struct ofputil_table_features *tf,
+                                      enum ofp_version ofp_version)
 {
     struct ofpbuf *request = NULL;
 
@@ -4810,10 +5024,22 @@  ofputil_encode_table_features_request(enum ofp_version ofp_version)
                      "(\'-O OpenFlow13\')");
     case OFP13_VERSION:
     case OFP14_VERSION:
-    case OFP15_VERSION:
         request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
                                ofp_version, 0);
         break;
+    case OFP15_VERSION: {
+	struct ofp15_table_features *otf;
+
+        request = ofpraw_alloc(OFPRAW_OFPST15_TABLE_FEATURES_REQUEST,
+                               ofp_version, 0);
+        if (tf != NULL && (tf->features & OFPTFF_FIRST_EGRESS)) {
+            otf = ofpbuf_put_zeros(request, sizeof *otf);
+            otf->table_id = tf->table_id;
+            otf->features = htonl(tf->features);
+            otf->length = htons(sizeof *otf);
+        }
+    }
+    break;
     default:
         OVS_NOT_REACHED();
     }
@@ -4822,11 +5048,29 @@  ofputil_encode_table_features_request(enum ofp_version ofp_version)
 }
 
 static void
-put_fields_property(struct ofpbuf *reply,
-                    const struct mf_bitmap *fields,
-                    const struct mf_bitmap *masks,
-                    enum ofp13_table_feature_prop_type property,
-                    enum ofp_version version)
+put_ofpst13_fields_property(struct ofpbuf *reply,
+                            const struct mf_bitmap *fields,
+                            const struct mf_bitmap *masks,
+                            enum ofp13_table_feature_prop_type property,
+                            enum ofp_version version)
+{
+    size_t start_ofs;
+    int field;
+
+    start_ofs = start_property(reply, property);
+    BITMAP_FOR_EACH_1 (field, MFF_N_IDS, fields->bm) {
+        nx_put_header(reply, field, version,
+                      masks && bitmap_is_set(masks->bm, field));
+    }
+    end_property(reply, start_ofs);
+}
+
+static void
+put_ofpst15_fields_property(struct ofpbuf *reply,
+                            const struct mf_bitmap *fields,
+                            const struct mf_bitmap *masks,
+                            enum ofp15_table_feature_prop_type property,
+                            enum ofp_version version)
 {
     size_t start_ofs;
     int field;
@@ -4840,11 +5084,30 @@  put_fields_property(struct ofpbuf *reply,
 }
 
 static void
-put_table_action_features(struct ofpbuf *reply,
-                          const struct ofputil_table_action_features *taf,
-                          enum ofp13_table_feature_prop_type actions_type,
-                          enum ofp13_table_feature_prop_type set_fields_type,
-                          int miss_offset, enum ofp_version version)
+put_ofpst13_table_action_features(
+    struct ofpbuf *reply, const struct ofputil_table_action_features *taf,
+    enum ofp13_table_feature_prop_type actions_type,
+    enum ofp13_table_feature_prop_type set_fields_type,
+    int miss_offset, enum ofp_version version)
+{
+    size_t start_ofs;
+
+    start_ofs = start_property(reply, actions_type + miss_offset);
+    put_bitmap_properties(reply,
+                          ntohl(ofpact_bitmap_to_openflow(taf->ofpacts,
+                                                          version)));
+    end_property(reply, start_ofs);
+
+    put_ofpst13_fields_property(reply, &taf->set_fields, NULL,
+                                set_fields_type + miss_offset, version);
+}
+
+static void
+put_ofpst15_table_action_features(
+    struct ofpbuf *reply, const struct ofputil_table_action_features *taf,
+    enum ofp15_table_feature_prop_type actions_type,
+    enum ofp15_table_feature_prop_type set_fields_type,
+    int miss_offset, enum ofp_version version)
 {
     size_t start_ofs;
 
@@ -4854,12 +5117,12 @@  put_table_action_features(struct ofpbuf *reply,
                                                           version)));
     end_property(reply, start_ofs);
 
-    put_fields_property(reply, &taf->set_fields, NULL,
-                        set_fields_type + miss_offset, version);
+    put_ofpst15_fields_property(reply, &taf->set_fields, NULL,
+                                set_fields_type + miss_offset, version);
 }
 
 static void
-put_table_instruction_features(
+put_ofpst13_table_instruction_features(
     struct ofpbuf *reply, const struct ofputil_table_instruction_features *tif,
     int miss_offset, enum ofp_version version)
 {
@@ -4878,17 +5141,49 @@  put_table_instruction_features(
     }
     end_property(reply, start_ofs);
 
-    put_table_action_features(reply, &tif->write,
-                              OFPTFPT13_WRITE_ACTIONS,
-                              OFPTFPT13_WRITE_SETFIELD, miss_offset, version);
-    put_table_action_features(reply, &tif->apply,
-                              OFPTFPT13_APPLY_ACTIONS,
-                              OFPTFPT13_APPLY_SETFIELD, miss_offset, version);
+    put_ofpst13_table_action_features(reply, &tif->write,
+                                      OFPTFPT13_WRITE_ACTIONS,
+                                      OFPTFPT13_WRITE_SETFIELD,
+                                      miss_offset, version);
+    put_ofpst13_table_action_features(reply, &tif->apply,
+                                      OFPTFPT13_APPLY_ACTIONS,
+                                      OFPTFPT13_APPLY_SETFIELD,
+                                      miss_offset, version);
 }
 
-void
-ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
-                                    struct ovs_list *replies)
+static void
+put_ofpst15_table_instruction_features(
+    struct ofpbuf *reply, const struct ofputil_table_instruction_features *tif,
+    int miss_offset, enum ofp_version version)
+{
+    size_t start_ofs;
+    uint8_t table_id;
+
+    start_ofs = start_property(reply, OFPTFPT15_INSTRUCTIONS + miss_offset);
+    put_bitmap_properties(reply,
+                          ntohl(ovsinst_bitmap_to_openflow(tif->instructions,
+                                                           version)));
+    end_property(reply, start_ofs);
+
+    start_ofs = start_property(reply, OFPTFPT15_NEXT_TABLES + miss_offset);
+    BITMAP_FOR_EACH_1 (table_id, 255, tif->next) {
+        ofpbuf_put(reply, &table_id, 1);
+    }
+    end_property(reply, start_ofs);
+
+    put_ofpst15_table_action_features(reply, &tif->write,
+                                      OFPTFPT15_WRITE_ACTIONS,
+                                      OFPTFPT15_WRITE_SETFIELD,
+                                      miss_offset, version);
+    put_ofpst15_table_action_features(reply, &tif->apply,
+                                      OFPTFPT15_APPLY_ACTIONS,
+                                      OFPTFPT15_APPLY_SETFIELD,
+                                      miss_offset, version);
+}
+
+static void
+ofputil_ofpst13_table_features_reply(const struct ofputil_table_features *tf,
+                                     struct ovs_list *replies)
 {
     struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
     enum ofp_version version = ofpmp_version(replies);
@@ -4910,19 +5205,70 @@  ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
     }
     otf->max_entries = htonl(tf->max_entries);
 
-    put_table_instruction_features(reply, &tf->nonmiss, 0, version);
-    put_table_instruction_features(reply, &tf->miss, 1, version);
+    put_ofpst13_table_instruction_features(reply, &tf->nonmiss, 0, version);
+    put_ofpst13_table_instruction_features(reply, &tf->miss, 1, version);
+
+    put_ofpst13_fields_property(reply, &tf->match, &tf->mask,
+                                OFPTFPT13_MATCH, version);
+    put_ofpst13_fields_property(reply, &tf->wildcard, NULL,
+                                OFPTFPT13_WILDCARDS, version);
+
+    otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf);
+    otf->length = htons(reply->size - start_ofs);
+    ofpmp_postappend(replies, start_ofs);
+}
+
+static void
+ofputil_ofpst15_table_features_reply(const struct ofputil_table_features *tf,
+                                     struct ovs_list *replies)
+{
+    struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+    enum ofp_version version = ofpmp_version(replies);
+    size_t start_ofs = reply->size;
+    struct ofp15_table_features *otf;
+
+    otf = ofpbuf_put_zeros(reply, sizeof *otf);
+    otf->table_id = tf->table_id;
+    ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
+    otf->metadata_match = tf->metadata_match;
+    otf->metadata_write = tf->metadata_write;
+    if (tf->supports_eviction) {
+        otf->capabilities |= htonl(OFPTC14_EVICTION);
+    }
+    if (tf->supports_vacancy_events) {
+        otf->capabilities |= htonl(OFPTC14_VACANCY_EVENTS);
+    }
+    otf->max_entries = htonl(tf->max_entries);
+
+    if ((tf->features & OFPTFF_FIRST_EGRESS) != 0) {
+        otf->features = htonl(OFPTFF_FIRST_EGRESS);
+    }
+    put_ofpst15_table_instruction_features(reply, &tf->nonmiss, 0, version);
+    put_ofpst15_table_instruction_features(reply, &tf->miss, 1, version);
 
-    put_fields_property(reply, &tf->match, &tf->mask,
-                        OFPTFPT13_MATCH, version);
-    put_fields_property(reply, &tf->wildcard, NULL,
-                        OFPTFPT13_WILDCARDS, version);
+    put_ofpst15_fields_property(reply, &tf->match, &tf->mask,
+                                OFPTFPT15_MATCH, version);
+    put_ofpst15_fields_property(reply, &tf->wildcard, NULL,
+                                OFPTFPT15_WILDCARDS, version);
 
     otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf);
     otf->length = htons(reply->size - start_ofs);
     ofpmp_postappend(replies, start_ofs);
 }
 
+void
+ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
+                                    struct ovs_list *replies)
+{
+    enum ofp_version version = ofpmp_version(replies);
+
+    if (version < OFP15_VERSION) {
+        ofputil_ofpst13_table_features_reply(tf, replies);
+    } else {
+        ofputil_ofpst15_table_features_reply(tf, replies);
+    }
+}
+
 static enum ofperr
 parse_table_desc_eviction_property(struct ofpbuf *property,
                                    struct ofputil_table_desc *td)
@@ -6052,6 +6398,7 @@  ofputil_decode_table_stats_reply(struct ofpbuf *msg,
     memset(features, 0, sizeof *features);
     features->supports_eviction = -1;
     features->supports_vacancy_events = -1;
+    features->features = -1;
 
     switch ((enum ofp_version) oh->version) {
     case OFP10_VERSION:
@@ -9781,3 +10128,10 @@  ofputil_encode_get_async_config(const struct ofp_header *oh,
 
     return buf;
 }
+
+uint8_t egress_table_id;
+
+void set_egress_table_id(uint8_t table_id)
+{
+    egress_table_id = table_id;
+}
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index c0541d4..b16b1fb 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -745,6 +745,15 @@  struct ofputil_table_features {
     int supports_eviction;               /* OF1.4+ only. */
     int supports_vacancy_events;         /* OF1.4+ only. */
 
+    /* The features field is a bitmap of OFPTFF_* values that defines how the
+     * flow table can be used and what are its basic features.
+     *
+     * 'features' is relevant only for Openflow 1.5 and later only. For 1.5,
+     * it will be OFPTFF_FIRST_EGRESS if first egress table is set, otherwise
+     * 0. For other versions, they are decoded as -1 and ignored for encoding.
+     */
+    int features;                  /* OF1.5+ only. */
+
     /* Table features related to instructions.  There are two instances:
      *
      *   - 'miss' reports features available in the table miss flow.
@@ -801,7 +810,9 @@  int ofputil_decode_table_desc(struct ofpbuf *,
                               struct ofputil_table_desc *,
                               enum ofp_version);
 
-struct ofpbuf *ofputil_encode_table_features_request(enum ofp_version);
+struct ofpbuf *
+ofputil_encode_table_features_request(const struct ofputil_table_features *,
+                                      enum ofp_version);
 
 struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version);
 
@@ -1323,4 +1334,7 @@  enum ofperr ofputil_decode_requestforward(const struct ofp_header *,
                                           struct ofputil_requestforward *);
 void ofputil_destroy_requestforward(struct ofputil_requestforward *);
 
+extern uint8_t egress_table_id;
+void set_egress_table_id(uint8_t table_id);
+
 #endif /* ofp-util.h */
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 2a6bd91..af5268b 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -95,6 +95,7 @@  struct ofproto {
     int n_tables;
     cls_version_t tables_version;  /* Controls which rules are visible to
                                     * table lookups. */
+    uint8_t first_egress_table_id; /* Contains first egress table id. */
 
     /* Rules indexed on their cookie values, in all flow tables. */
     struct hindex cookies OVS_GUARDED_BY(ofproto_mutex);
@@ -267,6 +268,11 @@  struct oftable {
 
     atomic_ulong n_matched;
     atomic_ulong n_missed;
+
+   /* This flag indicates that this flow table is the first egress table, when
+    * a packet is output to a port, egress processing will start with this flow
+    * table. */
+    bool is_first_egress;
 };
 
 /* Assigns TABLE to each oftable, in turn, in OFPROTO.
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index a5ce04e..99ffe2e 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3123,7 +3123,8 @@  handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
 static void
 query_tables(struct ofproto *ofproto,
              struct ofputil_table_features **featuresp,
-             struct ofputil_table_stats **statsp)
+             struct ofputil_table_stats **statsp,
+             enum ofp_version version)
 {
     struct mf_bitmap rw_fields = oxm_writable_fields();
     struct mf_bitmap match = oxm_matchable_fields();
@@ -3155,14 +3156,23 @@  query_tables(struct ofproto *ofproto,
         if (!more_tables) {
             f->nonmiss.instructions &= ~(1u << OVSINST_OFPIT11_GOTO_TABLE);
         }
-        f->nonmiss.write.ofpacts = (UINT64_C(1) << N_OFPACTS) - 1;
+        if (version >= OFP15_VERSION && egress_table_id &&
+            f->table_id >= egress_table_id) {
+            ofpact_put_ofp15_write_actions(&f->nonmiss.write.ofpacts, version);
+        } else {
+            f->nonmiss.write.ofpacts = (UINT64_C(1) << N_OFPACTS) - 1;
+        }
         f->nonmiss.write.set_fields = rw_fields;
-        f->nonmiss.apply = f->nonmiss.write;
+        f->nonmiss.apply.ofpacts = (UINT64_C(1) << N_OFPACTS) - 1;
+        f->nonmiss.apply.set_fields = f->nonmiss.write.set_fields;
         f->miss = f->nonmiss;
 
         f->match = match;
         f->mask = mask;
         f->wildcard = match;
+        if (ofproto->tables[i].is_first_egress) {
+            f->features = OFPTFF_FIRST_EGRESS;
+        }
     }
 
     if (statsp) {
@@ -3199,14 +3209,15 @@  query_tables(struct ofproto *ofproto,
 
 static void
 query_switch_features(struct ofproto *ofproto,
-                      bool *arp_match_ip, uint64_t *ofpacts)
+                      bool *arp_match_ip, uint64_t *ofpacts,
+                      enum ofp_version version)
 {
     struct ofputil_table_features *features, *f;
 
     *arp_match_ip = false;
     *ofpacts = 0;
 
-    query_tables(ofproto, &features, NULL);
+    query_tables(ofproto, &features, NULL, version);
     for (f = features; f < &features[ofproto->n_tables]; f++) {
         *ofpacts |= f->nonmiss.apply.ofpacts | f->miss.apply.ofpacts;
         if (bitmap_is_set(f->match.bm, MFF_ARP_SPA) ||
@@ -3229,7 +3240,7 @@  handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
     bool arp_match_ip;
     struct ofpbuf *b;
 
-    query_switch_features(ofproto, &arp_match_ip, &features.ofpacts);
+    query_switch_features(ofproto, &arp_match_ip, &features.ofpacts, oh->version);
 
     features.datapath_id = ofproto->datapath_id;
     features.n_buffers = pktbuf_capacity();
@@ -3541,7 +3552,7 @@  handle_table_stats_request(struct ofconn *ofconn,
     struct ofpbuf *reply;
     size_t i;
 
-    query_tables(ofproto, &features, &stats);
+    query_tables(ofproto, &features, &stats, request->version);
 
     reply = ofputil_encode_table_stats_reply(request);
     for (i = 0; i < ofproto->n_tables; i++) {
@@ -3565,15 +3576,41 @@  handle_table_features_request(struct ofconn *ofconn,
     struct ofputil_table_features *features;
     struct ovs_list replies;
     struct ofpbuf msg;
+    enum ofperr error = 0;
     size_t i;
 
     ofpbuf_use_const(&msg, request, ntohs(request->length));
     ofpraw_pull_assert(&msg);
-    if (msg.size || ofpmp_more(request)) {
-        return OFPERR_OFPTFFC_EPERM;
+
+    if (request->version < OFP15_VERSION) {
+        if (msg.size || ofpmp_more(request)) {
+            return OFPERR_OFPTFFC_EPERM;
+        }
+    } else {
+        if (msg.size) {
+            struct ofputil_table_features tf;
+            error = ofputil_decode_table_features(&msg, &tf, false);
+            if (error) {
+                return error;
+            }
+            if ((tf.features & OFPTFF_FIRST_EGRESS) != 0) {
+                for (i = 0; i < ofproto->n_tables; i++) {
+                    if (ofproto->tables[i].is_first_egress) {
+                        ofproto->tables[i].is_first_egress = false;
+                        break;
+                    }
+                }
+                ovs_mutex_lock(&ofproto_mutex);
+                ofproto->tables[tf.table_id].is_first_egress = true;
+                ofproto->first_egress_table_id = tf.table_id;
+                set_egress_table_id(tf.table_id);
+                ovs_mutex_unlock(&ofproto_mutex);
+            }
+            return 0;
+        }
     }
 
-    query_tables(ofproto, &features, NULL);
+    query_tables(ofproto, &features, NULL, request->version);
 
     ofpmp_init(&replies, request);
     for (i = 0; i < ofproto->n_tables; i++) {
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index ed9ffdb..c816d7f 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -2475,6 +2475,228 @@  f5 f6 f7 f8 f9 fa fb fc fd 00 00 00 00 00 00 00 \
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_TABLE_FEATURES request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 13 09 40 00 00 00 d5 00 0c 00 01 00 00 00 00 \
+09 30 00 00 00 00 00 00 74 61 62 6c 65 30 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff \
+ff ff ff ff ff ff ff ff 00 00 00 03 00 0f 42 40 \
+00 00 00 2c 00 01 00 08 00 00 00 00 00 02 00 08 \
+00 00 00 00 00 03 00 08 00 00 00 00 00 04 00 08 \
+00 00 00 00 00 05 00 08 00 00 00 00 00 00 00 00 \
+00 01 00 2c 00 01 00 08 00 00 00 00 00 02 00 08 \
+00 00 00 00 00 03 00 08 00 00 00 00 00 04 00 08 \
+00 00 00 00 00 05 00 08 00 00 00 00 00 00 00 00 \
+00 02 01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c \
+0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c \
+1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c \
+2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c \
+3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c \
+4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c \
+5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c \
+6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c \
+7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c \
+8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c \
+9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac \
+ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc \
+bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc \
+cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc \
+dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec \
+ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc \
+fd 00 00 00 00 00 00 00 00 03 01 01 01 02 03 04 \
+05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 \
+15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 \
+25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 \
+35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 \
+45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 \
+55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 \
+65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 \
+75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 \
+85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 \
+95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 \
+a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 \
+b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 \
+c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 \
+d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 \
+e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 \
+f5 f6 f7 f8 f9 fa fb fc fd 00 00 00 00 00 00 00 \
+00 04 00 84 00 00 00 08 00 00 00 00 00 0b 00 08 \
+00 00 00 00 00 0c 00 08 00 00 00 00 00 0f 00 08 \
+00 00 00 00 00 10 00 08 00 00 00 00 00 11 00 08 \
+00 00 00 00 00 12 00 08 00 00 00 00 00 13 00 08 \
+00 00 00 00 00 14 00 08 00 00 00 00 00 15 00 08 \
+00 00 00 00 00 16 00 08 00 00 00 00 00 17 00 08 \
+00 00 00 00 00 18 00 08 00 00 00 00 00 19 00 08 \
+00 00 00 00 00 1a 00 08 00 00 00 00 00 1b 00 08 \
+00 00 00 00 00 00 00 00 00 05 00 84 00 00 00 08 \
+00 00 00 00 00 0b 00 08 00 00 00 00 00 0c 00 08 \
+00 00 00 00 00 0f 00 08 00 00 00 00 00 10 00 08 \
+00 00 00 00 00 11 00 08 00 00 00 00 00 12 00 08 \
+00 00 00 00 00 13 00 08 00 00 00 00 00 14 00 08 \
+00 00 00 00 00 15 00 08 00 00 00 00 00 16 00 08 \
+00 00 00 00 00 17 00 08 00 00 00 00 00 18 00 08 \
+00 00 00 00 00 19 00 08 00 00 00 00 00 1a 00 08 \
+00 00 00 00 00 1b 00 08 00 00 00 00 00 00 00 00 \
+00 06 00 84 00 00 00 08 00 00 00 00 00 0b 00 08 \
+00 00 00 00 00 0c 00 08 00 00 00 00 00 0f 00 08 \
+00 00 00 00 00 10 00 08 00 00 00 00 00 11 00 08 \
+00 00 00 00 00 12 00 08 00 00 00 00 00 13 00 08 \
+00 00 00 00 00 14 00 08 00 00 00 00 00 15 00 08 \
+00 00 00 00 00 16 00 08 00 00 00 00 00 17 00 08 \
+00 00 00 00 00 18 00 08 00 00 00 00 00 19 00 08 \
+00 00 00 00 00 1a 00 08 00 00 00 00 00 1b 00 08 \
+00 00 00 00 00 00 00 00 00 07 00 84 00 00 00 08 \
+00 00 00 00 00 0b 00 08 00 00 00 00 00 0c 00 08 \
+00 00 00 00 00 0f 00 08 00 00 00 00 00 10 00 08 \
+00 00 00 00 00 11 00 08 00 00 00 00 00 12 00 08 \
+00 00 00 00 00 13 00 08 00 00 00 00 00 14 00 08 \
+00 00 00 00 00 15 00 08 00 00 00 00 00 16 00 08 \
+00 00 00 00 00 17 00 08 00 00 00 00 00 18 00 08 \
+00 00 00 00 00 19 00 08 00 00 00 00 00 1a 00 08 \
+00 00 00 00 00 1b 00 08 00 00 00 00 00 00 00 00 \
+00 08 00 dc 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 80 00 0a 02 00 00 08 02 \
+80 00 0c 02 80 00 0e 01 80 00 44 04 80 00 46 01 \
+80 00 48 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 80 00 38 04 80 00 14 01 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 00 01 34 01 \
+80 00 2a 02 80 00 2c 04 80 00 2e 04 80 00 30 06 \
+80 00 32 06 80 00 1a 02 80 00 1c 02 00 01 44 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+80 00 26 01 80 00 28 01 80 00 3a 01 80 00 3c 01 \
+80 00 3e 10 80 00 40 06 80 00 42 06 00 00 00 00 \
+00 0a 00 dc 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 80 00 0a 02 00 00 08 02 \
+80 00 0c 02 80 00 0e 01 80 00 44 04 80 00 46 01 \
+80 00 48 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 80 00 38 04 80 00 14 01 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 00 01 34 01 \
+80 00 2a 02 80 00 2c 04 80 00 2e 04 80 00 30 06 \
+80 00 32 06 80 00 1a 02 80 00 1c 02 00 01 44 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+80 00 26 01 80 00 28 01 80 00 3a 01 80 00 3c 01 \
+80 00 3e 10 80 00 40 06 80 00 42 06 00 00 00 00 \
+00 0c 00 a8 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 00 00 08 02 80 00 0c 02 \
+80 00 0e 01 80 00 44 04 80 00 46 01 80 00 16 04 \
+80 00 18 04 80 00 34 10 80 00 36 10 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 80 00 2a 02 \
+80 00 2c 04 80 00 2e 04 80 00 30 06 80 00 32 06 \
+80 00 1a 02 80 00 1c 02 80 00 1e 02 80 00 20 02 \
+80 00 22 02 80 00 24 02 00 0d 00 a8 80 00 4c 08 \
+00 01 3e 04 00 01 40 04 80 00 04 08 00 00 00 02 \
+80 00 00 04 00 01 42 04 00 01 00 04 00 01 02 04 \
+00 01 04 04 00 01 06 04 00 01 08 04 00 01 0a 04 \
+00 01 0c 04 00 01 0e 04 80 00 08 06 80 00 06 06 \
+00 00 08 02 80 00 0c 02 80 00 0e 01 80 00 44 04 \
+80 00 46 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 00 00 0a 01 80 00 10 01 80 00 12 01 \
+00 01 3a 01 80 00 2a 02 80 00 2c 04 80 00 2e 04 \
+80 00 30 06 80 00 32 06 80 00 1a 02 80 00 1c 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+00 0e 00 a8 80 00 4c 08 00 01 3e 04 00 01 40 04 \
+80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \
+00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \
+00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \
+80 00 08 06 80 00 06 06 00 00 08 02 80 00 0c 02 \
+80 00 0e 01 80 00 44 04 80 00 46 01 80 00 16 04 \
+80 00 18 04 80 00 34 10 80 00 36 10 00 00 0a 01 \
+80 00 10 01 80 00 12 01 00 01 3a 01 80 00 2a 02 \
+80 00 2c 04 80 00 2e 04 80 00 30 06 80 00 32 06 \
+80 00 1a 02 80 00 1c 02 80 00 1e 02 80 00 20 02 \
+80 00 22 02 80 00 24 02 00 0f 00 a8 80 00 4c 08 \
+00 01 3e 04 00 01 40 04 80 00 04 08 00 00 00 02 \
+80 00 00 04 00 01 42 04 00 01 00 04 00 01 02 04 \
+00 01 04 04 00 01 06 04 00 01 08 04 00 01 0a 04 \
+00 01 0c 04 00 01 0e 04 80 00 08 06 80 00 06 06 \
+00 00 08 02 80 00 0c 02 80 00 0e 01 80 00 44 04 \
+80 00 46 01 80 00 16 04 80 00 18 04 80 00 34 10 \
+80 00 36 10 00 00 0a 01 80 00 10 01 80 00 12 01 \
+00 01 3a 01 80 00 2a 02 80 00 2c 04 80 00 2e 04 \
+80 00 30 06 80 00 32 06 80 00 1a 02 80 00 1c 02 \
+80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \
+"], [0], [OFPST_TABLE_FEATURES reply (OF1.5) (xid=0xd5):
+  table 0 ("table0"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    eviction: not supported
+    vacancy events: not supported
+    features: none
+    max_entries=1000000
+    instructions (table miss and others):
+      next tables: 1-253
+      instructions: apply_actions,clear_actions,write_actions,write_metadata,goto_table
+      Write-Actions and Apply-Actions features:
+        actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+        supported on Set-Field: tun_id tun_src tun_dst metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst
+    matching:
+      tun_id: exact match or wildcard
+      tun_src: exact match or wildcard
+      tun_dst: exact match or wildcard
+      metadata: exact match or wildcard
+      in_port: exact match or wildcard
+      in_port_oxm: exact match or wildcard
+      pkt_mark: exact match or wildcard
+      reg0: exact match or wildcard
+      reg1: exact match or wildcard
+      reg2: exact match or wildcard
+      reg3: exact match or wildcard
+      reg4: exact match or wildcard
+      reg5: exact match or wildcard
+      reg6: exact match or wildcard
+      reg7: exact match or wildcard
+      eth_src: exact match or wildcard
+      eth_dst: exact match or wildcard
+      eth_type: exact match or wildcard
+      vlan_tci: exact match or wildcard
+      vlan_vid: exact match or wildcard
+      vlan_pcp: exact match or wildcard
+      mpls_label: exact match or wildcard
+      mpls_tc: exact match or wildcard
+      mpls_bos: exact match or wildcard
+      ip_src: exact match or wildcard
+      ip_dst: exact match or wildcard
+      ipv6_src: exact match or wildcard
+      ipv6_dst: exact match or wildcard
+      ipv6_label: exact match or wildcard
+      nw_proto: exact match or wildcard
+      nw_tos: exact match or wildcard
+      ip_dscp: exact match or wildcard
+      nw_ecn: exact match or wildcard
+      nw_ttl: exact match or wildcard
+      ip_frag: exact match or wildcard
+      arp_op: exact match or wildcard
+      arp_spa: exact match or wildcard
+      arp_tpa: exact match or wildcard
+      arp_sha: exact match or wildcard
+      arp_tha: exact match or wildcard
+      tcp_src: exact match or wildcard
+      tcp_dst: exact match or wildcard
+      tcp_flags: exact match or wildcard
+      udp_src: exact match or wildcard
+      udp_dst: exact match or wildcard
+      sctp_src: exact match or wildcard
+      sctp_dst: exact match or wildcard
+      icmp_type: exact match or wildcard
+      icmp_code: exact match or wildcard
+      icmpv6_type: exact match or wildcard
+      icmpv6_code: exact match or wildcard
+      nd_target: exact match or wildcard
+      nd_sll: exact match or wildcard
+      nd_tll: exact match or wildcard
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_BARRIER_REQUEST - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '01 12 00 08 00 00 00 01'], [0], [dnl
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 52e1ab4..29e321a 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1919,6 +1919,239 @@  AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - table features (OpenFlow 1.5)])
+OVS_VSWITCHD_START
+head_table () {
+    printf '  table 0 ("%s"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    eviction: not supported
+    vacancy events: not supported
+    features: none
+    max_entries=1000000
+    instructions (table miss and others):
+      next tables: 1-253
+      instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
+      Write-Actions and Apply-Actions features:
+        actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata5
 8 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll
+    matching:
+      dp_hash: arbitrary mask
+      recirc_id: exact match or wildcard
+      conj_id: exact match or wildcard
+      tun_id: arbitrary mask
+      tun_src: arbitrary mask
+      tun_dst: arbitrary mask
+      tun_ipv6_src: arbitrary mask
+      tun_ipv6_dst: arbitrary mask
+      tun_flags: arbitrary mask
+      tun_gbp_id: arbitrary mask
+      tun_gbp_flags: arbitrary mask
+      tun_metadata0: arbitrary mask
+      tun_metadata1: arbitrary mask
+      tun_metadata2: arbitrary mask
+      tun_metadata3: arbitrary mask
+      tun_metadata4: arbitrary mask
+      tun_metadata5: arbitrary mask
+      tun_metadata6: arbitrary mask
+      tun_metadata7: arbitrary mask
+      tun_metadata8: arbitrary mask
+      tun_metadata9: arbitrary mask
+      tun_metadata10: arbitrary mask
+      tun_metadata11: arbitrary mask
+      tun_metadata12: arbitrary mask
+      tun_metadata13: arbitrary mask
+      tun_metadata14: arbitrary mask
+      tun_metadata15: arbitrary mask
+      tun_metadata16: arbitrary mask
+      tun_metadata17: arbitrary mask
+      tun_metadata18: arbitrary mask
+      tun_metadata19: arbitrary mask
+      tun_metadata20: arbitrary mask
+      tun_metadata21: arbitrary mask
+      tun_metadata22: arbitrary mask
+      tun_metadata23: arbitrary mask
+      tun_metadata24: arbitrary mask
+      tun_metadata25: arbitrary mask
+      tun_metadata26: arbitrary mask
+      tun_metadata27: arbitrary mask
+      tun_metadata28: arbitrary mask
+      tun_metadata29: arbitrary mask
+      tun_metadata30: arbitrary mask
+      tun_metadata31: arbitrary mask
+      tun_metadata32: arbitrary mask
+      tun_metadata33: arbitrary mask
+      tun_metadata34: arbitrary mask
+      tun_metadata35: arbitrary mask
+      tun_metadata36: arbitrary mask
+      tun_metadata37: arbitrary mask
+      tun_metadata38: arbitrary mask
+      tun_metadata39: arbitrary mask
+      tun_metadata40: arbitrary mask
+      tun_metadata41: arbitrary mask
+      tun_metadata42: arbitrary mask
+      tun_metadata43: arbitrary mask
+      tun_metadata44: arbitrary mask
+      tun_metadata45: arbitrary mask
+      tun_metadata46: arbitrary mask
+      tun_metadata47: arbitrary mask
+      tun_metadata48: arbitrary mask
+      tun_metadata49: arbitrary mask
+      tun_metadata50: arbitrary mask
+      tun_metadata51: arbitrary mask
+      tun_metadata52: arbitrary mask
+      tun_metadata53: arbitrary mask
+      tun_metadata54: arbitrary mask
+      tun_metadata55: arbitrary mask
+      tun_metadata56: arbitrary mask
+      tun_metadata57: arbitrary mask
+      tun_metadata58: arbitrary mask
+      tun_metadata59: arbitrary mask
+      tun_metadata60: arbitrary mask
+      tun_metadata61: arbitrary mask
+      tun_metadata62: arbitrary mask
+      tun_metadata63: arbitrary mask
+      metadata: arbitrary mask
+      in_port: exact match or wildcard
+      in_port_oxm: exact match or wildcard
+      actset_output: exact match or wildcard
+      pkt_mark: arbitrary mask
+      ct_state: arbitrary mask
+      ct_zone: exact match or wildcard
+      ct_mark: arbitrary mask
+      ct_label: arbitrary mask
+      reg0: arbitrary mask
+      reg1: arbitrary mask
+      reg2: arbitrary mask
+      reg3: arbitrary mask
+      reg4: arbitrary mask
+      reg5: arbitrary mask
+      reg6: arbitrary mask
+      reg7: arbitrary mask
+      xreg0: arbitrary mask
+      xreg1: arbitrary mask
+      xreg2: arbitrary mask
+      xreg3: arbitrary mask
+      eth_src: arbitrary mask
+      eth_dst: arbitrary mask
+      eth_type: exact match or wildcard
+      vlan_tci: arbitrary mask
+      vlan_vid: arbitrary mask
+      vlan_pcp: exact match or wildcard
+      mpls_label: exact match or wildcard
+      mpls_tc: exact match or wildcard
+      mpls_bos: exact match or wildcard
+      ip_src: arbitrary mask
+      ip_dst: arbitrary mask
+      ipv6_src: arbitrary mask
+      ipv6_dst: arbitrary mask
+      ipv6_label: arbitrary mask
+      nw_proto: exact match or wildcard
+      nw_tos: exact match or wildcard
+      ip_dscp: exact match or wildcard
+      nw_ecn: exact match or wildcard
+      nw_ttl: exact match or wildcard
+      ip_frag: arbitrary mask
+      arp_op: exact match or wildcard
+      arp_spa: arbitrary mask
+      arp_tpa: arbitrary mask
+      arp_sha: arbitrary mask
+      arp_tha: arbitrary mask
+      tcp_src: arbitrary mask
+      tcp_dst: arbitrary mask
+      tcp_flags: arbitrary mask
+      udp_src: arbitrary mask
+      udp_dst: arbitrary mask
+      sctp_src: arbitrary mask
+      sctp_dst: arbitrary mask
+      icmp_type: exact match or wildcard
+      icmp_code: exact match or wildcard
+      icmpv6_type: exact match or wildcard
+      icmpv6_code: exact match or wildcard
+      nd_target: arbitrary mask
+      nd_sll: arbitrary mask
+      nd_tll: arbitrary mask
+
+' $1
+}
+ditto() {
+    printf '  table %d ("%s"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    eviction: not supported
+    vacancy events: not supported
+    features: none
+    max_entries=%d
+    instructions (table miss and others):
+      next tables: %d-253
+      (same instructions)
+      (same actions)
+    (same matching)
+
+' $1 $2 $3 `expr $1 + 1`
+}
+tail_tables() {
+echo '  table 252 ("table252"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    eviction: not supported
+    vacancy events: not supported
+    features: none
+    max_entries=1000000
+    instructions (table miss and others):
+      next tables: 253
+      (same instructions)
+      (same actions)
+    (same matching)
+
+  table 253 ("table253"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    eviction: not supported
+    vacancy events: not supported
+    features: none
+    max_entries=1000000
+    instructions (table miss and others):
+      instructions: meter,apply_actions,clear_actions,write_actions,write_metadata
+      (same actions)
+    (same matching)
+'
+}
+first_egress_table() {
+echo '  table 251 ("table251"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    eviction: not supported
+    vacancy events: not supported
+    features: first egress table
+    max_entries=1000000
+    instructions (table miss and others):
+      next tables: 252-253
+      (same instructions)
+      Write-Actions features:
+        actions: set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata5
 8 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll
+      Apply-Actions features:
+        actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
+        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata5
 8 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll
+    (same matching)
+'
+}
+(head_table classifier
+ for i in `seq 1 251`; do
+     ditto $i table$i 1000000
+ done
+ tail_tables) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0], [0], [expout])
+# Set first egress table.
+ovs-ofctl -O Openflow15 set-first-egress-table br0 251
+
+# Check that the configuration was updated.
+(head_table classifier
+ for i in `seq 1 250`; do
+     ditto $i table$i 1000000
+ done
+ first_egress_table
+ tail_tables) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0], [0], [expout])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - table description (OpenFlow 1.4)])
 OVS_VSWITCHD_START
 (x=0
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 0d57f85..e2ab612 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -343,6 +343,7 @@  usage(void)
            "  dump-desc SWITCH            print switch description\n"
            "  dump-tables SWITCH          print table stats\n"
            "  dump-table-features SWITCH  print table features\n"
+           "  set-first-egress-table SWITCH TABLE  set first egress table\n"
            "  dump-table-desc SWITCH      print table description (OF1.4+)\n"
            "  mod-port SWITCH IFACE ACT   modify port behavior\n"
            "  mod-table SWITCH MOD        modify flow table behavior\n"
@@ -733,9 +734,9 @@  ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
 {
     struct ofpbuf *request;
     struct vconn *vconn;
-
+    struct ofputil_table_features *tf = NULL;
     open_vconn(ctx->argv[1], &vconn);
-    request = ofputil_encode_table_features_request(vconn_get_version(vconn));
+    request = ofputil_encode_table_features_request(tf, vconn_get_version(vconn));
 
     /* The following is similar to dump_trivial_stats_transaction(), but it
      * maintains the previous 'ofputil_table_features' from one stats reply
@@ -808,6 +809,35 @@  ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
 }
 
 static void
+ofctl_set_first_egress_table(struct ovs_cmdl_context *ctx)
+{
+    uint32_t usable_versions;
+    struct ofputil_table_features tf;
+    struct vconn *vconn;
+    char *error;
+
+    error = parse_ofp_table_features(&tf, ctx->argv[2], &usable_versions);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
+    uint32_t allowed_versions = get_allowed_ofp_versions();
+    if (!(allowed_versions & usable_versions)) {
+        struct ds versions = DS_EMPTY_INITIALIZER;
+        ofputil_format_version_bitmap_names(&versions, allowed_versions);
+        ovs_fatal(0, "set_first_egress_table '%s' requires one of the OpenFlow "
+                  "versions %s but none is enabled (use -O)",
+                  ctx->argv[2], ds_cstr(&versions));
+    }
+    mask_allowed_ofp_versions(usable_versions);
+
+    open_vconn(ctx->argv[1], &vconn);
+    transact_noreply(vconn, ofputil_encode_table_features_request(&tf,
+                                            vconn_get_version(vconn)));
+    vconn_close(vconn);
+}
+
+static void
 ofctl_dump_table_desc(struct ovs_cmdl_context *ctx)
 {
     struct ofpbuf *request;
@@ -3847,6 +3877,8 @@  static const struct ovs_cmdl_command all_commands[] = {
       1, 1, ofctl_dump_tables },
     { "dump-table-features", "switch",
       1, 1, ofctl_dump_table_features },
+    { "set-first-egress-table", "switch table",
+      2, 2, ofctl_set_first_egress_table },
     { "dump-table-desc", "switch",
       1, 1, ofctl_dump_table_desc },
     { "dump-flows", "switch",