diff mbox

[ovs-dev,4/4] Add support for OpenFlow 1.6 (draft) port status and port mod messages.

Message ID 20170406061146.6833-4-blp@ovn.org
State Accepted
Headers show

Commit Message

Ben Pfaff April 6, 2017, 6:11 a.m. UTC
OpenFlow 1.6 adds support for EUI-64 addresses for ports, and extends
the maximum length of OpenFlow port names from 16 to 64 bytes.

ONF-JIRA: EXT-566
Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 NEWS                               |   4 +-
 include/openflow/automake.mk       |   1 +
 include/openflow/openflow-1.0.h    |   7 +-
 include/openflow/openflow-1.1.h    |   3 +-
 include/openflow/openflow-1.4.h    |   4 +-
 include/openflow/openflow-1.6.h    |  98 +++++++++++
 include/openflow/openflow-common.h |   1 -
 include/openflow/openflow.h        |   3 +-
 include/openvswitch/ofp-msgs.h     |  16 +-
 include/openvswitch/ofp-util.h     |  15 +-
 lib/ofp-print.c                    |   9 +
 lib/ofp-util.c                     | 332 +++++++++++++++++++++++++++----------
 ofproto/ofproto-dpif-xlate.c       |   2 +-
 ofproto/ofproto-dpif.c             |   6 +-
 ofproto/ofproto.c                  |   6 +-
 tests/ofp-print.at                 |  41 +++++
 tests/ofproto.at                   |  66 ++++++++
 utilities/ovs-ofctl.c              |   1 +
 18 files changed, 505 insertions(+), 110 deletions(-)
 create mode 100644 include/openflow/openflow-1.6.h

Comments

Andy Zhou April 7, 2017, 8:34 p.m. UTC | #1
On Wed, Apr 5, 2017 at 11:11 PM, Ben Pfaff <blp@ovn.org> wrote:
> OpenFlow 1.6 adds support for EUI-64 addresses for ports, and extends
> the maximum length of OpenFlow port names from 16 to 64 bytes.
>
> ONF-JIRA: EXT-566
> Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Andy Zhou <azhou@ovn.org>
Ben Pfaff April 7, 2017, 10:53 p.m. UTC | #2
On Fri, Apr 07, 2017 at 01:34:40PM -0700, Andy Zhou wrote:
> On Wed, Apr 5, 2017 at 11:11 PM, Ben Pfaff <blp@ovn.org> wrote:
> > OpenFlow 1.6 adds support for EUI-64 addresses for ports, and extends
> > the maximum length of OpenFlow port names from 16 to 64 bytes.
> >
> > ONF-JIRA: EXT-566
> > Signed-off-by: Ben Pfaff <blp@ovn.org>
> Acked-by: Andy Zhou <azhou@ovn.org>

Thanks for the reviews!  I applied the remaining patches to master.
diff mbox

Patch

diff --git a/NEWS b/NEWS
index ec8572a7fc59..6ec1f2f0b7ef 100644
--- a/NEWS
+++ b/NEWS
@@ -10,13 +10,15 @@  Post-v2.7.0
        Log level can be changed in a usual OVS way using
        'ovs-appctl vlog' commands for 'dpdk' module. Lower bound
        still can be configured via extra arguments for DPDK EAL.
-   - The "learn" action now supports a "limit" option (see ovs-ofctl(8)).
    - New support for multiple VLANs (802.1ad or "QinQ"), including a new
      "dot1q-tunnel" port VLAN mode.
    - OVN:
      * Make the DHCPv4 router setting optional.
      * Gratuitous ARP for NAT addresses on a distributed logical router.
    - Add the command 'ovs-appctl stp/show' (see ovs-vswitchd(8)).
+   - OpenFlow:
+     * The "learn" action now supports a "limit" option (see ovs-ofctl(8)).
+     * Increased support for OpenFlow 1.6 (draft).
 
 v2.7.0 - 21 Feb 2017
 ---------------------
diff --git a/include/openflow/automake.mk b/include/openflow/automake.mk
index 18cc649899f8..9755e7c0396e 100644
--- a/include/openflow/automake.mk
+++ b/include/openflow/automake.mk
@@ -9,6 +9,7 @@  openflowinclude_HEADERS = \
 	include/openflow/openflow-1.3.h \
 	include/openflow/openflow-1.4.h \
 	include/openflow/openflow-1.5.h \
+	include/openflow/openflow-1.6.h \
 	include/openflow/openflow-common.h \
 	include/openflow/openflow.h
 
diff --git a/include/openflow/openflow-1.0.h b/include/openflow/openflow-1.0.h
index 68c79526efcb..ad06610da3a4 100644
--- a/include/openflow/openflow-1.0.h
+++ b/include/openflow/openflow-1.0.h
@@ -21,6 +21,11 @@ 
 
 #include <openflow/openflow-common.h>
 
+/* Maximum name of a port.
+ *
+ * OpenFlow 1.6 (draft) increases this to 64. */
+#define OFP10_MAX_PORT_NAME_LEN  16
+
 /* Port number(s)   meaning
  * ---------------  --------------------------------------
  * 0x0000           not assigned a meaning by OpenFlow 1.0
@@ -97,7 +102,7 @@  enum ofp10_port_features {
 struct ofp10_phy_port {
     ovs_be16 port_no;
     struct eth_addr hw_addr;
-    char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */
+    char name[OFP10_MAX_PORT_NAME_LEN]; /* Null-terminated */
 
     ovs_be32 config;        /* Bitmap of OFPPC_* and OFPPC10_* flags. */
     ovs_be32 state;         /* Bitmap of OFPPS_* and OFPPS10_* flags. */
diff --git a/include/openflow/openflow-1.1.h b/include/openflow/openflow-1.1.h
index de28475435fa..a29db8f3ef87 100644
--- a/include/openflow/openflow-1.1.h
+++ b/include/openflow/openflow-1.1.h
@@ -53,6 +53,7 @@ 
 #define OPENFLOW_11_H 1
 
 #include <openflow/openflow-common.h>
+#include <openflow/openflow-1.0.h>
 
 /* OpenFlow 1.1 uses 32-bit port numbers.  Open vSwitch, for now, uses OpenFlow
  * 1.0 port numbers internally.  We map them to OpenFlow 1.0 as follows:
@@ -112,7 +113,7 @@  struct ofp11_port {
     uint8_t pad[4];
     struct eth_addr hw_addr;
     uint8_t pad2[2];                  /* Align to 64 bits. */
-    char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */
+    char name[OFP10_MAX_PORT_NAME_LEN]; /* Null-terminated */
 
     ovs_be32 config;        /* Bitmap of OFPPC_* flags. */
     ovs_be32 state;         /* Bitmap of OFPPS_* and OFPPS11_* flags. */
diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h
index fcebe4eb3eb6..9399950b2837 100644
--- a/include/openflow/openflow-1.4.h
+++ b/include/openflow/openflow-1.4.h
@@ -1,4 +1,4 @@ 
-/* Copyright (c) 2008, 2014 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2014, 2017 The Board of Trustees of The Leland Stanford
 * Junior University
 * Copyright (c) 2011, 2012 Open Networking Foundation
 *
@@ -80,7 +80,7 @@  struct ofp14_port {
     uint8_t pad[2];
     struct eth_addr hw_addr;
     uint8_t pad2[2];                  /* Align to 64 bits. */
-    char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */
+    char name[OFP10_MAX_PORT_NAME_LEN]; /* Null-terminated */
 
     ovs_be32 config;        /* Bitmap of OFPPC_* flags. */
     ovs_be32 state;         /* Bitmap of OFPPS_* flags. */
diff --git a/include/openflow/openflow-1.6.h b/include/openflow/openflow-1.6.h
new file mode 100644
index 000000000000..1ba3cbd6fb3d
--- /dev/null
+++ b/include/openflow/openflow-1.6.h
@@ -0,0 +1,98 @@ 
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * Copyright (c) 2011, 2013, 2014 Open Networking Foundation
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+/*
+ * Copyright (c) 2017 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OPENFLOW_16_H
+#define OPENFLOW_16_H 1
+
+#include <openflow/openflow-common.h>
+
+#define OFP16_MAX_PORT_NAME_LEN  64
+
+/* Bitmap of hardware address types supported by an OpenFlow port. */
+enum ofp16_hardware_address_type {
+    OFPPHAT16_EUI48 = 1 << 0,   /* 48-bit Ethernet address. */
+    OFPPHAT16_EUI64 = 1 << 1,   /* 64-bit Ethernet address. */
+};
+
+struct ofp16_port {
+    ovs_be32 port_no;
+    ovs_be16 length;
+    ovs_be16 hw_addr_type;            /* Zero or more OFPPHAT16_*. */
+    struct eth_addr hw_addr;          /* EUI-48 hardware address. */
+    uint8_t pad[2];                   /* Align to 64 bits. */
+    struct eth_addr64 hw_addr64;      /* EUI-64 hardware address */
+    char name[OFP16_MAX_PORT_NAME_LEN]; /* Null-terminated */
+
+    ovs_be32 config;        /* Bitmap of OFPPC_* flags. */
+    ovs_be32 state;         /* Bitmap of OFPPS_* flags. */
+
+    /* Followed by 0 or more OFPPDPT14_* properties.  (OpenFlow 1.6 (draft)
+     * defines the same properties as OpenFlow 1.4.) */
+};
+OFP_ASSERT(sizeof(struct ofp16_port) == 96);
+
+struct ofp16_port_mod {
+    ovs_be32 port_no;
+    ovs_be16 hw_addr_type;       /* Zero or more OFPPHAT16_*. */
+    uint8_t pad[2];              /* Align to 64 bits. */
+    struct eth_addr hw_addr;
+    uint8_t pad2[2];
+    struct eth_addr64 hw_addr64; /* EUI-64 hardware address */
+
+    ovs_be32 config;        /* Bitmap of OFPPC_* flags. */
+    ovs_be32 mask;          /* Bitmap of OFPPC_* flags to be changed. */
+
+    /* Followed by 0 or more OFPPMPT14_* properties.  (OpenFlow 1.6 (draft)
+     * defines the same properties as OpenFlow 1.4.) */
+};
+OFP_ASSERT(sizeof(struct ofp16_port_mod) == 32);
+
+
+#endif /* openflow/openflow-1.6.h */
diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h
index 7b619a997f2f..530c105286ac 100644
--- a/include/openflow/openflow-common.h
+++ b/include/openflow/openflow-common.h
@@ -111,7 +111,6 @@  enum ofp_version {
 #define INTEL_VENDOR_ID 0x0000AA01 /* Intel */
 
 #define OFP_MAX_TABLE_NAME_LEN 32
-#define OFP_MAX_PORT_NAME_LEN  16
 
 #define OFP_OLD_PORT  6633
 #define OFP_PORT  6653
diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h
index 7976fbb5a1b1..fececb5637a3 100644
--- a/include/openflow/openflow.h
+++ b/include/openflow/openflow.h
@@ -1,5 +1,5 @@ 
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,5 +23,6 @@ 
 #include <openflow/openflow-1.3.h>
 #include <openflow/openflow-1.4.h>
 #include <openflow/openflow-1.5.h>
+#include <openflow/openflow-1.6.h>
 
 #endif /* openflow/openflow.h */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index 07fd8aa76ac2..34708f3bd846 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -1,5 +1,5 @@ 
 /*
- * Copyright (c) 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -170,8 +170,10 @@  enum ofpraw {
     OFPRAW_OFPT10_PORT_STATUS,
     /* OFPT 1.1-1.3 (12): struct ofp_port_status, struct ofp11_port. */
     OFPRAW_OFPT11_PORT_STATUS,
-    /* OFPT 1.4+ (12): struct ofp_port_status, struct ofp14_port, uint8_t[8][]. */
+    /* OFPT 1.4-1.5 (12): struct ofp_port_status, struct ofp14_port, uint8_t[8][]. */
     OFPRAW_OFPT14_PORT_STATUS,
+    /* OFPT 1.6+ (12): struct ofp_port_status, struct ofp16_port, uint8_t[8][]. */
+    OFPRAW_OFPT16_PORT_STATUS,
 
     /* OFPT 1.0 (13): struct ofp10_packet_out, uint8_t[]. */
     OFPRAW_OFPT10_PACKET_OUT,
@@ -194,8 +196,10 @@  enum ofpraw {
     OFPRAW_OFPT10_PORT_MOD,
     /* OFPT 1.1-1.3 (16): struct ofp11_port_mod. */
     OFPRAW_OFPT11_PORT_MOD,
-    /* OFPT 1.4+ (16): struct ofp14_port_mod, uint8_t[8][]. */
+    /* OFPT 1.4-1.5 (16): struct ofp14_port_mod, uint8_t[8][]. */
     OFPRAW_OFPT14_PORT_MOD,
+    /* OFPT 1.6+ (16): struct ofp16_port_mod, uint8_t[8][]. */
+    OFPRAW_OFPT16_PORT_MOD,
 
     /* OFPT 1.1-1.3 (17): struct ofp11_table_mod. */
     OFPRAW_OFPT11_TABLE_MOD,
@@ -552,7 +556,8 @@  enum ofptype {
                                   * OFPRAW_NXT_FLOW_REMOVED. */
     OFPTYPE_PORT_STATUS,         /* OFPRAW_OFPT10_PORT_STATUS.
                                   * OFPRAW_OFPT11_PORT_STATUS.
-                                  * OFPRAW_OFPT14_PORT_STATUS. */
+                                  * OFPRAW_OFPT14_PORT_STATUS.
+                                  * OFPRAW_OFPT16_PORT_STATUS. */
 
     /* Controller command messages. */
     OFPTYPE_PACKET_OUT,          /* OFPRAW_OFPT10_PACKET_OUT.
@@ -564,7 +569,8 @@  enum ofptype {
                                   * OFPRAW_OFPT15_GROUP_MOD. */
     OFPTYPE_PORT_MOD,            /* OFPRAW_OFPT10_PORT_MOD.
                                   * OFPRAW_OFPT11_PORT_MOD.
-                                  * OFPRAW_OFPT14_PORT_MOD. */
+                                  * OFPRAW_OFPT14_PORT_MOD.
+                                  * OFPRAW_OFPT16_PORT_MOD. */
     OFPTYPE_TABLE_MOD,           /* OFPRAW_OFPT11_TABLE_MOD.
                                   * OFPRAW_OFPT14_TABLE_MOD. */
 
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index 7cb9e7fd32bd..f664055c3939 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -45,7 +45,7 @@  ovs_be32 ofputil_port_to_ofp11(ofp_port_t ofp10_port);
 
 bool ofputil_port_from_string(const char *, ofp_port_t *portp);
 void ofputil_format_port(ofp_port_t port, struct ds *);
-void ofputil_port_to_string(ofp_port_t, char namebuf[OFP_MAX_PORT_NAME_LEN],
+void ofputil_port_to_string(ofp_port_t, char namebuf[OFP10_MAX_PORT_NAME_LEN],
                             size_t bufsize);
 
 /* Group numbers. */
@@ -598,11 +598,19 @@  enum ofputil_port_state {
     OFPUTIL_PS_STP_MASK    = 3 << 8  /* Bit mask for OFPPS10_STP_* values. */
 };
 
-/* Abstract ofp10_phy_port or ofp11_port. */
+/* Abstract ofp10_phy_port, ofp11_port, ofp14_port, or ofp16_port. */
 struct ofputil_phy_port {
     ofp_port_t port_no;
+
+    /* Hardware addresses.
+     *
+     * Most hardware has a normal 48-bit Ethernet address, in hw_addr.
+     * Some hardware might have a 64-bit address in hw_addr64.
+     * All-bits-0 indicates that a given address is not present. */
     struct eth_addr hw_addr;
-    char name[OFP_MAX_PORT_NAME_LEN];
+    struct eth_addr64 hw_addr64;
+
+    char name[OFP16_MAX_PORT_NAME_LEN]; /* 64 bytes in OF1.6+, 16 otherwise. */
     enum ofputil_port_config config;
     enum ofputil_port_state state;
 
@@ -682,6 +690,7 @@  struct ofpbuf *ofputil_encode_port_status(const struct ofputil_port_status *,
 struct ofputil_port_mod {
     ofp_port_t port_no;
     struct eth_addr hw_addr;
+    struct eth_addr64 hw_addr64;
     enum ofputil_port_config config;
     enum ofputil_port_config mask;
     enum netdev_features advertise;
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 8e82777fad40..a8cdfcbf20b1 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -436,6 +436,11 @@  ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port)
     ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT"\n",
                   name, ETH_ADDR_ARGS(port->hw_addr));
 
+    if (!eth_addr64_is_zero(port->hw_addr64)) {
+        ds_put_format(string, "     addr64: "ETH_ADDR64_FMT"\n",
+                      ETH_ADDR64_ARGS(port->hw_addr64));
+    }
+
     ds_put_cstr(string, "     config:     ");
     ofp_print_port_config(string, port->config);
 
@@ -1011,6 +1016,10 @@  ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
     ofputil_format_port(pm.port_no, string);
     ds_put_format(string, ": addr:"ETH_ADDR_FMT"\n",
                   ETH_ADDR_ARGS(pm.hw_addr));
+    if (!eth_addr64_is_zero(pm.hw_addr64)) {
+        ds_put_format(string, "     addr64: "ETH_ADDR64_FMT"\n",
+                      ETH_ADDR64_ARGS(pm.hw_addr64));
+    }
 
     ds_put_cstr(string, "     config: ");
     ofp_print_port_config(string, pm.config);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index fac82c40cc18..1f038c61ea97 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -4390,31 +4390,10 @@  parse_ofp14_port_ethernet_property(const struct ofpbuf *payload,
 }
 
 static enum ofperr
-ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+ofputil_pull_ofp14_port_properties(const void *props, size_t len,
+                                   struct ofputil_phy_port *pp)
 {
-    struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
-    if (!op) {
-        return OFPERR_OFPBRC_BAD_LEN;
-    }
-
-    size_t len = ntohs(op->length);
-    if (len < sizeof *op || len - sizeof *op > msg->size) {
-        return OFPERR_OFPBRC_BAD_LEN;
-    }
-    len -= sizeof *op;
-
-    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
-    if (error) {
-        return error;
-    }
-    pp->hw_addr = op->hw_addr;
-    ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
-
-    pp->config = ntohl(op->config) & OFPPC11_ALL;
-    pp->state = ntohl(op->state) & OFPPS11_ALL;
-
-    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
-                                                        len);
+    struct ofpbuf properties = ofpbuf_const_initializer(props, len);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -4443,6 +4422,65 @@  ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
     return 0;
 }
 
+static enum ofperr
+ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+    const struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
+    if (!op) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    size_t len = ntohs(op->length);
+    if (len < sizeof *op || len - sizeof *op > msg->size) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    len -= sizeof *op;
+
+    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+    if (error) {
+        return error;
+    }
+    pp->hw_addr = op->hw_addr;
+    ovs_strlcpy_arrays(pp->name, op->name);
+
+    pp->config = ntohl(op->config) & OFPPC11_ALL;
+    pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+    return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
+}
+
+static enum ofperr
+ofputil_pull_ofp16_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+    const struct ofp16_port *op = ofpbuf_try_pull(msg, sizeof *op);
+    if (!op) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    size_t len = ntohs(op->length);
+    if (len < sizeof *op || len - sizeof *op > msg->size) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    len -= sizeof *op;
+
+    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+    if (error) {
+        return error;
+    }
+    if (op->hw_addr_type & htons(OFPPHAT16_EUI48)) {
+        pp->hw_addr = op->hw_addr;
+    }
+    if (op->hw_addr_type & htons(OFPPHAT16_EUI64)) {
+        pp->hw_addr64 = op->hw_addr64;
+    }
+    ovs_strlcpy_arrays(pp->name, op->name);
+
+    pp->config = ntohl(op->config) & OFPPC11_ALL;
+    pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+    return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
+}
+
 static void
 ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
                               struct ofp10_phy_port *opp)
@@ -4485,8 +4523,20 @@  ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
 }
 
 static void
-ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
-                       struct ofpbuf *b)
+ofputil_encode_ofp14_port_ethernet_prop(
+    const struct ofputil_phy_port *pp,
+    struct ofp14_port_desc_prop_ethernet *eth)
+{
+    eth->curr = netdev_port_features_to_ofp11(pp->curr);
+    eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
+    eth->supported = netdev_port_features_to_ofp11(pp->supported);
+    eth->peer = netdev_port_features_to_ofp11(pp->peer);
+    eth->curr_speed = htonl(pp->curr_speed);
+    eth->max_speed = htonl(pp->max_speed);
+}
+
+static void
+ofputil_put_ofp14_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
 {
     struct ofp14_port *op;
     struct ofp14_port_desc_prop_ethernet *eth;
@@ -4497,17 +4547,39 @@  ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
     op->port_no = ofputil_port_to_ofp11(pp->port_no);
     op->length = htons(sizeof *op + sizeof *eth);
     op->hw_addr = pp->hw_addr;
-    ovs_strlcpy(op->name, pp->name, sizeof op->name);
+    ovs_strlcpy_arrays(op->name, pp->name);
     op->config = htonl(pp->config & OFPPC11_ALL);
     op->state = htonl(pp->state & OFPPS11_ALL);
 
     eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
-    eth->curr = netdev_port_features_to_ofp11(pp->curr);
-    eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
-    eth->supported = netdev_port_features_to_ofp11(pp->supported);
-    eth->peer = netdev_port_features_to_ofp11(pp->peer);
-    eth->curr_speed = htonl(pp->curr_speed);
-    eth->max_speed = htonl(pp->max_speed);
+    ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
+}
+
+static void
+ofputil_put_ofp16_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
+{
+    struct ofp16_port *op;
+    struct ofp14_port_desc_prop_ethernet *eth;
+
+    ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth);
+
+    op = ofpbuf_put_zeros(b, sizeof *op);
+    op->port_no = ofputil_port_to_ofp11(pp->port_no);
+    op->length = htons(sizeof *op + sizeof *eth);
+    if (!eth_addr_is_zero(pp->hw_addr)) {
+        op->hw_addr_type |= htons(OFPPHAT16_EUI48);
+        op->hw_addr = pp->hw_addr;
+    }
+    if (!eth_addr64_is_zero(pp->hw_addr64)) {
+        op->hw_addr_type |= htons(OFPPHAT16_EUI64);
+        op->hw_addr64 = pp->hw_addr64;
+    }
+    ovs_strlcpy_arrays(op->name, pp->name);
+    op->config = htonl(pp->config & OFPPC11_ALL);
+    op->state = htonl(pp->state & OFPPS11_ALL);
+
+    eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
+    ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
 }
 
 static void
@@ -4531,9 +4603,11 @@  ofputil_put_phy_port(enum ofp_version ofp_version,
 
     case OFP14_VERSION:
     case OFP15_VERSION:
-    case OFP16_VERSION:
         ofputil_put_ofp14_port(pp, b);
         break;
+    case OFP16_VERSION:
+        ofputil_put_ofp16_port(pp, b);
+        break;
 
     default:
         OVS_NOT_REACHED();
@@ -4932,10 +5006,13 @@  ofputil_encode_port_status(const struct ofputil_port_status *ps,
 
     case OFP14_VERSION:
     case OFP15_VERSION:
-    case OFP16_VERSION:
         raw = OFPRAW_OFPT14_PORT_STATUS;
         break;
 
+    case OFP16_VERSION:
+        raw = OFPRAW_OFPT16_PORT_STATUS;
+        break;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -4964,74 +5041,126 @@  parse_port_mod_ethernet_property(struct ofpbuf *property,
     return error;
 }
 
-/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
- * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
-enum ofperr
-ofputil_decode_port_mod(const struct ofp_header *oh,
-                        struct ofputil_port_mod *pm, bool loose)
+static enum ofperr
+ofputil_decode_ofp10_port_mod(const struct ofp10_port_mod *opm,
+                              struct ofputil_port_mod *pm)
+{
+    pm->port_no = u16_to_ofp(ntohs(opm->port_no));
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC10_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
+    pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
+    return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp11_port_mod(const struct ofp11_port_mod *opm,
+                              struct ofputil_port_mod *pm)
 {
-    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
-    enum ofpraw raw = ofpraw_pull_assert(&b);
-    if (raw == OFPRAW_OFPT10_PORT_MOD) {
-        const struct ofp10_port_mod *opm = b.data;
+    enum ofperr error;
 
-        pm->port_no = u16_to_ofp(ntohs(opm->port_no));
-        pm->hw_addr = opm->hw_addr;
-        pm->config = ntohl(opm->config) & OFPPC10_ALL;
-        pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
-        pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
-    } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
-        const struct ofp11_port_mod *opm = b.data;
+    error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+    if (error) {
+        return error;
+    }
+
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC11_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+    pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
+
+    return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp14_port_mod_properties(struct ofpbuf *b, bool loose,
+                                         struct ofputil_port_mod *pm)
+{
+    while (b->size > 0) {
+        struct ofpbuf property;
         enum ofperr error;
+        uint64_t type;
 
-        error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+        error = ofpprop_pull(b, &property, &type);
         if (error) {
             return error;
         }
 
-        pm->hw_addr = opm->hw_addr;
-        pm->config = ntohl(opm->config) & OFPPC11_ALL;
-        pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
-        pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
-    } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
-        const struct ofp14_port_mod *opm = ofpbuf_pull(&b, sizeof *opm);
-        enum ofperr error;
+        switch (type) {
+        case OFPPMPT14_ETHERNET:
+            error = parse_port_mod_ethernet_property(&property, pm);
+            break;
 
-        memset(pm, 0, sizeof *pm);
+        default:
+            error = OFPPROP_UNKNOWN(loose, "port_mod", type);
+            break;
+        }
 
-        error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
         if (error) {
             return error;
         }
+    }
+    return 0;
+}
 
-        pm->hw_addr = opm->hw_addr;
-        pm->config = ntohl(opm->config) & OFPPC11_ALL;
-        pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+static enum ofperr
+ofputil_decode_ofp14_port_mod(struct ofpbuf *b, bool loose,
+                              struct ofputil_port_mod *pm)
+{
+    const struct ofp14_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
+    enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+    if (error) {
+        return error;
+    }
 
-        while (b.size > 0) {
-            struct ofpbuf property;
-            enum ofperr error;
-            uint64_t type;
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC11_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
 
-            error = ofpprop_pull(&b, &property, &type);
-            if (error) {
-                return error;
-            }
+    return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
+}
 
-            switch (type) {
-            case OFPPMPT14_ETHERNET:
-                error = parse_port_mod_ethernet_property(&property, pm);
-                break;
+static enum ofperr
+ofputil_decode_ofp16_port_mod(struct ofpbuf *b, bool loose,
+                              struct ofputil_port_mod *pm)
+{
+    const struct ofp16_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
+    enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+    if (error) {
+        return error;
+    }
 
-            default:
-                error = OFPPROP_UNKNOWN(loose, "port_mod", type);
-                break;
-            }
+    if (opm->hw_addr_type & htons(OFPPHAT16_EUI48)) {
+        pm->hw_addr = opm->hw_addr;
+    }
+    if (opm->hw_addr_type & htons(OFPPHAT16_EUI64)) {
+        pm->hw_addr64 = opm->hw_addr64;
+    }
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC11_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
 
-            if (error) {
-                return error;
-            }
-        }
+    return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
+}
+
+/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
+ * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_port_mod(const struct ofp_header *oh,
+                        struct ofputil_port_mod *pm, bool loose)
+{
+    memset(pm, 0, sizeof *pm);
+
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT10_PORT_MOD) {
+        return ofputil_decode_ofp10_port_mod(b.data, pm);
+    } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+        return ofputil_decode_ofp11_port_mod(b.data, pm);
+    } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
+        return ofputil_decode_ofp14_port_mod(&b, loose, pm);
+    } else if (raw == OFPRAW_OFPT16_PORT_MOD) {
+        return ofputil_decode_ofp16_port_mod(&b, loose, pm);
     } else {
         return OFPERR_OFPBRC_BAD_TYPE;
     }
@@ -5079,8 +5208,7 @@  ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         break;
     }
     case OFP14_VERSION:
-    case OFP15_VERSION:
-    case OFP16_VERSION: {
+    case OFP15_VERSION: {
         struct ofp14_port_mod *opm;
 
         b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, 0);
@@ -5096,6 +5224,29 @@  ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         }
         break;
     }
+    case OFP16_VERSION: {
+        struct ofp16_port_mod *opm;
+
+        b = ofpraw_alloc(OFPRAW_OFPT16_PORT_MOD, ofp_version, 0);
+        opm = ofpbuf_put_zeros(b, sizeof *opm);
+        opm->port_no = ofputil_port_to_ofp11(pm->port_no);
+        if (!eth_addr_is_zero(pm->hw_addr)) {
+            opm->hw_addr_type |= htons(OFPPHAT16_EUI48);
+            opm->hw_addr = pm->hw_addr;
+        }
+        if (!eth_addr64_is_zero(pm->hw_addr64)) {
+            opm->hw_addr_type |= htons(OFPPHAT16_EUI64);
+            opm->hw_addr64 = pm->hw_addr64;
+        }
+        opm->config = htonl(pm->config & OFPPC11_ALL);
+        opm->mask = htonl(pm->mask & OFPPC11_ALL);
+
+        if (pm->advertise) {
+            ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
+                             netdev_port_features_to_ofp11(pm->advertise));
+        }
+        break;
+    }
     default:
         OVS_NOT_REACHED();
     }
@@ -7103,7 +7254,7 @@  ofputil_port_from_string(const char *s, ofp_port_t *portp)
                       "be translated to %u when talking to an OF1.1 or "
                       "later controller", port32, port32 + OFPP11_OFFSET);
         } else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
-            char name[OFP_MAX_PORT_NAME_LEN];
+            char name[OFP10_MAX_PORT_NAME_LEN];
 
             ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name);
             VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
@@ -7148,7 +7299,7 @@  ofputil_port_from_string(const char *s, ofp_port_t *portp)
 void
 ofputil_format_port(ofp_port_t port, struct ds *s)
 {
-    char name[OFP_MAX_PORT_NAME_LEN];
+    char name[OFP10_MAX_PORT_NAME_LEN];
 
     ofputil_port_to_string(port, name, sizeof name);
     ds_put_cstr(s, name);
@@ -7160,7 +7311,7 @@  ofputil_format_port(ofp_port_t port, struct ds *s)
  * by name, e.g. "LOCAL". */
 void
 ofputil_port_to_string(ofp_port_t port,
-                       char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize)
+                       char namebuf[OFP10_MAX_PORT_NAME_LEN], size_t bufsize)
 {
     switch (port) {
 #define OFPUTIL_NAMED_PORT(NAME)                        \
@@ -7259,8 +7410,9 @@  ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
     }
     case OFP14_VERSION:
     case OFP15_VERSION:
-    case OFP16_VERSION:
         return b->size ? ofputil_pull_ofp14_port(pp, b) : EOF;
+    case OFP16_VERSION:
+        return b->size ? ofputil_pull_ofp16_port(pp, b) : EOF;
     default:
         OVS_NOT_REACHED();
     }
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index ca286e3720a5..a24aef9a43a1 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -4597,7 +4597,7 @@  xlate_output_trunc_action(struct xlate_ctx *ctx,
 {
     bool support_trunc = ctx->xbridge->support.trunc;
     struct ovs_action_trunc *trunc;
-    char name[OFP_MAX_PORT_NAME_LEN];
+    char name[OFP10_MAX_PORT_NAME_LEN];
 
     switch (port) {
     case OFPP_TABLE:
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 9556a0e0cd58..6a5ffb94fa94 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4845,7 +4845,7 @@  ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ovs_rwlock_rdlock(&ofproto->ml->rwlock);
     LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
         struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e);
-        char name[OFP_MAX_PORT_NAME_LEN];
+        char name[OFP10_MAX_PORT_NAME_LEN];
 
         ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
                                name, sizeof name);
@@ -4886,7 +4886,7 @@  ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
     ovs_rwlock_rdlock(&ofproto->ms->rwlock);
     LIST_FOR_EACH (grp, group_node, &ofproto->ms->group_lru) {
         LIST_FOR_EACH(b, bundle_node, &grp->bundle_lru) {
-            char name[OFP_MAX_PORT_NAME_LEN];
+            char name[OFP10_MAX_PORT_NAME_LEN];
 
             bundle = b->port;
             ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
@@ -4900,7 +4900,7 @@  ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
 
     /* ports connected to multicast routers */
     LIST_FOR_EACH(mrouter, mrouter_node, &ofproto->ms->mrouter_lru) {
-        char name[OFP_MAX_PORT_NAME_LEN];
+        char name[OFP10_MAX_PORT_NAME_LEN];
 
         bundle = mrouter->port;
         ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 84ea95b0c2a2..7440d5b52092 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2346,6 +2346,7 @@  ofport_open(struct ofproto *ofproto,
     }
     pp->port_no = ofproto_port->ofp_port;
     netdev_get_etheraddr(netdev, &pp->hw_addr);
+    pp->hw_addr64 = eth_addr64_zero;
     ovs_strlcpy(pp->name, ofproto_port->name, sizeof pp->name);
     netdev_get_flags(netdev, &flags);
     pp->config = flags & NETDEV_UP ? 0 : OFPUTIL_PC_PORT_DOWN;
@@ -2366,6 +2367,7 @@  ofport_equal(const struct ofputil_phy_port *a,
              const struct ofputil_phy_port *b)
 {
     return (eth_addr_equals(a->hw_addr, b->hw_addr)
+            && eth_addr64_equals(a->hw_addr64, b->hw_addr64)
             && a->state == b->state
             && !((a->config ^ b->config) & OFPUTIL_PC_PORT_DOWN)
             && a->curr == b->curr
@@ -2460,6 +2462,7 @@  static void
 ofport_modified(struct ofport *port, struct ofputil_phy_port *pp)
 {
     port->pp.hw_addr = pp->hw_addr;
+    port->pp.hw_addr64 = pp->hw_addr64;
     port->pp.config = ((port->pp.config & ~OFPUTIL_PC_PORT_DOWN)
                         | (pp->config & OFPUTIL_PC_PORT_DOWN));
     port->pp.state = ((port->pp.state & ~OFPUTIL_PS_LINK_DOWN)
@@ -3630,7 +3633,8 @@  port_mod_start(struct ofconn *ofconn, struct ofputil_port_mod *pm,
     if (!*port) {
         return OFPERR_OFPPMFC_BAD_PORT;
     }
-    if (!eth_addr_equals((*port)->pp.hw_addr, pm->hw_addr)) {
+    if (!eth_addr_equals((*port)->pp.hw_addr, pm->hw_addr) ||
+        !eth_addr64_equals((*port)->pp.hw_addr64, pm->hw_addr64)) {
         return OFPERR_OFPPMFC_BAD_HW_ADDR;
     }
     return 0;
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 5738a9657baa..dfa26e0ce2ae 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -695,6 +695,32 @@  OFPT_PORT_STATUS (OF1.4) (xid=0x0): MOD: 3(eth0): addr:50:54:00:00:00:01
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PORT_STATUS - OF1.6])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+07 0c 00 90 00 00 00 00 02 00 00 00 00 00 00 00 \
+\
+00 00 00 03 00 80 00 03 50 54 00 00 00 01 00 00 \
+50 54 00 ff fe 00 00 01 \
+65 74 68 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 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 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 \
+00 00 20 08 00 00 28 0f 00 00 28 0f 00 00 00 00 \
+00 01 86 a0 00 01 86 a0 \
+"], [0], [dnl
+OFPT_PORT_STATUS (OF1.6) (xid=0x0): MOD: 3(eth0): addr:50:54:00:00:00:01
+     addr64: 50:54:00:ff:fe:00:00:01
+     config:     0
+     state:      0
+     current:    100MB-FD AUTO_NEG
+     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+     speed: 100 Mbps now, 100 Mbps max
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PACKET_OUT - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1124,6 +1150,21 @@  OFPT_PORT_MOD (OF1.4) (xid=0x3): port: 3: addr:50:54:00:00:00:01
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PORT_MOD - OF1.6])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+07 10 00 30 00 00 00 03 00 00 00 03 00 03 00 00 \
+50 54 00 00 00 01 00 00 50 54 00 ff fe 00 00 01 \
+00 00 00 01 00 00 00 01 00 00 00 08 00 00 00 01
+" 3], [0], [dnl
+OFPT_PORT_MOD (OF1.6) (xid=0x3): port: 3: addr:50:54:00:00:00:01
+     addr64: 50:54:00:ff:fe:00:00:01
+     config: PORT_DOWN
+     mask:   PORT_DOWN
+     advertise: 10MB-HD
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_TABLE_MOD - OF1.1])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
diff --git a/tests/ofproto.at b/tests/ofproto.at
index b1ce712672be..5c0d0762390f 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -179,6 +179,40 @@  OFPST_PORT_DESC reply (OF1.5):
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - port-desc stats (OpenFlow 1.6)])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
+AT_CHECK([ovs-ofctl -F OXM-OpenFlow16 -O OpenFlow16 -vwarn dump-ports-desc br0], [0], [stdout])
+AT_CHECK([strip_xids < stdout | sed 's/00:0./00:0x/'], [0], [dnl
+OFPST_PORT_DESC reply (OF1.6):
+ 1(p1): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max
+ 2(p2): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max
+ 3(p3): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max
+ LOCAL(br0): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max
+])
+AT_CHECK([ovs-ofctl -F OXM-OpenFlow16 -O OpenFlow16 -vwarn dump-ports-desc br0 2], [0], [stdout])
+AT_CHECK([strip_xids < stdout | sed 's/00:0./00:0x/'], [0], [dnl
+OFPST_PORT_DESC reply (OF1.6):
+ 2(p2): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl CHECK_QUEUE_STATS(label, option, format)
 m4_define([CHECK_QUEUE_STATS], [
 AT_SETUP([ofproto - queue stats - (OpenFlow $1)])
@@ -1254,6 +1288,38 @@  done
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - mod-port (OpenFlow 1.6)])
+OVS_VSWITCHD_START
+for command_config_state in \
+    'up 0 0' \
+    'down PORT_DOWN LINK_DOWN' \
+    'no-receive PORT_DOWN,NO_RECV LINK_DOWN' \
+    'no-forward PORT_DOWN,NO_RECV,NO_FWD LINK_DOWN' \
+    'no-packet-in PORT_DOWN,NO_RECV,NO_FWD,NO_PACKET_IN LINK_DOWN' \
+    'forward PORT_DOWN,NO_RECV,NO_PACKET_IN LINK_DOWN' \
+    'packet-in PORT_DOWN,NO_RECV LINK_DOWN' \
+    'up NO_RECV 0' \
+    'receive 0 0'
+do
+    set $command_config_state
+    command=$[1] config=`echo $[2] | sed 's/,/ /g'` state=$[3]
+    AT_CHECK([ovs-ofctl -O OpenFlow16 -vwarn mod-port br0 br0 $command])
+    AT_CHECK([ovs-ofctl -O OpenFlow16 -vwarn show br0], [0], [stdout])
+    AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.6): dpid:fedcba9876543210
+n_tables:254, n_buffers:0
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS BUNDLES
+OFPST_PORT_DESC reply (OF1.6):
+ LOCAL(br0): addr:aa:55:aa:55:00:00
+     config:     $config
+     state:      $state
+     speed: 0 Mbps now, 0 Mbps max
+OFPT_GET_CONFIG_REPLY (OF1.6): frags=normal miss_send_len=0
+])
+done
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - basic flow_mod commands (NXM)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 0855fb4e08d2..1a5e2345b7d4 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -2162,6 +2162,7 @@  ofctl_mod_port(struct ovs_cmdl_context *ctx)
 
     pm.port_no = pp.port_no;
     pm.hw_addr = pp.hw_addr;
+    pm.hw_addr64 = pp.hw_addr64;
     pm.config = 0;
     pm.mask = 0;
     pm.advertise = 0;